Index: despeckle.c
===================================================================
RCS file: /cvs/gnome/gimp/plug-ins/common/despeckle.c,v
retrieving revision 1.69
diff -u -r1.69 despeckle.c
--- despeckle.c	15 Sep 2004 19:56:01 -0000	1.69
+++ despeckle.c	15 Oct 2004 17:24:57 -0000
@@ -33,7 +33,18 @@
 #include <libgimp/gimpui.h>
 
 #include "libgimp/stdplugins-intl.h"
+typedef enum
+{
+  MEDIAN,
+  FMVMF,
+  DDF
+} FilterMethod;
 
+typedef struct
+{
+  gint          diameter;
+  FilterMethod  method;
+} FilterValues;
 
 /*
  * Constants...
@@ -48,13 +59,21 @@
 
 #define FILTER_ADAPTIVE  0x01
 #define FILTER_RECURSIVE 0x02
+#define FILTER_FMVMF     0x04
 
-#define despeckle_radius (despeckle_vals[0])    /* Radius of filter */
+#define despeckle_radius (despeckle_vals[0])    /* diameter of filter */
 #define filter_type      (despeckle_vals[1])    /* Type of filter */
 #define black_level      (despeckle_vals[2])    /* Black level */
 #define white_level      (despeckle_vals[3])    /* White level */
-#define update_toggle    (despeckle_vals[4])    /* Update the preview? */
+#define threshold        (despeckle_vals[4])    /* Threshold for tvmf */
+#define weight           (despeckle_vals[5])    /* weight DDF for BVDMF and VMF */
+#define update_toggle    (despeckle_vals[6])    /* Update the preview? */
+
+#define VALUE_SWAP(a,b)   { register  gdouble   t=(a);(a)=(b);(b)=t;} 
+#define POINTER_SWAP(a,b) { register  guchar*   t=(a);(a)=(b);(b)=t;} 
+
 
+    
 /*
  * Local functions...
  */
@@ -67,15 +86,60 @@
                         GimpParam       **return_vals);
 
 static void      despeckle (void);
-
-static gboolean  despeckle_dialog          (void);
-
-static void      dialog_iscale_update      (GtkAdjustment *, gint *);
-static void      dialog_adaptive_callback  (GtkWidget *, gpointer);
-static void      dialog_recursive_callback (GtkWidget *, gpointer);
-
-static void      preview_update            (GtkWidget *preview);
-
+static void      despeckle_median (guchar  *src, 
+                                   guchar  *dst, 
+                                   gint     width, 
+                                   gint     height, 
+                                   gint     bpp, 
+                                   gint     radius,
+                                   gboolean prevmode);
+static void      despeckle_fmvmf  (guchar  *src, 
+                                   guchar  *dst, 
+                                   gint     width, 
+                                   gint     height, 
+                                   gint     bpp, 
+                                   gint     radius,
+                                   gboolean prevmode);
+static void      despeckle_ddf    (guchar  *src, 
+                                   guchar  *dst, 
+                                   gint     width, 
+                                   gint     height, 
+                                   gint     bpp, 
+                                   gint     radius,
+                                   gboolean prevmode); 
+
+static gboolean  despeckle_dialog (void);
+static gint      row_tile_cache   (gint     width, 
+                                   gint     radius, 
+                                   gint     bpp);                                            
+
+static void      dialog_iscale_update      (GtkAdjustment *, 
+                                            gint          *);
+static void      dialog_adaptive_callback  (GtkWidget     *, 
+                                            gpointer);
+static void      dialog_recursive_callback (GtkWidget     *,
+                                            gpointer);
+static void      my_radio_button_update    (GtkWidget     *, 
+                                            gpointer);
+
+static void      preview_update            (GtkWidget     *preview);
+
+static gint      quick_median_select       (guchar       **p, 
+                                            guchar        *i,
+                                            gint           n);
+static guchar    pixel_intensity           (guchar        *p,                                            
+                                            gint           bpp);
+static void      pixel_copy                (guchar        *a,
+                                            guchar        *b, 
+                                            gint           n);
+static gint      vector_norm               (guchar        *a, 
+                                            gint           n);       
+static gint      vector_dotp               (guchar        *a,
+                                            guchar        *b, 
+                                            gint           n);
+static gint      vector_diff               (guchar        *a,
+                                            guchar        *b,
+                                            gint           n);
 
 /*
  * Globals...
@@ -100,15 +164,23 @@
                        sel_height;              /* Selection height */
 static gint            img_bpp;                 /* Bytes-per-pixel in image */
 
