Index: despeckle.c
===================================================================
RCS file: /cvs/gnome/gimp/plug-ins/common/despeckle.c,v
retrieving revision 1.60
diff -u -p -u -p -r1.60 despeckle.c
--- a/despeckle.c	26 May 2004 17:29:29 -0000	1.60
+++ b/despeckle.c	19 Jun 2004 09:28:58 -0000
@@ -1,6 +1,4 @@
 /*
- * "$Id: despeckle.c,v 1.60 2004/05/26 17:29:29 maurits Exp $"
- *
  *   Despeckle (adaptive median) filter for The GIMP -- an image manipulation
  *   program
  *
@@ -23,8 +21,6 @@
 
 #include "config.h"
 
-#include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 
 #ifdef __GNUC__
@@ -51,7 +47,7 @@
 #define SCALE_WIDTH	 100
 #define ENTRY_WIDTH	   3
 #define MAX_RADIUS	  20
-
+#define SORT_PIVOT	  20
 #define FILTER_ADAPTIVE	 0x01
 #define FILTER_RECURSIVE 0x02
 
@@ -70,8 +66,9 @@ static void      run   (const gchar     
                         const GimpParam  *param,
                         gint             *nreturn_vals,
                         GimpParam       **return_vals);
+static void	despeckle (GimpDrawable *drawable);
 
-static void      despeckle (void);
+static gint	despeckle_dialog (void);
 
 static gboolean  despeckle_dialog          (void);
 
@@ -79,10 +76,23 @@ static void      dialog_iscale_update   
 static void      dialog_adaptive_callback  (GtkWidget *, gpointer);
 static void      dialog_recursive_callback (GtkWidget *, gpointer);
 
-static void      preview_init              (void);
 static void      preview_exit              (void);
 static void      preview_update            (void);
-static void      preview_scroll_callback   (void);
+static void      preview_srcoll            (void);
+static void      render_despeckle          (guchar   *dst_buffer,
+                                            gint      width,
+                                            gint      height,
+                                            gint      bpp,
+                                            gint      initialradius);
+static void      fill_mask                 (guchar   *mask,
+                                            gint      width);
+static void      quick_sort                (guchar   *buffer,
+                                            gint      size);
+static void      q_sort                    (guchar   *buffer,
+                                            gint      left,
+                                            gint      right);
+static void      bubble_sort               (guchar   *buffer,
+                                            gint      size);
 
 
 /*
@@ -104,9 +114,6 @@ static gint            preview_width,		/
 		       preview_y1,		/* Upper-left Y of preview */
 		       preview_x2,		/* Lower-right X of preview */
 		       preview_y2;		/* Lower-right Y of preview */
-static guchar	      *preview_src = NULL,	/* Source pixel rows */
-	       	      *preview_dst,		/* Destination pixel row */
-	       	      *preview_sort;		/* Pixel value sort array */
 static GtkObject      *hscroll_data,		/* Horizontal scrollbar data */
 	       	      *vscroll_data;		/* Vertical scrollbar data */
 
@@ -117,7 +124,7 @@ static gint	       sel_x1,			/* Selectio
 		       sel_y2;
 static gint	       sel_width,		/* Selection width */
 		       sel_height;		/* Selection height */
