home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / plug-ins / common / plasma.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-08-24  |  19.9 KB  |  810 lines

  1. /*
  2.  * This is a plugin for the GIMP.
  3.  *
  4.  * Copyright (C) 1996 Stephen Norris
  5.  *
  6.  * This program is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License as published by
  8.  * the Free Software Foundation; either version 2 of the License, or
  9.  * (at your option) any later version.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program; if not, write to the Free Software
  18.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  19.  *
  20.  */
  21.  
  22. /*
  23.  * This plug-in produces plasma fractal images. The algorithm is losely
  24.  * based on a description of the fractint algorithm, but completely
  25.  * re-implemented because the fractint code was too ugly to read :)
  26.  *
  27.  * Please send any patches or suggestions to me: srn@flibble.cs.su.oz.au.
  28.  */
  29.  
  30. /*
  31.  * TODO:
  32.  *    - The progress bar sucks.
  33.  *    - It writes some pixels more than once.
  34.  */
  35.  
  36. /* Version 1.01 */
  37.  
  38. /*
  39.  * Ported to GIMP Plug-in API 1.0
  40.  *    by Eiichi Takamori <taka@ma1.seikyou.ne.jp>
  41.  *
  42.  * $Id: plasma.c,v 1.26 2000/08/22 01:26:55 neo Exp $
  43.  *
  44.  * A few functions names and their order are changed :)
  45.  * Plasma implementation almost hasn't been changed.
  46.  *
  47.  * Feel free to correct my WRONG English, or to modify Plug-in Path,
  48.  * and so on. ;-)
  49.  *
  50.  * Version 1.02 
  51.  *
  52.  * May 2000
  53.  * tim copperfield [timecop@japan.co.jp]
  54.  * Added dynamic preview mode.
  55.  *
  56.  */
  57.  
  58. #include "config.h"
  59.  
  60. #include <stdlib.h>
  61. #include <stdio.h>
  62. #include <time.h>  /* For random seeding */
  63.  
  64. #include <gtk/gtk.h>
  65.  
  66. #include <libgimp/gimp.h>
  67. #include <libgimp/gimpui.h>
  68.  
  69. #include "libgimp/stdplugins-intl.h"
  70.  
  71.  
  72. /* Some useful macros */
  73.  
  74. #define ENTRY_WIDTH      75
  75. #define SCALE_WIDTH     128
  76. #define TILE_CACHE_SIZE  32
  77. #define PREVIEW_SIZE    128
  78.  
  79. typedef struct
  80. {
  81.   gint     seed;
  82.   gdouble  turbulence;
  83.   /* Interface only */
  84.   gboolean timeseed;
  85. } PlasmaValues;
  86.  
  87. typedef struct
  88. {
  89.   gint run;
  90. } PlasmaInterface;
  91.  
  92. /*
  93.  * Function prototypes.
  94.  */
  95.  
  96. static void    query    (void);
  97. static void    run    (gchar   *name,
  98.              gint    nparams,
  99.              GimpParam  *param,
  100.              gint    *nreturn_vals,
  101.              GimpParam  **return_vals);
  102.  
  103. static GtkWidget *preview_widget         (void);
  104. static gint    plasma_dialog            (GimpDrawable *drawable);
  105. static void     plasma_ok_callback       (GtkWidget *widget, 
  106.                       gpointer   data);
  107.  
  108. static void    plasma         (GimpDrawable *drawable, 
  109.                   gboolean   preview_mode);
  110. static void     random_rgb   (guchar    *d);
  111. static void     add_random   (guchar    *d,
  112.                   gint       amnt);
  113. static void     init_plasma  (GimpDrawable *drawable, 
  114.                   gboolean   preview_mode);
  115. static void     provide_tile (GimpDrawable *drawable,
  116.                   gint       col,
  117.                   gint       row);
  118. static void     end_plasma   (GimpDrawable *drawable,
  119.                   gboolean   preview_mode);
  120. static void     get_pixel    (GimpDrawable *drawable,
  121.                   gint       x,
  122.                   gint       y,
  123.                   guchar    *pixel,
  124.                   gboolean   preview_mode);
  125. static void     put_pixel    (GimpDrawable *drawable,
  126.                   gint       x,
  127.                   gint       y,
  128.                   guchar    *pixel,
  129.                   gboolean   preview_mode);
  130. static gint     do_plasma    (GimpDrawable *drawable,
  131.                   gint       x1,
  132.                   gint       y1,
  133.                   gint       x2,
  134.                   gint       y2,
  135.                   gint       depth,
  136.                   gint       scale_depth,
  137.                   gboolean   preview_mode);
  138.  
  139. /***** Local vars *****/
  140.  
  141. GimpPlugInInfo PLUG_IN_INFO =
  142. {
  143.   NULL,  /* init_proc  */
  144.   NULL,  /* quit_proc  */
  145.   query, /* query_proc */
  146.   run,   /* run_proc   */
  147. };
  148.  
  149. static PlasmaValues pvals =
  150. {
  151.   0,     /* seed       */
  152.   1.0,   /* turbulence */
  153.   TRUE   /* Time seed? */
  154. };
  155.  
  156. static PlasmaInterface pint =
  157. {
  158.   FALSE     /* run */
  159. };
  160.  
  161. static guchar *work_buffer;
  162. static GtkWidget *preview;
  163.  
  164. /***** Functions *****/
  165.  
  166. MAIN ()
  167.  
  168. static void
  169. query (void)
  170. {
  171.   static GimpParamDef args[]=
  172.   {
  173.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  174.     { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
  175.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  176.     { GIMP_PDB_INT32, "seed", "Random seed" },
  177.     { GIMP_PDB_FLOAT, "turbulence", "Turbulence of plasma" }
  178.   };
  179.   static gint nargs = sizeof (args) / sizeof (args[0]);
  180.  
  181.   gimp_install_procedure ("plug_in_plasma",
  182.               "Create a plasma cloud like image to the specified drawable",
  183.               "More help",
  184.               "Stephen Norris & (ported to 1.0 by) Eiichi Takamori",
  185.               "Stephen Norris",
  186.               "May 2000",
  187.               N_("<Image>/Filters/Render/Clouds/Plasma..."),
  188.               "RGB*, GRAY*",
  189.               GIMP_PLUGIN,
  190.               nargs, 0,
  191.               args, NULL);
  192. }
  193.  
  194. static void
  195. run (gchar   *name,
  196.      gint    nparams,
  197.      GimpParam  *param,
  198.      gint    *nreturn_vals,
  199.      GimpParam  **return_vals)
  200. {
  201.   static GimpParam values[1];
  202.   GimpDrawable *drawable;
  203.   GimpRunModeType run_mode;
  204.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  205.  
  206.   run_mode = param[0].data.d_int32;
  207.  
  208.   *nreturn_vals = 1;
  209.   *return_vals = values;
  210.  
  211.   values[0].type = GIMP_PDB_STATUS;
  212.   values[0].data.d_status = status;
  213.  
  214.   /*  Get the specified drawable  */
  215.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  216.  
  217.   switch (run_mode)
  218.     {
  219.     case GIMP_RUN_INTERACTIVE:
  220.       INIT_I18N_UI();
  221.       /*  Possibly retrieve data  */
  222.       gimp_get_data ("plug_in_plasma", &pvals);
  223.  
  224.       /*  First acquire information with a dialog  */
  225.       if (! plasma_dialog (drawable))
  226.     {
  227.       gimp_drawable_detach (drawable);
  228.       return;
  229.     }
  230.       break;
  231.  
  232.     case GIMP_RUN_NONINTERACTIVE:
  233.       INIT_I18N();
  234.       /*  Make sure all the arguments are there!  */
  235.       if (nparams != 5)
  236.     {
  237.       status = GIMP_PDB_CALLING_ERROR;
  238.     }
  239.       else
  240.     {
  241.       pvals.seed = (gint) param[3].data.d_int32;
  242.       pvals.turbulence = (gdouble) param[4].data.d_float;
  243.           pvals.timeseed = FALSE;
  244.  
  245.       if (pvals.turbulence <= 0)
  246.         status = GIMP_PDB_CALLING_ERROR;
  247.     }
  248.       break;
  249.  
  250.     case GIMP_RUN_WITH_LAST_VALS:
  251.       INIT_I18N();
  252.       /*  Possibly retrieve data  */
  253.       gimp_get_data ("plug_in_plasma", &pvals);
  254.       break;
  255.  
  256.     default:
  257.       break;
  258.     }
  259.  
  260.   if (status == GIMP_PDB_SUCCESS)
  261.     {
  262.       /*  Make sure that the drawable is gray or RGB color  */
  263.       if (gimp_drawable_is_rgb (drawable->id) || gimp_drawable_is_gray (drawable->id))
  264.     {
  265.       gimp_progress_init (_("Plasma..."));
  266.       gimp_tile_cache_ntiles (TILE_CACHE_SIZE);
  267.  
  268.       plasma (drawable, FALSE);
  269.  
  270.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  271.         gimp_displays_flush ();
  272.  
  273.       /*  Store data  */
  274.       if (run_mode == GIMP_RUN_INTERACTIVE || 
  275.           (pvals.timeseed && run_mode == GIMP_RUN_WITH_LAST_VALS))
  276.         gimp_set_data ("plug_in_plasma", &pvals, sizeof (PlasmaValues));
  277.     }
  278.       else
  279.     {
  280.       /* gimp_message ("plasma: cannot operate on indexed color images"); */
  281.       status = GIMP_PDB_EXECUTION_ERROR;
  282.     }
  283.     }
  284.  
  285.   values[0].data.d_status = status;
  286.   gimp_drawable_detach (drawable);
  287. }
  288.  
  289. static gint
  290. plasma_dialog (GimpDrawable *drawable)
  291. {
  292.   GtkWidget *dlg;
  293.   GtkWidget *main_vbox;
  294.   GtkWidget *abox;
  295.   GtkWidget *frame;
  296.   GtkWidget *table;
  297.   GtkWidget *seed;
  298.   GtkObject *adj;
  299.  
  300.   gimp_ui_init ("plasma", TRUE);
  301.  
  302.   dlg = gimp_dialog_new (_("Plasma"), "plasma",
  303.              gimp_standard_help_func, "filters/plasma.html",
  304.              GTK_WIN_POS_MOUSE,
  305.              FALSE, TRUE, FALSE,
  306.  
  307.              _("OK"), plasma_ok_callback,
  308.              NULL, NULL, NULL, TRUE, FALSE,
  309.              _("Cancel"), gtk_widget_destroy,
  310.              NULL, 1, NULL, FALSE, TRUE,
  311.  
  312.              NULL);
  313.  
  314.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  315.               GTK_SIGNAL_FUNC (gtk_main_quit),
  316.               NULL);
  317.  
  318.   gimp_help_init ();
  319.  
  320.   main_vbox = gtk_vbox_new (FALSE, 2);
  321.   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 6);
  322.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), main_vbox, TRUE, TRUE, 0);
  323.   gtk_widget_show (main_vbox);
  324.  
  325.   /* make a nice preview frame */
  326.   frame = gtk_frame_new (_("Preview"));
  327.   gtk_container_set_border_width (GTK_CONTAINER (frame), 4);
  328.   gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
  329.   gtk_widget_show (frame);
  330.   abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
  331.   gtk_container_set_border_width (GTK_CONTAINER (abox), 4);
  332.   gtk_container_add (GTK_CONTAINER (frame), abox);
  333.   gtk_widget_show (abox);
  334.   frame = gtk_frame_new (NULL);
  335.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  336.   gtk_container_add (GTK_CONTAINER (abox), frame);
  337.   gtk_widget_show (frame);
  338.   preview = preview_widget (); /* we are here */
  339.   gtk_container_add (GTK_CONTAINER (frame), preview);
  340.   plasma (drawable, TRUE); /* preview image */
  341.   gtk_widget_show (preview);
  342.   
  343.   /*  parameter settings  */
  344.   frame = gtk_frame_new (_("Parameter Settings"));
  345.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  346.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  347.   gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
  348.  
  349.   table = gtk_table_new (2, 3, FALSE);
  350.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  351.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  352.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  353.   gtk_container_add (GTK_CONTAINER (frame), table);
  354.  
  355.   seed = gimp_random_seed_new (&pvals.seed,
  356.                    &pvals.timeseed,
  357.                    TRUE, FALSE);
  358.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
  359.                  _("Random Seed:"), 1.0, 0.5,
  360.                  seed, 1, TRUE);
  361.   gtk_signal_connect_object (GTK_OBJECT (GIMP_RANDOM_SEED_SPINBUTTON_ADJ (seed)),
  362.                  "value_changed",
  363.                  GTK_SIGNAL_FUNC (plasma),
  364.                  (gpointer)drawable);
  365.   gtk_signal_connect_object (GTK_OBJECT (GIMP_RANDOM_SEED_TOGGLEBUTTON (seed)),
  366.                  "toggled",
  367.                  GTK_SIGNAL_FUNC (plasma),
  368.                  (gpointer)drawable);
  369.  
  370.   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
  371.                   _("Turbulence:"), SCALE_WIDTH, 0,
  372.                   pvals.turbulence,
  373.                   0.1, 7.0, 0.1, 1.0, 1,
  374.                   TRUE, 0, 0,
  375.                   NULL, NULL);
  376.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  377.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  378.               &pvals.turbulence);
  379.   gtk_signal_connect_object (GTK_OBJECT (adj), "value_changed",
  380.                  GTK_SIGNAL_FUNC (plasma),
  381.                  (gpointer)drawable);
  382.  
  383.   gtk_widget_show (frame);
  384.   gtk_widget_show (table);
  385.   gtk_widget_show (dlg);
  386.  
  387.   gtk_main ();
  388.   gimp_help_free ();
  389.   gdk_flush ();
  390.  
  391.   return pint.run;
  392. }
  393.  
  394. static void
  395. plasma_ok_callback (GtkWidget *widget,
  396.             gpointer   data)
  397. {
  398.   pint.run = TRUE;
  399.  
  400.   gtk_widget_destroy (GTK_WIDGET (data));
  401. }
  402.  
  403. #define AVE(n, v1, v2) n[0] = ((gint)v1[0] + (gint)v2[0]) / 2;\
  404.     n[1] = ((gint)v1[1] + (gint)v2[1]) / 2;\
  405.     n[2] = ((gint)v1[2] + (gint)v2[2]) / 2;
  406.  
  407. /*
  408.  * Some glabals to save passing too many paramaters that don't change.
  409.  */
  410.  
  411. static gint    ix1, iy1, ix2, iy2;    /* Selected image size. */
  412. static GimpTile   *tile=NULL;
  413. static gint     tile_row, tile_col;
  414. static gint     tile_width, tile_height;
  415. static gint     tile_dirty;
  416. static gint    bpp, has_alpha, alpha;
  417. static gdouble    turbulence;
  418. static glong    max_progress, progress;
  419.  
  420. /*
  421.  * The setup function.
  422.  */
  423.  
  424. static void
  425. plasma (GimpDrawable *drawable, 
  426.     gboolean    preview_mode)
  427. {
  428.   gint  depth;
  429.  
  430.   init_plasma (drawable, preview_mode);
  431.  
  432.   /*
  433.    * This first time only puts in the seed pixels - one in each
  434.    * corner, and one in the center of each edge, plus one in the
  435.    * center of the image.
  436.    */
  437.  
  438.   do_plasma (drawable, ix1, iy1, ix2 - 1, iy2 - 1, -1, 0, preview_mode);
  439.  
  440.   /*
  441.    * Now we recurse through the images, going further each time.
  442.    */
  443.   depth = 1;
  444.   while (!do_plasma (drawable, ix1, iy1, ix2 - 1, iy2 - 1, depth, 0, preview_mode))
  445.     {
  446.       depth ++;
  447.     }
  448.   end_plasma (drawable, preview_mode);
  449. }
  450.  
  451. static void
  452. init_plasma (GimpDrawable *drawable,
  453.          gboolean   preview_mode)
  454. {
  455.   if (pvals.timeseed)
  456.     pvals.seed = time(NULL);
  457.  
  458.   srand (pvals.seed);
  459.   turbulence = pvals.turbulence;
  460.  
  461.   if (preview_mode) 
  462.     {
  463.       ix1 = iy1 = 0;
  464.       ix2 = GTK_PREVIEW (preview)->buffer_width;
  465.       iy2 = GTK_PREVIEW (preview)->buffer_height;
  466.       bpp = GTK_PREVIEW (preview)->bpp;
  467.       alpha = bpp;
  468.       has_alpha = 0;
  469.       work_buffer = g_malloc (GTK_PREVIEW (preview)->rowstride * iy2);
  470.       memcpy (work_buffer, GTK_PREVIEW (preview)->buffer, GTK_PREVIEW (preview)->rowstride * iy2);
  471.     } 
  472.   else 
  473.     {
  474.       gimp_drawable_mask_bounds (drawable->id, &ix1, &iy1, &ix2, &iy2);
  475.       bpp = drawable->bpp;
  476.       has_alpha = gimp_drawable_has_alpha (drawable->id);
  477.       if (has_alpha)
  478.     alpha = bpp-1;
  479.       else
  480.     alpha = bpp;
  481.     }
  482.  
  483.   max_progress = (ix2 - ix1) * (iy2 - iy1);
  484.   progress = 0;
  485.  
  486.   tile_width  = gimp_tile_width ();
  487.   tile_height = gimp_tile_height ();
  488.  
  489.   tile = NULL;
  490.   tile_row = 0; tile_col = 0;
  491. }
  492.  
  493. static void
  494. provide_tile (GimpDrawable *drawable,
  495.           gint       col,
  496.           gint       row)
  497. {
  498.   if (col != tile_col || row != tile_row || !tile)
  499.     {
  500.       if (tile)
  501.     gimp_tile_unref (tile, tile_dirty);
  502.  
  503.       tile_col = col;
  504.       tile_row = row;
  505.       tile = gimp_drawable_get_tile (drawable, TRUE, tile_row, tile_col);
  506.       tile_dirty = FALSE;
  507.       gimp_tile_ref (tile);
  508.     }
  509. }
  510.  
  511. static void
  512. end_plasma (GimpDrawable *drawable,
  513.         gboolean   preview_mode)
  514. {
  515.   if (preview_mode) 
  516.     {
  517.       memcpy (GTK_PREVIEW (preview)->buffer, work_buffer, GTK_PREVIEW (preview)->rowstride * iy2);
  518.       g_free (work_buffer);
  519.       gtk_widget_queue_draw (preview);
  520.     } 
  521.   else 
  522.     {
  523.       if (tile)
  524.     gimp_tile_unref (tile, tile_dirty);
  525.       tile = NULL;
  526.  
  527.       gimp_drawable_flush (drawable);
  528.       gimp_drawable_merge_shadow (drawable->id, TRUE);
  529.       gimp_drawable_update (drawable->id, ix1, iy1, (ix2 - ix1), (iy2 - iy1));
  530.     }
  531. }
  532.  
  533. static void
  534. get_pixel (GimpDrawable *drawable,
  535.        gint       x,
  536.        gint       y,
  537.        guchar    *pixel,
  538.        gboolean   preview_mode)
  539. {
  540.   gint   row, col;
  541.   gint   offx, offy, i;
  542.   guchar  *ptr;
  543.  
  544.   if (x < ix1)       x = ix1;
  545.   if (x > ix2 - 1)   x = ix2 - 1;
  546.   if (y < iy1)       y = iy1;
  547.   if (y > iy2 - 1)   y = iy2 - 1;
  548.  
  549.   if (preview_mode) 
  550.     {
  551.       memcpy (pixel, work_buffer + (y * GTK_PREVIEW (preview)->rowstride) + (x * bpp), bpp);
  552.     }
  553.   else 
  554.     {
  555.       col = x / tile_width;
  556.       row = y / tile_height;
  557.       offx = x % tile_width;
  558.       offy = y % tile_height;
  559.       
  560.       provide_tile (drawable, col, row);
  561.       ptr = tile->data + (offy * tile->ewidth + offx) * bpp;
  562.  
  563.       for (i = 0; i < alpha; i++)
  564.     pixel[i] = ptr[i];
  565.     }
  566. }
  567.  
  568. static void
  569. put_pixel (GimpDrawable *drawable,
  570.        gint       x,
  571.        gint       y,
  572.        guchar    *pixel,
  573.        gboolean   preview_mode)
  574. {
  575.   gint   row, col;
  576.   gint   offx, offy, i;
  577.   guchar  *ptr;
  578.  
  579.   if (x < ix1)       x = ix1;
  580.   if (x > ix2 - 1)   x = ix2 - 1;
  581.   if (y < iy1)       y = iy1;
  582.   if (y > iy2 - 1)   y = iy2 - 1;
  583.  
  584.   if (preview_mode)
  585.     memcpy (work_buffer + (y * GTK_PREVIEW (preview)->rowstride) + (x * bpp), pixel, bpp);
  586.   else
  587.     {
  588.       col = x / tile_width;
  589.       row = y / tile_height;
  590.       offx = x % tile_width;
  591.       offy = y % tile_height;
  592.  
  593.       provide_tile (drawable, col, row);
  594.       ptr = tile->data + (offy * tile->ewidth + offx) * bpp;
  595.  
  596.       for (i = 0; i < alpha; i++)
  597.     ptr[i] = pixel[i];
  598.  
  599.       if (has_alpha)
  600.     ptr[alpha] = 255;
  601.  
  602.       tile_dirty = TRUE;
  603.       progress++;
  604.     }
  605. }
  606.  
  607. static void
  608. random_rgb (guchar *d)
  609. {
  610.   gint i;
  611.  
  612.   for(i = 0; i < alpha; i++)
  613.     {
  614.       d[i] = rand() % 256;
  615.     }
  616. }
  617.  
  618. static void
  619. add_random (guchar *d,
  620.         gint    amnt)
  621. {
  622.   gint i, tmp;
  623.  
  624.   for (i = 0; i < alpha; i++)
  625.     {
  626.       if (amnt == 0)
  627.     {
  628.       amnt = 1;
  629.     }
  630.       tmp = amnt/2 - rand() % amnt;
  631.  
  632.       if ((gint)d[i] + tmp < 0)
  633.     {
  634.       d[i] = 0;
  635.     }
  636.       else if ((gint)d[i] + tmp > 255)
  637.     {
  638.       d[i] = 255;
  639.     }
  640.       else
  641.     {
  642.       d[i] += tmp;
  643.     }
  644.     }
  645. }
  646.  
  647. static gint
  648. do_plasma (GimpDrawable *drawable,
  649.        gint       x1,
  650.        gint       y1,
  651.        gint       x2,
  652.        gint       y2,
  653.        gint       depth,
  654.        gint       scale_depth,
  655.        gboolean   preview_mode)
  656. {
  657.   guchar  tl[3], ml[3], bl[3], mt[3], mm[3], mb[3], tr[3], mr[3], br[3];
  658.   guchar  tmp[3];
  659.   gint    ran;
  660.   gint    xm, ym;
  661.  
  662.   static gint count = 0;
  663.  
  664.   /* Initial pass through - no averaging. */
  665.  
  666.   if (depth == -1)
  667.     {
  668.       random_rgb (tl);
  669.       put_pixel (drawable, x1, y1, tl, preview_mode);
  670.       random_rgb (tr);
  671.       put_pixel (drawable, x2, y1, tr, preview_mode);
  672.       random_rgb (bl);
  673.       put_pixel (drawable, x1, y2, bl, preview_mode);
  674.       random_rgb (br);
  675.       put_pixel (drawable, x2, y2, br, preview_mode);
  676.       random_rgb (mm);
  677.       put_pixel (drawable, (x1 + x2) / 2, (y1 + y2) / 2, mm, preview_mode);
  678.       random_rgb (ml);
  679.       put_pixel (drawable, x1, (y1 + y2) / 2, ml, preview_mode);
  680.       random_rgb (mr);
  681.       put_pixel (drawable, x2, (y1 + y2) / 2, mr, preview_mode);
  682.       random_rgb (mt);
  683.       put_pixel (drawable, (x1 + x2) / 2, y1, mt, preview_mode);
  684.       random_rgb (ml);
  685.       put_pixel (drawable, (x1 + x2) / 2, y2, ml, preview_mode);
  686.  
  687.       return 0;
  688.     }
  689.  
  690.   /*
  691.    * Some later pass, at the bottom of this pass,
  692.    * with averaging at this depth.
  693.    */
  694.   if (depth == 0)
  695.     {
  696.       gdouble    rnd;
  697.       gint    xave, yave;
  698.  
  699.       get_pixel (drawable, x1, y1, tl, preview_mode);
  700.       get_pixel (drawable, x1, y2, bl, preview_mode);
  701.       get_pixel (drawable, x2, y1, tr, preview_mode);
  702.       get_pixel (drawable, x2, y2, br, preview_mode);
  703.  
  704.       rnd = (256.0 / (2.0 * (gdouble)scale_depth)) * turbulence;
  705.       ran = rnd;
  706.  
  707.       xave = (x1 + x2) / 2;
  708.       yave = (y1 + y2) / 2;
  709.  
  710.       if (xave == x1 && xave == x2 && yave == y1 && yave == y2)
  711.     {
  712.       return 0;
  713.     }
  714.  
  715.       if (xave != x1 || xave != x2)
  716.     {
  717.       /* Left. */
  718.       AVE (ml, tl, bl);
  719.       add_random (ml, ran);
  720.       put_pixel (drawable, x1, yave, ml, preview_mode);
  721.  
  722.       if (x1 != x2)
  723.         {
  724.                 /* Right. */
  725.           AVE (mr, tr, br);
  726.           add_random (mr, ran);
  727.           put_pixel (drawable, x2, yave, mr, preview_mode);
  728.         }
  729.     }
  730.  
  731.       if (yave != y1 || yave != y2)
  732.     {
  733.       if (x1 != xave || yave != y2)
  734.         {
  735.           /* Bottom. */
  736.           AVE (mb, bl, br);
  737.           add_random (mb, ran);
  738.           put_pixel (drawable, xave, y2, mb, preview_mode);
  739.         }
  740.  
  741.       if (y1 != y2)
  742.         {
  743.           /* Top. */
  744.           AVE (mt, tl, tr);
  745.           add_random (mt, ran);
  746.           put_pixel (drawable, xave, y1, mt, preview_mode);
  747.         }
  748.     }
  749.  
  750.       if (y1 != y2 || x1 != x2)
  751.     {
  752.       /* Middle pixel. */
  753.       AVE (mm, tl, br);
  754.       AVE (tmp, bl, tr);
  755.       AVE (mm, mm, tmp);
  756.  
  757.       add_random (mm, ran);
  758.       put_pixel (drawable, xave, yave, mm, preview_mode);
  759.     }
  760.  
  761.       count ++;
  762.  
  763.       if (!(count % 2000) && !preview_mode)
  764.     {
  765.       gimp_progress_update ((double) progress / (double) max_progress);
  766.     }
  767.  
  768.       if ((x2 - x1) < 3 && (y2 - y1) < 3)
  769.     {
  770.       return 1;
  771.     }
  772.       return 0;
  773.     }
  774.  
  775.   xm = (x1 + x2) >> 1;
  776.   ym = (y1 + y2) >> 1;
  777.  
  778.   /* Top left. */
  779.   do_plasma (drawable, x1, y1, xm, ym, depth - 1, scale_depth + 1, preview_mode);
  780.   /* Bottom left. */
  781.   do_plasma (drawable, x1, ym, xm ,y2, depth - 1, scale_depth + 1, preview_mode);
  782.   /* Top right. */
  783.   do_plasma (drawable, xm, y1, x2 , ym, depth - 1, scale_depth + 1, preview_mode);
  784.   /* Bottom right. */
  785.   return do_plasma (drawable, xm, ym, x2, y2, depth - 1, scale_depth + 1, preview_mode);
  786. }
  787.  
  788.  
  789. /* preview library */
  790.  
  791.  
  792. static GtkWidget *
  793. preview_widget (void)
  794. {
  795.   GtkWidget *preview;
  796.   guchar    *buf;
  797.   gint       y;
  798.  
  799.   preview = gtk_preview_new (GTK_PREVIEW_COLOR);
  800.   gtk_preview_size (GTK_PREVIEW (preview), PREVIEW_SIZE, PREVIEW_SIZE);
  801.  
  802.   buf = g_malloc0 (PREVIEW_SIZE * 3);
  803.   for (y = 0; y < PREVIEW_SIZE; y++) 
  804.     gtk_preview_draw_row (GTK_PREVIEW (preview), buf, 0, y, PREVIEW_SIZE);
  805.   g_free (buf);
  806.  
  807.   return preview;
  808. }
  809.  
  810.