-static gint despeckle_vals[5] =
+static gint despeckle_vals[7] =
 {
-  3,                  /* Default value for the radius */
+  3,                  /* Default value for the diameter */
   FILTER_ADAPTIVE,    /* Default value for the filter type */
   7,                  /* Default value for the black level */
   248,                /* Default value for the white level */
+  1  ,                /* Default value for threshold */
+  127,                /* Default value for weight    */
   TRUE                /* Default value for the update toggle */
 };
 
+static FilterValues fvals =
+{
+  3.0,  /*  y diameter  */
+  MEDIAN
+};
+
 
 /*
  * 'main()' - Main entry - just call gimp_main()...
@@ -128,10 +200,11 @@
     { GIMP_PDB_INT32,    "run_mode", "Interactive, non-interactive" },
     { GIMP_PDB_IMAGE,    "image",    "Input image" },
     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
-    { GIMP_PDB_INT32,    "radius",   "Filter box radius (default = 3)" },
+    { GIMP_PDB_INT32,    "diameter",   "Filter box diameter (default = 3)" },
     { GIMP_PDB_INT32,    "type",     "Filter type (0 = median, 1 = adaptive, 2 = recursive-median, 3 = recursive-adaptive)" },
     { GIMP_PDB_INT32,    "black",    "Black level (-1 to 255)" },
-    { GIMP_PDB_INT32,    "white",    "White level (0 to 256)" }
+    { GIMP_PDB_INT32,    "white",    "White level (0 to 256)" },
+    { GIMP_PDB_INT32,    "threshold","Threschold for TFVM and DDF filter" }
   };
 
   gimp_install_procedure (PLUG_IN_NAME,
@@ -214,9 +287,12 @@
       /*
        * Get information from the dialog...
        */
-
-      if (!despeckle_dialog ())
-        return;
+      if (gimp_drawable_is_rgb(drawable->drawable_id) ||
+          gimp_drawable_is_gray(drawable->drawable_id))
+       {
+          if (!despeckle_dialog ())
+          return;
+       }
       break;
 
     case GIMP_RUN_NONINTERACTIVE:
@@ -224,14 +300,14 @@
        * Make sure all the arguments are present...
        */
 
-      if (nparams < 4 || nparams > 7)
+      if (nparams < 4 || nparams > 9)
         status = GIMP_PDB_CALLING_ERROR;
       else if (nparams == 4)
         {
           despeckle_radius = param[3].data.d_int32;
           filter_type      = FILTER_ADAPTIVE;
           black_level      = 7;
-          white_level      = 248;
+          white_level      = 248;         
         }
       else if (nparams == 5)
         {
@@ -246,6 +322,24 @@
           filter_type      = param[4].data.d_int32;
           black_level      = param[5].data.d_int32;
           white_level      = 248;
+          threshold        = 127;
+        }
+      else if (nparams == 7)
+        {
+          despeckle_radius = param[3].data.d_int32;
+          filter_type      = param[4].data.d_int32;
+          black_level      = param[5].data.d_int32;
+          white_level      = param[6].data.d_int32;
+          threshold        = 127;
+        }
+      else if (nparams == 8)
+        {
+          despeckle_radius = param[3].data.d_int32;
+          filter_type      = param[4].data.d_int32;
+          black_level      = param[5].data.d_int32;
+          white_level      = param[6].data.d_int32;
+          threshold        = param[7].data.d_int32;
+          weight           = 127;
         }
       else
         {
@@ -253,6 +347,8 @@
           filter_type      = param[4].data.d_int32;
           black_level      = param[5].data.d_int32;
           white_level      = param[6].data.d_int32;
+          threshold        = param[7].data.d_int32;
+          weight           = param[8].data.d_int32;
         }
       break;
 