-static gint	       img_bpp;			/* Bytes-per-pixel in image */
+
 
 static gint despeckle_vals[4] =
 {
@@ -184,7 +191,7 @@ run (const gchar      *name,
 {
   GimpRunMode	     run_mode;
   GimpPDBStatusType  status;
-  GimpParam	     *values;
+  GimpParam	    *values;
 
   INIT_I18N ();
 
@@ -208,14 +215,11 @@ run (const gchar      *name,
    */
 
   drawable = gimp_drawable_get (param[2].data.d_drawable);
-
   gimp_drawable_mask_bounds (drawable->drawable_id,
-			     &sel_x1, &sel_y1, &sel_x2, &sel_y2);
+                             &sel_x1, &sel_y1, &sel_x2, &sel_y2);
 
   sel_width  = sel_x2 - sel_x1;
   sel_height = sel_y2 - sel_y1;
-  img_bpp    = gimp_drawable_bpp (drawable->drawable_id);
-
   /*
    * See how we will run
    */
@@ -282,7 +286,7 @@ run (const gchar      *name,
       INIT_I18N();
       gimp_get_data (PLUG_IN_NAME, despeckle_vals);
 	break;
-	
+
     default:
       status = GIMP_PDB_CALLING_ERROR;
       break;
@@ -301,14 +305,15 @@ run (const gchar      *name,
 	   * Set the tile cache size...
 	   */
 
-	  gimp_tile_cache_ntiles (2 * (drawable->width + gimp_tile_width() - 1) /
+	  gimp_tile_cache_ntiles (2 *
+                                  (drawable->width + gimp_tile_width() - 1) /
 				  gimp_tile_width() + 1);
 
 	  /*
 	   * Run!
 	   */
 
-	  despeckle ();
+	  despeckle (drawable);
 
 	  /*
 	   * If run mode is interactive, flush displays...
@@ -356,32 +361,12 @@ run (const gchar      *name,
  */
 
 static void
-despeckle (void)
+despeckle (GimpDrawable *drawable)
 {
   GimpPixelRgn	src_rgn,	/* Source image region */
 		dst_rgn;	/* Destination image region */
-  guchar      **src_rows,	/* Source pixel rows */
-	       *dst_row,	/* Destination pixel row */
-	       *src_ptr,	/* Source pixel pointer */
-	       *sort,		/* Pixel value sort array */
-	       *sort_ptr;	/* Current sort value */
-  gint		sort_count,	/* Number of soft values */
-		i, j, t, d,	/* Looping vars */
-		x, y,		/* Current location in image */
-		row,		/* Current row in src_rows */
-		rowcount,	/* Number of rows loaded */
-		lasty,		/* Last row loaded in src_rows */
-		trow,		/* Looping var */
-		startrow,	/* Starting row for loop */
-		endrow,		/* Ending row for loop */
-		max_row,	/* Maximum number of filled src_rows */
-		size,		/* Width/height of the filter box */
-		width,		/* Byte width of the image */
-		xmin, xmax, tx,	/* Looping vars */
-		radius,		/* Current radius */
-		hist0,		/* Histogram count for 0 values */
-		hist255;	/* Histogram count for 255 values */
-
+  guchar       *buffer=NULL; 	/* Source pixel rows */
+  gint		bpp;		/* bytes per pixel */
   /*
    * Let the user know what we're doing...
    */
@@ -392,181 +377,28 @@ despeckle (void)
    * Setup for filter...
    */
 
-  gimp_pixel_rgn_init (&src_rgn, drawable, sel_x1, sel_y1, sel_width, sel_height,
+  bpp = gimp_drawable_bpp (drawable->drawable_id);
+
+  gimp_pixel_rgn_init (&src_rgn, drawable,
+                       sel_x1, sel_y1, sel_width, sel_height,
 		       FALSE, FALSE);
-  gimp_pixel_rgn_init (&dst_rgn, drawable, sel_x1, sel_y1, sel_width, sel_height,
+  gimp_pixel_rgn_init (&dst_rgn, drawable,
+                       sel_x1, sel_y1, sel_width, sel_height,
 		       TRUE, TRUE);
 
-  size     = despeckle_radius * 2 + 1;
-  max_row  = 2 * gimp_tile_height ();
-  width    = sel_width * img_bpp;
+  g_free (buffer);
+  buffer = g_new (guchar, sel_width * sel_height * bpp);
 
-  src_rows    = g_new (guchar *, max_row);
-  src_rows[0] = g_new (guchar, max_row * width);
+  gimp_pixel_rgn_get_rect (&src_rgn,
+                           buffer, sel_x1, sel_y1, sel_width, sel_height);
 
-  for (row = 1; row < max_row; row ++)
-    src_rows[row] = src_rows[0] + row * width;
+  render_despeckle (buffer, sel_width,  sel_height, bpp, despeckle_radius);
 
-  dst_row = g_new (guchar, width),
-  sort    = g_new (guchar, size * size);
+  gimp_pixel_rgn_set_rect (&dst_rgn,
+                           buffer, sel_x1, sel_y1, sel_width, sel_height);
 
-  /*
-   * Pre-load the first "size" rows for the filter...
-   */
-
-  if (sel_height < gimp_tile_height())
-    rowcount = sel_height;
-  else
-    rowcount = gimp_tile_height ();
-
-  gimp_pixel_rgn_get_rect (&src_rgn, src_rows[0], sel_x1, sel_y1, sel_width,
-			   rowcount);
+  g_free (buffer);
 
-  row   = rowcount;
-  lasty = sel_y1 + rowcount;
-
-  /*
-   * Despeckle...
-   */
-
-  for (y = sel_y1 ; y < sel_y2; y ++)
-    {
-      if ((y + despeckle_radius) >= lasty &&
-	  lasty < sel_y2)
-	{
-	  /*
-	   * Load the next block of rows...
-	   */
-
-	  rowcount -= gimp_tile_height();
-	  if ((i = sel_y2 - lasty) > gimp_tile_height())
-	    i = gimp_tile_height();
-
-	  gimp_pixel_rgn_get_rect (&src_rgn, src_rows[row],
-				   sel_x1, lasty, sel_width, i);
-
-	  rowcount += i;
-	  lasty    += i;
-	  row      = (row + i) % max_row;
-	};
-
-      /*
-       * Now find the median pixels and save the results...
-       */
-
-      radius = despeckle_radius;
-
-      memcpy (dst_row, src_rows[(row + y - lasty + max_row) % max_row], width);
-
-      if (y >= (sel_y1 + radius) && y < (sel_y2 - radius))
-	{
-	  for (x = 0; x < width; x ++)
-	    {
-	      hist0   = 0;
-	      hist255 = 0;
-	      xmin    = x - radius * img_bpp;
-	      xmax    = x + (radius + 1) * img_bpp;
-
-	      if (xmin < 0)
-		xmin = x % img_bpp;
-
-	      if (xmax > width)
-		xmax = width;
-
-	      startrow = (row + y - lasty - radius + max_row) % max_row;
-	      endrow   = (row + y - lasty + radius + 1 + max_row) % max_row;
-
-	      for (sort_ptr = sort, trow = startrow;
-		   trow != endrow;
-		   trow = (trow + 1) % max_row)
-		for (tx = xmin, src_ptr = src_rows[trow] + xmin;
-		     tx < xmax;
-		     tx += img_bpp, src_ptr += img_bpp)
-		  {
-		    if ((*sort_ptr = *src_ptr) <= black_level)
-		      hist0 ++;
-		    else if (*sort_ptr >= white_level)
-		      hist255 ++;
-
-		    if (*sort_ptr < white_level && *sort_ptr > black_level)
-		      sort_ptr ++;
-		  };
-
-	      /*
-	       * Shell sort the color values...
-	       */
-
-	      sort_count = sort_ptr - sort;
-
-	      if (sort_count > 1)
-		{
-		  for (d = sort_count / 2; d > 0; d = d / 2)
-		    for (i = d; i < sort_count; i ++)
-		      for (j = i - d, sort_ptr = sort + j;
-			   j >= 0 && sort_ptr[0] > sort_ptr[d];
-			   j -= d, sort_ptr -= d)
-			{
-			  t           = sort_ptr[0];
-			  sort_ptr[0] = sort_ptr[d];
-			  sort_ptr[d] = t;
-			};
-
-		  /*
-		   * Assign the median value...
-		   */
-
-		  t = sort_count / 2;
-
-		  if (sort_count & 1)
-		    dst_row[x] = (sort[t] + sort[t + 1]) / 2;
-		  else
-		    dst_row[x] = sort[t];
-
-		  /*
-		   * Save the change to the source image too if the user
-		   * wants the recursive method...
-		   */
-
-		  if (filter_type & FILTER_RECURSIVE)
-		    src_rows[(row + y - lasty + max_row) % max_row][x] =
-		      dst_row[x];
-		};
-
-	      /*
-	       * Check the histogram and adjust the radius accordingly...
-	       */
-
-	      if (filter_type & FILTER_ADAPTIVE)
-		{
-		  if (hist0 >= radius || hist255 >= radius)
-		    {
-		      if (radius < despeckle_radius)
-			radius ++;
-		    }
-		  else if (radius > 1)
-		    radius --;
-		};
-	    };
-	};
-
-      gimp_pixel_rgn_set_row (&dst_rgn, dst_row, sel_x1, y, sel_width);
-
-      if ((y & 15) == 0)
-	gimp_progress_update ((double) (y - sel_y1) / (double) sel_height);
-    };
-
-  /*
-   * OK, we're done.  Free all memory used...
-   */
-
-  g_free (src_rows[0]);
-  g_free (src_rows);
-  g_free (dst_row);
-  g_free (sort);
-
-  /*
-   * Update the screen...
-   */
 
   gimp_drawable_flush (drawable);
   gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
@@ -574,6 +406,220 @@ despeckle (void)
 			sel_x1, sel_y1, sel_width, sel_height);
 }
 
+static void
+render_despeckle (guchar *dst_buffer,
+               gint width,
+               gint height,
+               gint bpp,
+               gint initialradius)
+{
+   gint    maskwidth;
+   guchar *mask=NULL;              /* Mask buffer */
+   guchar *row_buffer = NULL;      /* Row operations buffer */
+   guchar *rec_buffer = NULL;	   /* Non FILTER_RECURSIVE buffer */
+   guchar *values = NULL;          /* Channel buffer */
+   gint   row, col;                /* Row and column for buffer */
+   gint   maskrow, maskcol;        /* Row and column for mask buffer */
+   gint   absrow, abscol;          /* Absolute row/col for surrounding pixels */
+   gint   valuecount;              /* Count sected pixels */
+   gint   pos,rowpos,rowposalpha;  /* Position within buffer */
+   gint   medpos;                  /* Median value position in value buffer */
+   gint   alphasum=0;              /* Sum of alpha channel values */
+   gint   radiusrgba[4];           /* Working radius */
+   gint   radius,channel;          /* Non specific loop variable */
+   gint  hist0   = 0;
+   gint  hist255 = 0;
+
+   radiusrgba[0] = radiusrgba[1] = radiusrgba[2] = radiusrgba[3]=
+     initialradius;
+
+   /* filter temporary buffer */
+   rec_buffer = g_new (guchar, width * height * bpp);
+   memcpy (rec_buffer, dst_buffer, width * height * bpp);
+
+   /* Process each row */
+   for (row = 0; row < height; row++)
+     {
+        /* Make sure row buffer is fresh */
+        g_free (row_buffer);
+        row_buffer = g_new (guchar, width * bpp);
+
+        /* copy contents from tomporary buffer in row buffer */
+        memcpy (row_buffer, (rec_buffer+(row*width*bpp)), width*bpp);
+
+        /* process each column */
+        for (col = 0 ; col < width ; col++)
+    	  {
+            /* Calculate starting with alpha */
+            for (channel = bpp-1; channel >= 0; channel--)
+              {
+                /* addaptive radius per channel */
+                radius = radiusrgba[channel];
+                maskwidth = radius * 2 + 1;
+
+                /* Skip edges 
+                if ( (row - radius) < 0 || (row+radius) >= height ||
+                     (col - radius) < 0 || (col+radius) >= width    )
+                  continue;
+                */
+
+                /* Position in buffer */
+                pos         = ((row * width) + col) * bpp + channel;
+                rowpos      = col * bpp + channel;
+                rowposalpha = col * bpp + 3;
+
+                /* Calculate mask */
+                g_free (mask);
+                mask = g_new(guchar, (SQR (maskwidth)));
+                fill_mask (mask, maskwidth);
+
+                /* Value buffer */
+                g_free (values);
+                values = g_new(guchar, (SQR (maskwidth)));
+
+                hist0      = 0;
+                hist255    = 0;
+                valuecount = 0;
+
+                for (maskrow = 0 ; maskrow < maskwidth ; maskrow++)
+                  {
+                    for (maskcol=0 ; maskcol < maskwidth ; maskcol++)
+                      {
+                        abscol = col-radius+maskcol;
+                        absrow = row-radius+maskrow;
+
+                        if (abscol >= 0     &&
+                            absrow >= 0     &&
+                            abscol <  width &&
+                            absrow < height &&
+                            mask[maskrow*maskwidth+maskcol] & 1)
+                          {
+                             if (channel < 3 )
+                               {
+                                 if ( *(rec_buffer+pos) <= black_level)
+                                   hist0 ++;
+                                 else if (*(rec_buffer+pos) >= white_level)
+                                   hist255 ++;
+                                 if ( *(rec_buffer+pos) < white_level && 
+                                      *(rec_buffer+pos) > black_level )
+                                   {
+                                     *(values+valuecount) = *(rec_buffer+pos);
+                                     valuecount++;
+                                   }
+                               }
+                             else
+                               {
+                                  *(values+valuecount) = *(rec_buffer+pos);
+                                  valuecount++;
+                               }
+                          }
+                      }
+                  }
+
+                /*
+                 * Sort pixels and
+                 * Assign the median value...
+                 */
+
+                switch (valuecount)
+                  {
+                    case 0:   /* No values within histogram */
+                      if (channel < 3)
+                        *(row_buffer+rowpos) = 255;
+                      break;
+
+                    case 1:   /* Only one value within histogram so assign */
+                      if (channel == 3)
+                        alphasum =(*(values) == 0) ? 1 :  *(values);
+                      *(row_buffer+rowpos)   = *(values);
+                      break;
+
+                    case 2:   /* Two values median = mean */
+                      if (channel == 3 )
+                        {
+                          alphasum =   *(values) + *(values+1);
+                          *(row_buffer+rowpos) =  alphasum/2 ;
+                          alphasum =(alphasum == 0) ? 1 : alphasum;
+                        }
+                      else
+                        {
+                          alphasum =(alphasum == 0) ? 1 : alphasum;
+                          if (bpp==4)
+                            *(row_buffer+rowpos) = 
+                                      (*(row_buffer+rowposalpha) * 
+                                        (*(values) + *(values+1)))/alphasum;
+                          else
+                            *(row_buffer+rowpos) = (*(values) + *(values+1))/2;
+                        }
+                      break;
+
+                    default:
+                      if (valuecount > SORT_PIVOT)
+                        quick_sort (values, valuecount);
+                      else
+                        bubble_sort (values, valuecount);
+                      if (valuecount & 1)
+                        {
+                          medpos = valuecount/2;
+                          *(row_buffer+rowpos)   = *(values+medpos);
+                        }
+                      else
+                        {
+                          if (channel == 3)
+                            {
+                              alphasum =   *(values) + *(values+1);
+                              *(row_buffer+rowpos) =  alphasum/2 ;
+                            }
+                          else
+                            {
+                              /* compensate for base 0 */
+                              medpos = (valuecount-1)/2;
+                              alphasum =(alphasum == 0) ? 1 : alphasum;
+
+                              if (bpp == 4)
+                                *(row_buffer+rowpos) = 
+                                  (*(row_buffer+rowposalpha) *
+                                    (*(values+medpos) + *(values+medpos+1)) ) / 
+                                    alphasum;
+                              else
+                                *(row_buffer+rowpos) = (*(values+medpos) + 
+                                                        *(values+medpos+1)) / 2;
+                            }
+                        }
+                      break;
+                  }
+
+                if (filter_type & FILTER_RECURSIVE)
+                  *(rec_buffer+pos) = *(row_buffer+rowpos);
+
+                /*
+                 * Check the histogram and adjust the radius accordingly...
+                 */
+
+                if (filter_type & FILTER_ADAPTIVE)
+		  {
+		    if (hist0 >= radius || hist255 >= radius)
+		      {
+		        if (radius < initialradius)
+		  	radius ++;
+		      }
+		    else if (radius > 1)
+		      radius --;
+
+                    radiusrgba[channel]=radius;
+ 		  }
+             } /* channel */
+          } /* colum counter x */
+
+        /* Copy temporatry buffer back to destination buffer */
+        memcpy ((dst_buffer+(row*width*bpp)),row_buffer, width*bpp);
+     } /* row counter y */
+
+     g_free (mask);
+     g_free (values);
+     g_free (row_buffer);
+     g_free (rec_buffer);
+}
 
 /*
  * 'despeckle_dialog()' - Popup a dialog window for the filter box size...
@@ -641,7 +687,7 @@ despeckle_dialog (void)
 				     MIN (preview_width, sel_width));
 
   g_signal_connect (hscroll_data, "value_changed",
-                    G_CALLBACK (preview_scroll_callback),
+                    G_CALLBACK (preview_srcoll),
                     NULL);
 
   scrollbar = gtk_hscrollbar_new (GTK_ADJUSTMENT (hscroll_data));
@@ -655,7 +701,7 @@ despeckle_dialog (void)
 				     MIN (preview_height, sel_height));
 
   g_signal_connect (vscroll_data, "value_changed",
-                    G_CALLBACK (preview_scroll_callback),
+                    G_CALLBACK (preview_srcoll),
                     NULL);
 
   scrollbar = gtk_vscrollbar_new (GTK_ADJUSTMENT (vscroll_data));
@@ -664,7 +710,6 @@ despeckle_dialog (void)
 		    GTK_FILL, 0, 0);
   gtk_widget_show (scrollbar);
 
-  preview_init ();
 
   preview_x1 = sel_x1;
   preview_y1 = sel_y1;
@@ -686,7 +731,7 @@ despeckle_dialog (void)
   button = gtk_check_button_new_with_mnemonic (_("_Adaptive"));
   gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
-				(filter_type & FILTER_ADAPTIVE) ? TRUE : FALSE);
+				(filter_type & FILTER_ADAPTIVE));
   gtk_widget_show (button);
 
   g_signal_connect (button, "toggled",
@@ -696,7 +741,7 @@ despeckle_dialog (void)
   button = gtk_check_button_new_with_mnemonic (_("R_ecursive"));
   gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
-				(filter_type & FILTER_RECURSIVE) ? TRUE : FALSE);
+				(filter_type & FILTER_ADAPTIVE));
   gtk_widget_show (button);
 
   g_signal_connect (button, "toggled",
@@ -761,12 +806,6 @@ despeckle_dialog (void)
   gtk_widget_destroy (dialog);
 
   /*
-   * Free the preview data...
-   */
-
-  preview_exit ();
-
-  /*
    * Return ok/cancel...
    */
 
@@ -775,41 +814,152 @@ despeckle_dialog (void)
 
 
 /*
- * 'preview_init()' - Initialize the preview window...
+ * 'preview_update()' - Update the preview window.
  */
-
 static void
-preview_init (void)
+preview_update (void)
 {
-  gint	size,		/* Size of filter box */
-	width;		/* Byte width of the image */
-
-  /*
-   * Setup for preview filter...
-   */
-
-  size  = despeckle_radius * 2 + 1;
-  width = preview_width * img_bpp;
-
-  if (preview_src != NULL)
-    {
-      g_free (preview_src);
-      g_free (preview_dst);
-      g_free (preview_sort);
-    }
-
-  preview_src  = (guchar *) g_new (guchar *, width * preview_height);
-  preview_dst  = g_new (guchar, width);
-  preview_sort = g_new (guchar, size * size);
+   GimpPixelRgn	src_rgn;	/* Source image region */
+   gint	        x, y, offset;	/* Current location in image */
+   guchar	check;		/* Checkerboard pattern */
+   guchar      *preview_src = NULL;
+   gint         radius      = despeckle_radius;
+   gint         pos, preview_pos;       /* Position within buffer        */
+   guchar      *render_buffer = NULL;   /* Buffer to hold rendered image */
+   gint         bpp;
+
+   /*
+    * Setup for filter...
+    */
+   gimp_pixel_rgn_init (&src_rgn, drawable,
+                        preview_x1, preview_y1, preview_width, preview_height,
+                        FALSE, FALSE);
+   bpp = gimp_drawable_bpp (drawable->drawable_id);
+
+   /*
+    * Pre-load the preview rectangle...
+    */
+   g_free (preview_src);
+   preview_src  = g_new (guchar, preview_width * preview_height * bpp);
+   gimp_pixel_rgn_get_rect (&src_rgn, preview_src, preview_x1, preview_y1,
+                            preview_width, preview_height);
+
+   render_despeckle (preview_src, preview_width,  preview_height, bpp, radius);
+
+   /* Preview is RGB */
+   g_free (render_buffer);
+   render_buffer = g_new (guchar, preview_width * preview_height * 3);
+
+   for ( y = 0 ; y < preview_height ; y++ )
+     for ( x = 0 ; x < preview_width ; x++ )
+       {
+         pos = (y*preview_width+x)*bpp;
+         preview_pos = (y*preview_width+x)*3;
+
+         switch (bpp)
+           {
+             case 1:
+               *(render_buffer+preview_pos)   = *(preview_src+pos);
+               *(render_buffer+preview_pos+1) = *(preview_src+pos);
+               *(render_buffer+preview_pos+2) = *(preview_src+pos);
+               break;
+             case 2:
+               if (*(preview_src+pos) == 255)
+                 {
+                   *(render_buffer+preview_pos)   = *(preview_src+pos);
+                   *(render_buffer+preview_pos+1) = *(preview_src+pos);
+                   *(render_buffer+preview_pos+2) = *(preview_src+pos);
+                 }
+               else
+                 {
+                   if ((y & GIMP_CHECK_SIZE) ^ (x & GIMP_CHECK_SIZE))
+                     check = GIMP_CHECK_LIGHT * 255;
+                   else
+                check = GIMP_CHECK_DARK * 255;
+                   if (*(preview_src+pos) == 0)
+                     {
+                       *(render_buffer+preview_pos)   = check;
+                       *(render_buffer+preview_pos+1) = check;
+                       *(render_buffer+preview_pos+2) = check;
+                     }
+                   else
+                     {
+                       *(render_buffer+preview_pos)   = check + 
+                         ((*(preview_src+pos) - check) * *(preview_src+pos+1)) / 
+                         255;
+                       *(render_buffer+preview_pos+1) = 
+                         *(render_buffer+preview_pos);
+                       *(render_buffer+preview_pos+2) = 
+                         *(render_buffer+preview_pos);
+                     }
+            };
+               break;
+            case 3:
+               *(render_buffer+preview_pos)   = *(preview_src+pos);
+               *(render_buffer+preview_pos+1) = *(preview_src+pos+1);
+               *(render_buffer+preview_pos+2) = *(preview_src+pos+2);
+               break;
+             case 4:
+               if (*(preview_src+pos+3) == 255)
+                 {
+                    *(render_buffer+preview_pos)   = *(preview_src+pos);
+                    *(render_buffer+preview_pos+1) = *(preview_src+pos+1);
+                    *(render_buffer+preview_pos+2) = *(preview_src+pos+2);
+                 }
+               else
+                 {
+                   if ((y & GIMP_CHECK_SIZE) ^ (x & GIMP_CHECK_SIZE))
+                     check = GIMP_CHECK_LIGHT * 255;
+                   else
+                     check = GIMP_CHECK_DARK * 255;
+                   if (*(preview_src+pos+3) == 0)
+                     {
+                        *(render_buffer+preview_pos)   = check;
+                        *(render_buffer+preview_pos+1) = check;
+                        *(render_buffer+preview_pos+2) = check;
+                     }
+                   else
+                     {
+                        *(render_buffer+preview_pos)   = check + 
+                          ((*(preview_src+pos+0) - check) * 
+                          *(preview_src+pos+3)) / 255;
+                        *(render_buffer+preview_pos+1) = check + 
+                          ((*(preview_src+pos+1) - check) * 
+                          *(preview_src+pos+3)) / 255;
+                        *(render_buffer+preview_pos+2) = check + 
+                          ((*(preview_src+pos+2) - check) * 
+                          *(preview_src+pos+3)) / 255;
+                     }
+                 }
+               break;
+  	    }
+
+       }
+
+
+   /*
+    * Update the screen...
+    */
+   for (y = 0; y < preview_height; y++)
+     {
+        offset = y * 3 * preview_width;
+        gtk_preview_draw_row (GTK_PREVIEW (preview),
+                              render_buffer + offset, 0, y, preview_width);
+     }
+
+   gtk_widget_queue_draw (preview);
+
+   g_free (render_buffer);
+   g_free (preview_src);
+   preview_exit();
 }
 
-
 /*
- * 'preview_scroll_callback()' - Update the preview when a scrollbar is moved.
+ * 'preview_scroll()' - Update the preview when a scrollbar is moved.
  */
 
 static void
-preview_scroll_callback (void)
+preview_srcoll (void)
 {
   preview_x1 = sel_x1 + GTK_ADJUSTMENT (hscroll_data)->value;
   preview_y1 = sel_y1 + GTK_ADJUSTMENT (vscroll_data)->value;
@@ -819,241 +969,9 @@ preview_scroll_callback (void)
   preview_update ();
 }
 
-
-/*
- * 'preview_update()' - Update the preview window.
- */
-
-static void
-preview_update (void)
-{
-  GimpPixelRgn	src_rgn;	/* Source image region */
-  guchar       *sort_ptr,	/* Current preview_sort value */
-	       *src_ptr,	/* Current source pixel */
-	       *dst_ptr;	/* Current destination pixel */
-  gint		sort_count,	/* Number of soft values */
-		i, j, t, d,	/* Looping vars */
-		x, y,		/* Current location in image */
-		size,		/* Width/height of the filter box */
-		width,		/* Byte width of the image */
-		xmin, xmax, tx,	/* Looping vars */
-		radius,		/* Current radius */
-		hist0,		/* Histogram count for 0 values */
-		hist255;	/* Histogram count for 255 values */
-  guchar	check,		/* Checkerboard pattern */
-		rgb[PREVIEW_SIZE * PREVIEW_SIZE * 3],
-				/* Output image */
-	       *rgb_ptr;	/* Pixel pointer for output */
-
- /*
-  * Setup for filter...
-  */
-
-  gimp_pixel_rgn_init (&src_rgn, drawable,
-		       preview_x1, preview_y1, preview_width, preview_height,
-		       FALSE, FALSE);
-
-  /*
-   * Pre-load the preview rectangle...
-   */
-
-  size  = despeckle_radius * 2 + 1;
-  width = preview_width * img_bpp;
-
-  gimp_pixel_rgn_get_rect (&src_rgn, preview_src, preview_x1, preview_y1,
-			   preview_width, preview_height);
-
-  /*
-   * Despeckle...
-   */
-
-  for (y = 0; y < preview_height; y ++)
-    {
-      /*
-       * Now find the median pixels and save the results...
-       */
-
-      radius = despeckle_radius;
-
-      memcpy (preview_dst, preview_src + y * width, width);
-
-      if (y >= radius && y < (preview_height - radius))
-	{
-	  for (x = 0, dst_ptr = preview_dst; x < width; x ++, dst_ptr ++)
-	    {
-	      hist0   = 0;
-	      hist255 = 0;
-	      xmin    = x - radius * img_bpp;
-	      xmax    = x + (radius + 1) * img_bpp;
-
-	      if (xmin < 0)
-		xmin = x % img_bpp;
-
-	      if (xmax > width)
-		xmax = width;
-
-	      for (i = -radius, sort_ptr = preview_sort,
-		     src_ptr = preview_src + width * (y - radius);
-		   i <= radius;
-		   i ++, src_ptr += width)
-		for (tx = xmin; tx < xmax; tx += img_bpp)
-		  {
-		    if ((*sort_ptr = src_ptr[tx]) <= black_level)
-		      hist0 ++;
-		    else if (*sort_ptr >= white_level)
-		      hist255 ++;
-
-		    if (*sort_ptr < white_level && *sort_ptr > black_level)
-		      sort_ptr ++;
-		  };
-
-	      /*
-	       * Shell preview_sort the color values...
-	       */
-
-	      sort_count = sort_ptr - preview_sort;
-
-	      if (sort_count > 1)
-		{
-		  for (d = sort_count / 2; d > 0; d = d / 2)
-		    for (i = d; i < sort_count; i ++)
-		      for (j = i - d, sort_ptr = preview_sort + j;
-			   j >= 0 && sort_ptr[0] > sort_ptr[d];
-			   j -= d, sort_ptr -= d)
-			{
-			  t           = sort_ptr[0];
-			  sort_ptr[0] = sort_ptr[d];
-			  sort_ptr[d] = t;
-			};
-
-		  /*
-		   * Assign the median value...
-		   */
-
-		  t = sort_count / 2;
-
-		  if (sort_count & 1)
-		    *dst_ptr = (preview_sort[t] + preview_sort[t + 1]) / 2;
-		  else
-		    *dst_ptr = preview_sort[t];
-
-		  /*
-		   * Save the change to the source image too if the user
-		   * wants the recursive method...
-		   */
-
-		  if (filter_type & FILTER_RECURSIVE)
-		    preview_src[y * width + x] = *dst_ptr;
-		};
-
-	      /*
-	       * Check the histogram and adjust the radius accordingly...
-	       */
-
-	      if (filter_type & FILTER_ADAPTIVE)
-		{
-		  if (hist0 >= radius || hist255 >= radius)
-		    {
-		      if (radius < despeckle_radius)
-			radius ++;
-		    }
-		  else if (radius > 1)
-		    radius --;
-		};
-	    };
-	};
-
-      /*
-       * Draw this row...
-       */
-
-      rgb_ptr = rgb + y * preview_width * 3;
-
-      switch (img_bpp)
-	{
-	case 1:
-          for (x = preview_width, dst_ptr = preview_dst;
-               x > 0;
-               x --, dst_ptr ++, rgb_ptr += 3)
-            rgb_ptr[0] = rgb_ptr[1] = rgb_ptr[2] = *dst_ptr;
-          break;
-
-	case 2:
-	  for (x = preview_width, dst_ptr = preview_dst;
-	       x > 0;
-	       x --, dst_ptr += 2, rgb_ptr += 3)
-	    if (dst_ptr[1] == 255)
-	      rgb_ptr[0] = rgb_ptr[1] = rgb_ptr[2] = *dst_ptr;
-	    else
-	      {
-		if ((y & GIMP_CHECK_SIZE) ^ (x & GIMP_CHECK_SIZE))
-		  check = GIMP_CHECK_LIGHT * 255;
-		else
-		  check = GIMP_CHECK_DARK * 255;
-
-		if (dst_ptr[1] == 0)
-		  rgb_ptr[0] = rgb_ptr[1] = rgb_ptr[2] = check;
-		else
-		  rgb_ptr[0] = rgb_ptr[1] = rgb_ptr[2] =
-                    check + ((dst_ptr[0] - check) * dst_ptr[1]) / 255;
-	      };
-          break;
-
-	case 3:
-          memcpy (rgb_ptr, preview_dst, preview_width * 3);
-          break;
-
-	case 4:
-	  for (x = preview_width, dst_ptr = preview_dst;
-	       x > 0;
-	       x --, dst_ptr += 4, rgb_ptr += 3)
-	    if (dst_ptr[3] == 255)
-	      {
-		rgb_ptr[0] = dst_ptr[0];
-		rgb_ptr[1] = dst_ptr[1];
-		rgb_ptr[2] = dst_ptr[2];
-	      }
-	    else
-	      {
-		if ((y & GIMP_CHECK_SIZE) ^ (x & GIMP_CHECK_SIZE))
-		  check = GIMP_CHECK_LIGHT * 255;
-		else
-		  check = GIMP_CHECK_DARK * 255;
-
-		if (dst_ptr[3] == 0)
-		  rgb_ptr[0] = rgb_ptr[1] = rgb_ptr[2] = check;
-		else
-		  {
-		    rgb_ptr[0] =
-		      check + ((dst_ptr[0] - check) * dst_ptr[3]) / 255;
-		    rgb_ptr[1] =
-		      check + ((dst_ptr[1] - check) * dst_ptr[3]) / 255;
-		    rgb_ptr[2] =
-		      check + ((dst_ptr[2] - check) * dst_ptr[3]) / 255;
-		  };
-	      };
-          break;
-	};
-    };
-
-  /*
-   * Update the screen...
-   */
-
-  for (y = 0, rgb_ptr = rgb;
-       y < preview_height;
-       y ++, rgb_ptr += preview_width * 3)
-    gtk_preview_draw_row (GTK_PREVIEW (preview), rgb_ptr, 0, y, preview_width);
-
-  gtk_widget_queue_draw (preview);
-}
-
 static void
 preview_exit (void)
 {
-  g_free (preview_src);
-  g_free (preview_dst);
-  g_free (preview_sort);
 }
 
 static void
@@ -1062,9 +980,6 @@ dialog_iscale_update (GtkAdjustment *adj
 {
   *value = adjustment->value;
 
-  if (value == &despeckle_radius)
-    preview_init ();
-
   preview_update ();
 }
 
@@ -1091,3 +1006,107 @@ dialog_recursive_callback (GtkWidget *wi
 
   preview_update ();
 }
+
+static void
+fill_mask (guchar *mask,
+           gint    width)
+{
+   /*    Radius 3 generates a mask like :
+         -     +
+  	 3210123
+
+      -3 0011100
+       2 0111110
+       1 1111111
+       0 1111111
+       1 1111111
+       2 0111110
+      +3 0011100
+
+   */
+
+    gint r  = (width - 1) / 2;
+    gint r2 = (gint) (SQR (r)) + 1;
+    gint x,y;
+
+    for (x = -r; x <= r; x++)
+      {
+       for (y = -r; y <= r; y++)
+         {
+           if ((SQR(x) + SQR(y)) <= r2)
+             mask[r + x + (r+y) * width] = 1;
+           else
+             mask[r + x + (r+y) * width] = 0;
+         }
+      }
+}
+
+/* School book sort */
+static void
+quick_sort (guchar *buffer,
+            gint    size)
+{
+  q_sort (buffer, 0, size - 1);
+}
+
+static void
+q_sort (guchar *buffer,
+        gint    left,
+        gint    right)
+{
+  gint p, l, r;
+
+  l = left;
+  r = right;
+  p = buffer[left];
+
+  while (left < right)
+    {
+      while ((buffer[right] >= p) && (left < right))
+        right--;
+
+      if (left != right)
+        {
+          buffer[left] = buffer[right];
+          left++;
+        }
+      while ((buffer[left] <= p) && (left < right))
+        left++;
+      if (left != right)
+        {
+          buffer[right] = buffer[left];
+          right--;
+        }
+    }
+
+  buffer[left] = p;
+  p = left;
+  left = l;
+  right = r;
+
+  if (left < p)
+    q_sort (buffer, l, p-1);
+  if (right > p)
+    q_sort (buffer, p+1, r);
+}
+
+/* School book sort */
+static void
+bubble_sort (guchar *buffer,
+             gint    size)
+{
+  gint i, j, t;
+
+  for (i = (size - 1); i >= 0; i--)
+    {
+      for (j = 1; j <= i; j++)
+        {
+          if (buffer[j-1] > buffer[j])
+            {
+              t = buffer[j-1];
+              buffer[j-1] = buffer[j];
+              buffer[j] = t;
+            }
+        }
+    }
+}