@@ -276,15 +372,13 @@
 
   if (status == GIMP_PDB_SUCCESS)
     {
-      if ((gimp_drawable_is_rgb(drawable->drawable_id) ||
-           gimp_drawable_is_gray(drawable->drawable_id)))
+    	if (gimp_drawable_is_rgb(drawable->drawable_id) ||
+            gimp_drawable_is_gray(drawable->drawable_id))
         {
           /*
            * Set the tile cache size...
-           */
-
-          gimp_tile_cache_ntiles (2 * (drawable->width + gimp_tile_width() - 1) /
-                                  gimp_tile_width() + 1);
+           */ 
+          gimp_tile_cache_size(2*row_tile_cache(sel_width, MAX_RADIUS, img_bpp));
 
           /*
            * Run!
@@ -293,7 +387,7 @@
           despeckle ();
 
           /*
-           * If run mode is interactive, flush displays...
+           * If run prevmode is interactive, flush displays...
            */
 
           if (run_mode != GIMP_RUN_NONINTERACTIVE)
@@ -333,7 +427,7 @@
  * circular row buffer to improve performance.
  *
  * The adaptive filter is based on the median filter but analizes the histogram
- * of the region around the target pixel and adjusts the despeckle radius
+ * of the region around the target pixel and adjusts the despeckle diameter
  * accordingly.
  */
 
@@ -341,219 +435,45 @@
 despeckle (void)
 {
   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 */
-
-  /*
-   * Let the user know what we're doing...
-   */
-
-  gimp_progress_init (_("Despeckling..."));
-
-  /*
-   * Setup for filter...
-   */
-
+                dst_rgn;
+  guchar        *src, *dst;
+ 
   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,
                        TRUE, TRUE);
-
-  size     = despeckle_radius * 2 + 1;
-  max_row  = 2 * gimp_tile_height ();
-  width    = sel_width * img_bpp;
-
-  src_rows    = g_new (guchar *, max_row);
-  src_rows[0] = g_new (guchar, max_row * width);
-
-  for (row = 1; row < max_row; row ++)
-    src_rows[row] = src_rows[0] + row * width;
-
-  dst_row = g_new (guchar, width),
-  sort    = g_new (guchar, size * size);
-
-  /*
-   * 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);
-
-  row   = rowcount;
-  lasty = sel_y1 + rowcount;
-
-  /*
-   * Despeckle...
-   */
-
-  for (y = sel_y1 ; y < sel_y2; y ++)
-    {
-      if ((y + despeckle_radius) >= lasty &&
-          lasty < sel_y2)
+  src  = g_new (guchar, sel_width * sel_height * img_bpp);
+  dst  = g_new (guchar, sel_width * sel_height * img_bpp);
+  gimp_pixel_rgn_get_rect (&src_rgn, src, sel_x1, sel_y1,sel_width, sel_height);
+  switch(fvals.method)
+    {         
+      case FMVMF:
         {
-          /*
-           * 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;
+          despeckle_fmvmf(src, dst, sel_width, sel_height,  img_bpp, despeckle_radius, FALSE);
         }
-
-      /*
-       * 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))
+        break;
+      case DDF:
         {
-          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 --;
-                }
-            }
+          despeckle_ddf(src, dst, sel_width, sel_height,  img_bpp, despeckle_radius, FALSE);
         }
-
-      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);
+        break;
+      default :
+        {
+          despeckle_median(src, dst, sel_width, sel_height,  img_bpp, despeckle_radius, FALSE);
+        }
+        break;
     }
-
-  /*
-   * 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_pixel_rgn_set_rect (&dst_rgn, dst, sel_x1, sel_y1, sel_width, sel_height);
   gimp_drawable_flush (drawable);
   gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
   gimp_drawable_update (drawable->drawable_id,
                         sel_x1, sel_y1, sel_width, sel_height);
+  g_free(src);                      
+  g_free(dst);
+
 }
 
 
@@ -567,6 +487,7 @@
   GtkWidget *dialog;
   GtkWidget *main_vbox;
   GtkWidget *vbox;
+  GtkWidget *hbox;
   GtkWidget *table;
   GtkWidget *frame;
   GtkWidget *button;
@@ -604,6 +525,26 @@
   frame = gimp_frame_new (_("Type"));
   gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
   gtk_widget_show (frame);
+  
+  
+   hbox = gtk_hbox_new (FALSE, 12);
+  gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
+  gtk_widget_show (hbox);
+
+  /*  parameter settings  */
+  frame = gimp_int_radio_group_new (TRUE, _("Filter Method"),
+                                    G_CALLBACK (my_radio_button_update),
+                                    &fvals.method, fvals.method,
+                                    _("_Median"), MEDIAN, NULL,
+                                    _("_FMVMF"), FMVMF, NULL,                                 
+                                    _("_DDF"), DDF, NULL,
+                                    NULL);
+
+  gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+  gtk_widget_show (frame);
+ 
+ 
+  frame = gimp_frame_new (_("Median"));
 
   vbox = gtk_vbox_new (FALSE, 6);
   gtk_container_add (GTK_CONTAINER (frame), vbox);
@@ -629,14 +570,17 @@
                     G_CALLBACK (dialog_recursive_callback),
                     NULL);
 
-  table = gtk_table_new (3, 3, FALSE);
+  gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
+  gtk_widget_show (frame);
+
+  table = gtk_table_new (4, 3, FALSE);
   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
   gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
   gtk_widget_show (table);
 
   /*
-   * Box size (radius) control...
+   * Box size (diameter) control...
    */
 
   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
@@ -675,6 +619,32 @@
                     &white_level);
 
   /*
+   * threshold control...
+   */
+
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 3,
+                              _("_Threshold:"), SCALE_WIDTH, ENTRY_WIDTH,
+                              threshold, -1, 255, 1, 8, 0,
+                              TRUE, 0, 0,
+                              NULL, NULL);
+  g_signal_connect (adj, "value_changed",
+                    G_CALLBACK (dialog_iscale_update),
+                    &threshold);
+
+  /*
+   * threshold control...
+   */
+
+  adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 4,
+                              _("_Weight:"), SCALE_WIDTH, ENTRY_WIDTH,
+                              weight, 1, 255, 1, 8, 0,
+                              TRUE, 0, 0,
+                              NULL, NULL);
+  g_signal_connect (adj, "value_changed",
+                    G_CALLBACK (dialog_iscale_update),
+                    &threshold);
+
+  /*
    * Show it and wait for the user to do something...
    */
 
@@ -699,29 +669,13 @@
 preview_update (GtkWidget *widget)
 {
   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 */
-                x1, y1,         /* Offset of the preview from the drawable */
-                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       *rgba;           /* Output image */
+  guchar       *dst;            /* Output image */
   GimpPreview  *preview;        /* The preview widget */
-
-  guchar       *preview_src;    /* Source pixel rows */
-  guchar       *preview_dst;    /* Destination pixel row */
-  guchar       *preview_sort;   /* Pixel value sort array */
-
+  guchar       *src;            /* Source pixel rows */
+  gint x1,y1;
   preview = GIMP_PREVIEW (widget);
-  rgba = g_new (guchar, preview->width * preview->height * img_bpp);
-  /*
+
+          /*
    * Setup for filter...
    */
   gimp_preview_get_position (preview, &x1, &y1);
@@ -733,135 +687,36 @@
   /*
    * Pre-load the preview rectangle...
    */
-
-  size  = despeckle_radius * 2 + 1;
-  width = preview->width * img_bpp;
-
-  preview_src  = g_new (guchar, width * preview->height);
-  preview_dst  = g_new (guchar, width);
-  preview_sort = g_new (guchar, size * size);
-
-  gimp_pixel_rgn_get_rect (&src_rgn, preview_src, x1, y1,
+  dst = g_new (guchar, preview->width * preview->height * img_bpp);
+  src  = g_new (guchar, preview->width * img_bpp *preview->height);
+  gimp_pixel_rgn_get_rect (&src_rgn, src, x1, 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))
+  switch(fvals.method)
+    {         
+      case FMVMF:
         {
-          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 --;
-                }
-            }
+          despeckle_fmvmf(src, dst, preview->width, preview->height,  img_bpp, despeckle_radius, TRUE);
         }
-
-      /*
-       * Draw this row...
-       */
-
-      memcpy (rgba + y * width, preview_dst, width);
+        break;
+      case DDF:
+        {
+          despeckle_ddf(src, dst, preview->width, preview->height,  img_bpp, despeckle_radius, TRUE);
+        }
+        break;
+      default :
+        {
+          despeckle_median(src, dst, preview->width, preview->height,  img_bpp, despeckle_radius, TRUE);
+        }
+        break;
     }
-
   /*
    * Update the screen...
    */
-
   gimp_drawable_preview_draw_buffer (GIMP_DRAWABLE_PREVIEW (preview),
-                                     rgba, preview->width * img_bpp);
-  g_free (rgba);
-
-  g_free (preview_src);
-  g_free (preview_dst);
-  g_free (preview_sort);
+                                     dst, preview->width * img_bpp);
+  g_free (dst);
+  g_free (src);
 }
 
 static void
@@ -896,3 +751,500 @@
 
   gimp_preview_invalidate (GIMP_PREVIEW (preview));
 }
+
+static void
+my_radio_button_update (GtkWidget *widget,
+			  gpointer   data)
+{
+  gint *toggle_val;
+
+  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+    {
+      toggle_val = (gint *) data;
+
+      *toggle_val = (gint) gtk_object_get_user_data (GTK_OBJECT (widget));
+    }
+
+  gimp_toggle_button_sensitive_update (GTK_TOGGLE_BUTTON (widget));
+  gimp_preview_invalidate (GIMP_PREVIEW (preview));
+}
+
+
+static void
+despeckle_median(guchar *src, guchar *dst, gint width, gint height, gint bpp, gint radius, gboolean prevmode)
+{  
+  if ( !prevmode )
+    {
+       gimp_progress_init(_("Despeckle"));
+       gimp_progress_update (0.0);
+    }
+  gint          pos1, pos2, med, x, y, jh,jv, box, hist0, hist255, diameter;
+  guchar        **buf;
+  guchar        *ibuf, *pixel;
+  gdouble       prog, maxprog;
+  maxprog  = width*height;
+  prog     = 0;
+
+  diameter=(2*radius)+1;
+  box     =diameter*diameter; 
+  buf     = g_new(guchar*, box*sizeof(guchar *));
+  ibuf    = g_new(guchar,  box);
+  for ( x = 0 ;  x < width; x++ )
+    {
+       prog+=width;
+       for ( y = 0 ;  y < height; y++ )
+         {            
+            hist0   = 0;
+            hist255 = 0;                       	
+            if ( x >= radius && y >= radius &&  x < width-radius && y < height-radius )
+              {
+                 /* Make sure Svm is ininialized to a sufficient large value */
+                 med=0;                  
+                 for ( jh = x-radius ; jh <= x+radius ; jh++)
+	           {         
+	              for ( jv = y-radius, pos1=0 ; jv <= y+radius ; jv++)
+                        {                             
+                           pos2=(jh+(jv*width))*bpp;
+                           if (*(src+pos2) > black_level && *(src+pos2) < white_level )
+                             {
+		               *(buf+med)=src+pos2;
+                               *(ibuf+med)=pixel_intensity(src+pos2,bpp);
+                               med++;
+                             }
+                           else
+                             {
+                             	if (*(src+pos2) <= black_level) 
+                             	  hist0++;
+                             	if (*(src+pos2) >= white_level) 
+                             	  hist255++;
+                             }
+                        }                                            
+                   }
+                 if ( med < 2 ) 
+                   {  
+                     pos1 = (x+(y*width))*bpp;
+                     pixel_copy(dst+pos1, src+pos1, bpp);                              
+                   }
+                 else
+                   {
+                     pos1 = (x+(y*width))*bpp;
+                     med  = quick_median_select(buf, ibuf, med+1);
+                     pixel = buf[med];
+                     if (filter_type & FILTER_RECURSIVE)
+                       pixel_copy(src+pos1, pixel, bpp);                     
+                     pixel_copy(dst+pos1, pixel, bpp);
+                   }
+              } 
+            else 
+              {
+                 pos1 = (x+(y*width))*bpp;
+                 pixel_copy(dst+pos1, src+pos1, bpp);            
+              }
+            /*
+             * Check the histogram and adjust the diameter accordingly...
+             */
+            if (filter_type & FILTER_ADAPTIVE)
+              {
+                if (hist0 >= radius || hist255 >= radius)
+                  {
+                    if ( radius < diameter/2)
+                      {
+                        radius++;
+                      }
+                  }
+                else if (radius > 1)
+                  {
+                     radius--;
+                  }
+              }
+            if ( !prevmode )
+              {                 
+                 gimp_progress_update (prog/maxprog);
+              }
+         }
+    }
+  g_free(buf);
+  g_free(ibuf);
+}
+
+
+
+
+/*
+ * Fast Modified Vector Median Filter (FMVMF)
+ * 
+ * References :
+ * B. Smolka, A. Chydzinski, M. Szczepanski, K. N. Plataniotis, and A. N. Venet-sanopoulos. 
+ * On a new class of filters for the impulsive noise reduction in color images. 
+ * In S. Loncaric and H. Babic, editors, 
+ * Proceedings of the 2nd International Symposium on Image and Signal Analysis, 
+ * Pula, Croatia, pages 114 118, June 2001. 
+ * 
+ * B. Smolka, A. Chydzinski, K. Wojciechowski, K. Plataniotis, and A.N. Venet-sanopoulos. 
+ * On the reduction of impulsive noise in multichannel image process-ing. 
+ * Optical Engineering, 40(6):902 908, 2001. 
+ * 
+ * B. Smolka, M. Szczepanski, K.N. Plataniotis, and A. N. Venetsanopoulos. 
+ * Fast modified vector median filter. In W. Skarbek, editor, 
+ * Computer Analysis of Images and Patterns, LNCS, volume 2124, 
+ * pages 570 580. Springer-Verlag, 2001.
+ * 
+ * URL: http://dx.doi.org/10.1155/S1024123X04110016
+ * 
+ */
+static void 
+despeckle_fmvmf(guchar *src, guchar *dst, gint width, gint height, gint bpp, gint radius, gboolean prevmode)
+{
+  gint          pos1,pos2,x,y,x1,y1,x2,y2,xx1,yy1,xx2,yy2,diameter,c;
+  gdouble       sim,max,beta, *expo;  
+  guchar        *pixel;
+  if ( !prevmode )
+    {
+       gimp_progress_init(_("Despeckle"));
+       gimp_progress_update (0);
+    }
+
+  /* create odd diameter */
+  radius=(radius>4)?4:radius;
+  diameter=(2*radius)+1;
+
+  pixel = g_new (guchar, bpp);
+
+  /* beta - similarity function coefficient */
+  beta = 0.00504;
+  expo = g_new(gdouble, 256*256);
+  for (x=0 ; x < 256 ; x++ )
+    {
+      for (y=0 ; y < 256 ; y++ )
+        {
+          expo[x+y*256]=exp(-beta*abs(x-y));
+        }
+    }
+  for ( x=0 ; x < width ;x++)
+    {
+       for ( y=0 ; y < height ;y++)
+          {
+             max=-1;
+             pos2 = (x+y*width)*bpp;
+             pixel_copy(pixel, src+pos2, bpp);
+             for (x2=-radius ; x2 <= radius ; x2++)
+               {
+                 for (y2=-radius ; y2 <= radius ; y2++)
+                   {                                  
+                     xx2=x+x2;
+                     yy2=y+y2;
+                     sim=0;
+                     for (x1=-radius ; x1 <= radius ; x1++)
+                       {
+                         for (y1=-radius ; y1 <= radius ; y1++)
+                           {
+                              xx1=x+x1;
+                              yy1=y+y1;
+                              if (     (x2  != x1 || y2  !=y1  ) 
+                                    && (x1  != 0  || y1  !=0   ) 
+                                    && (xx1 > -1  && yy1 > -1  )
+                                    && (xx2 > -1  && yy2 > -1  )
+                                  )
+                                {                             
+                                   pos1 = (xx1+yy1*width)*bpp;
+                                   pos2 = (xx2+yy2*width)*bpp;
+                                   /* sum precalculated simularity value*/                                     
+                                   for ( c = 0 ; c < bpp ; c++)
+                                     {
+                                       sim+=expo[src[pos1+c]+src[pos2+c]*256];
+                                     }
+                                }
+                           }
+                       }                     
+                     if ( sim>max &&  xx2>-1 && yy2 >-1 )
+                       {
+                          max=sim;
+                          pos2 = (xx2+yy2*width)*bpp;
+                          pixel_copy(pixel, src+pos2, bpp);
+                       }
+                   }
+               }
+               pos2 = (x+(y*width))*bpp;
+               if (filter_type & FILTER_RECURSIVE)
+                  pixel_copy(src+pos2, pixel, bpp);
+               pixel_copy(dst+pos2, pixel, bpp);
+               if ( !prevmode )
+                 {                 
+                   gimp_progress_update ((x+(y*width)+1.0)/(width*height));
+                 }
+          }
+    }
+  g_free(pixel);                       
+  g_free(expo);              
+}
+
+
+
+static void
+despeckle_ddf (guchar *src, guchar *dst, gint width, gint height, gint bpp, gint radius, gboolean prevmode)
+{
+  gint          iv,ih,jv,jh,diameter, x, y,pos, pos1, pos2, pos3,count,size;
+  gdouble       *Dvm, *Svm;
+  gdouble       dvm, sum, dsum, asum;
+  gdouble       *norm;
+  guchar        *psrc;
+  guchar        *pixels;
+  gint          diff;
+  gdouble       dot,ang;  
+  gdouble       norm1;
+  gdouble       norm2;
+  gdouble       pow1, pow2;
+  gdouble       prog, maxprog;
+ 
+  if ( !prevmode )
+    {
+       gimp_progress_init(_("Despeckle"));
+       gimp_progress_update (0);
+    }
+  
+  radius   = (radius>2)?2:radius;
+  diameter = (radius*2+1);  
+  size     = diameter*diameter;
+  maxprog  = width*height;
+  prog     = 0;
+  pow1     = threshold/255;
+  pow2     = (255 - threshold)/255;
+
+  /*
+   * Despeckle...
+   */   
+  Dvm     = g_new(gdouble, size*sizeof(gdouble));
+  Svm     = g_new(gdouble, size*sizeof(gdouble));
+  pixels  = g_new(guchar, size*bpp+bpp);
+  norm = g_new(gdouble, width*height*sizeof(gdouble));
+  for ( y = 0 ;  y < height; y++ )
+    {
+      for ( x = 0 ;  x < width; x++ )     
+        {
+           pos = (x+(y*width));
+           psrc=src+pos*bpp; 
+           norm[pos]=vector_norm(psrc, bpp);
+        }
+    }
+  for ( y = 0 ;  y < height; y++ )     
+    {
+       prog+=width;
+       for ( x = 0 ;  x < width; x++ )
+         {
+            pos1 = (x+(y*width))*bpp;
+            pixel_copy(pixels, src+pos1, bpp);
+            pixel_copy(dst+pos1, src+pos1, bpp);
+            if ( x >= radius && y >= radius &&  x < width-radius && y < height-radius )
+              {
+                /* Make sure Svm is ininialized to a sufficient large value */
+                count=0;
+                Svm[0] = 256 * size * 4;
+                Dvm[0] = sqrt(size*2+1);
+                for ( jv = y-radius ; jv <= y+radius ; jv++)         
+                  {        
+                    for ( jh = x-radius ; jh <= x+radius ; jh++)
+                      {  
+                         pos2 = (jh+(jv*width))*bpp;
+                         sum =dsum = asum = 0;
+                         for ( iv = y-radius ; iv <= y+radius ; iv++)
+                           {         
+                              for ( ih = x-radius ; ih <= x+radius ; ih++)
+                                {
+                                   pos3=(ih+(iv*width))*bpp;
+                                   dot=diff=ang=0;
+                                   diff  = vector_diff(src+pos2, src+pos3, bpp);
+                                   /* Vector dot product            */               
+                                   /* A . B = |A| * |B| * cos 0     */
+                                   /* cos 0 = ( A . B ) / (|A|*|B|) */ 
+                                   norm1 = norm[(jh+(jv*width))];/*vector_norm(src+pos2,bpp);*/
+                                   norm2 = norm[(ih+(iv*width))];/*vector_norm(src+pos3,bpp);*/                                   
+                                   dot   = vector_dotp(src+pos2, src+pos3, bpp);
+                                   ang=dot/(norm1*norm2);
+                                   /* Rounding error */
+                                   ang = ( ang > 1  ) ?  1 : ang;
+                                   ang = ( ang < -1 ) ? -1 : ang;
+                                   dsum+=diff;
+                                   asum+=fabs(acos(ang));
+                                }
+                            }
+                          sum=pow(dsum, pow1)*pow(asum, pow2);
+                          if ( sum <= Svm[0] )
+                            {                                           
+                               dvm=sqrt((jh-x)*(jh-x)+(jv-y)*(jv-y));
+                               count++;
+                               pos=count*bpp;
+                               pixel_copy(pixels+pos, src+pos2, bpp);
+                               Dvm[count] = dvm;
+                               Svm[count] = sum;
+                            }
+                      }                      
+                  }
+                while ( count >= 1 && Svm[count] <= Svm[0])
+                  {
+                     if ( Dvm[count-1] <= Dvm[0] )
+                       { 
+                          pos = count*bpp;
+                          pixel_copy(pixels, pixels+pos, bpp);
+                          Dvm[0] = Dvm[count];
+                          Svm[0] = Svm[count];
+                       }
+                     count--;
+                  }
+                if ( vector_diff(pixels, src+pos1, bpp) > threshold)
+                  pixel_copy(dst+pos1, pixels, bpp);
+              } 
+         }      
+       if ( !prevmode )
+         {                 
+           gimp_progress_update (prog/maxprog);
+         }  
+    }
+
+  /*
+   * OK, we're done.  Free all memory used...
+   */
+  g_free(norm);
+  g_free(Dvm);
+  g_free(Svm);
+  g_free(pixels);
+}
+
+/* * This Quickselect routine is based on the algorithm described in 
+* "Numerical recipes in C", Second Edition, 
+* Cambridge University Press, 1992, Section 8.5, ISBN 0-521-43108-5 
+* This code by Nicolas Devillard - 1998. Public domain. 
+*
+* modified to swap pointers: swap is done by comparing intensity value
+* for the pointer to RGB
+*/ 
+gint 
+quick_median_select(guchar **p, guchar *i, gint n) 
+{ 
+  gint low, high ; 
+  gint median; 
+  gint middle, ll, hh; 
+
+  low = 0 ; 
+  high = n-1 ; 
+  median = (low + high) / 2; 
+  for (;;) 
+    { 
+      if (high <= low) /* One element only */ 
+        return median; 
+      if (high == low + 1) 
+        { 
+          /* Two elements only */ 
+          if (i[low] > i[high])
+            { 
+               VALUE_SWAP(i[low], i[high]) ; 
+               POINTER_SWAP(p[low], p[high]) ; 
+            }
+          return median; 
+        } 
+      /* Find median of low, middle and high items; swap into position low */ 
+      middle = (low + high) / 2; 
+      if (i[middle] > i[high]) 
+        {
+           VALUE_SWAP(i[middle], i[high]) ; 
+           POINTER_SWAP(p[middle], p[high]) ; 
+        }
+      if (i[low] > i[high]) 
+        {
+           VALUE_SWAP(i[low], i[high]) ; 
+           POINTER_SWAP(p[low], p[high]) ; 
+        }
+      if (i[middle] > i[low]) 
+        {
+          VALUE_SWAP(i[middle], i[low]) ; 
+          POINTER_SWAP(p[middle], p[low]) ; 
+        }
+      /* Swap low item (now in position middle) into position (low+1) */       
+      VALUE_SWAP(i[middle], i[low+1]) ; 
+      POINTER_SWAP(p[middle], p[low+1])
+      /* Nibble from each end towards middle, swapping items when stuck */ 
+      ll = low + 1; 
+      hh = high; 
+      for (;;) 
+        { 
+           do ll++; 
+           while (i[low] > i[ll] ) ; 
+           do hh--; 
+           while (i[hh]  > i[low]) ; 
+           if (hh < ll) 
+              break; 
+           VALUE_SWAP(i[ll], i[hh]) ; 
+           POINTER_SWAP(p[ll], p[hh]) ; 
+        } 
+      /* Swap middle item (in position low) back into correct position */       
+      VALUE_SWAP(i[low], i[hh]) ; 
+      POINTER_SWAP(p[low], p[hh]) ;
+      /* Re-set active partition */ 
+      if (hh <= median) 
+        low = ll;
+      if (hh >= median) 
+        high = hh - 1; 
+    } 
+} 
+
+/*
+ * Desc:   Returns the intensity of a RGB pixel.
+ * Source: [FOLE90:589] 
+ */
+static guchar
+pixel_intensity(guchar *p, gint n)
+{  
+   if ( n!=3 )
+     return p[0];
+   return (guchar)(0.3*p[0] + 0.59*p[1] + 0.11*p[2]);
+}
+
+static gint row_tile_cache(gint width, gint radius, gint bpp)
+{
+  gint horizontal_tiles, vertical_tiles,tiles;
+  gint size;
+
+  horizontal_tiles =width/gimp_tile_width()+1;
+  vertical_tiles   =(2*radius+1)/gimp_tile_height();
+  vertical_tiles   = (vertical_tiles  <=1) ? 2 : 2*vertical_tiles+2;
+  tiles = horizontal_tiles * vertical_tiles;
+  size = (tiles * gimp_tile_width() * gimp_tile_height() * bpp)/1024;
+  return size+1;
+}
+        
+
+static void
+pixel_copy(guchar *a,guchar *b, gint n)
+  { 
+    for (;n>0;n--,a++,b++)
+      {
+        *a=*b;
+      }
+  }
+
+static gint
+vector_norm(guchar *a, gint n)       
+  {
+     gint q=0; 
+     for (;n>0;n--,a++) 
+       q+=(*(a) * *(a));
+     return sqrt(q);
+  }
+
+static gint
+vector_dotp(guchar *a,guchar *b, gint n)
+  {
+     gint d=0;
+     for (;n>0;n--,a++,b++)
+       d+=(*(a) * *(b));
+     return d;
+  }
+
+static gint
+vector_diff(guchar *a,guchar *b, gint n)
+  {
+     gint d=0;
+     for (;n>0;n--,a++,b++)
+       d+=(*(a) - *(b))*(*(a) - *(b));
+     return d;
+  }
+
