[gegl] median-blur: cleanup; optimize for different formats; add "exact" prop



commit 813499e7ef7e0c995c022b6e3b34644535fa3085
Author: Ell <ell_se yahoo com>
Date:   Fri Nov 17 16:09:23 2017 -0500

    median-blur: cleanup; optimize for different formats; add "exact" prop
    
    Some code cleanup, in particular, to remove assumptions about the
    number of components, so that we can use the op natively with more
    formats.
    
    Optimize the op for different input formats, in particular, Y *,
    so that we may eventually use it in GIMP to expand/contract
    selections, instead of gimp:grow and gimp:shrink.
    
    Add "exact" prop, which, when set, avoids input-value quantization
    (and, in particular, clipping), and produces exact results.  This
    comes at the cost of speed: performance is generally ~1.5x to ~3x
    worse.
    
    When "exact" is not set, quantize the input values into 256 bins,
    instead of 1024 bins, since the higher precision is probably not
    relevant to the user in this case.
    
    Update reference hash.

 operations/common/median-blur.c |  527 +++++++++++++++++++++++++++++++--------
 1 files changed, 429 insertions(+), 98 deletions(-)
---
diff --git a/operations/common/median-blur.c b/operations/common/median-blur.c
index 0bf41a0..162d9f7 100644
--- a/operations/common/median-blur.c
+++ b/operations/common/median-blur.c
@@ -48,6 +48,9 @@ property_double  (alpha_percentile, _("Alpha percentile"), 50)
   value_range (0, 100)
   description (_("Neighborhood alpha percentile"))
 
+property_boolean (exact, _("Exact result"), FALSE)
+  description (_("Avoid clipping and quantization errors (slower)"))
+
 #else
 
 #define GEGL_OP_AREA_FILTER
@@ -56,20 +59,44 @@ property_double  (alpha_percentile, _("Alpha percentile"), 50)
 
 #include "gegl-op.h"
 
-#define N_BINS  1024
+#define DEFAULT_N_BINS   256
+#define MAX_CHUNK_WIDTH  128
+#define MAX_CHUNK_HEIGHT 128
+
+#define SAFE_CLAMP(x, min, max) ((x) > (min) ? (x) < (max) ? (x) : (max) : (min))
+
+static gfloat        default_bin_values[DEFAULT_N_BINS];
+static gint          default_alpha_values[DEFAULT_N_BINS];
+static volatile gint default_values_initialized = FALSE;
+
+typedef struct
+{
+  gboolean  quantize;
+  gint     *neighborhood_outline;
+} UserData;
+
+typedef struct
+{
+  gfloat value;
+  gint   index;
+} InputValue;
 
 typedef struct
 {
-  gint bins[N_BINS];
-  gint last_median;
-  gint last_median_sum;
+  gint   *bins;
+  gfloat *bin_values;
+  gint    last_median;
+  gint    last_median_sum;
 } HistogramComponent;
 
 typedef struct
 {
-  HistogramComponent components[4];
-  gint               count;
-  gint               size;
+  HistogramComponent  components[4];
+  gint               *alpha_values;
+  gint                count;
+  gint                size;
+  gint                n_components;
+  gint                n_color_components;
 } Histogram;
 
 typedef enum
@@ -89,7 +116,7 @@ histogram_get_median (Histogram *hist,
   gint                i     = comp->last_median;
   gint                sum   = comp->last_median_sum;
 
-  if (component == 3)
+  if (component == hist->n_color_components)
     count = hist->size;
 
   if (count == 0)
@@ -111,22 +138,23 @@ histogram_get_median (Histogram *hist,
   comp->last_median     = i;
   comp->last_median_sum = sum;
 
-  return (gfloat) i / (gfloat) (N_BINS - 1);
+  return comp->bin_values[i];
 }
 
 static inline void
 histogram_modify_val (Histogram    *hist,
                       const gint32 *src,
-                      gboolean      has_alpha,
-                      gint          diff)
+                      gint          diff,
+                      gint          n_color_components,
+                      gboolean      has_alpha)
 {
   gint alpha = diff;
   gint c;
 
   if (has_alpha)
-    alpha *= src[3];
+    alpha *= hist->alpha_values[src[n_color_components]];
 
-  for (c = 0; c < 3; c++)
+  for (c = 0; c < n_color_components; c++)
     {
       HistogramComponent *comp = &hist->components[c];
       gint                bin  = src[c];
@@ -145,8 +173,8 @@ histogram_modify_val (Histogram    *hist,
 
   if (has_alpha)
     {
-      HistogramComponent *comp = &hist->components[3];
-      gint                bin  = src[3];
+      HistogramComponent *comp = &hist->components[n_color_components];
+      gint                bin  = src[n_color_components];
 
       comp->bins[bin] += diff;
 
@@ -159,30 +187,76 @@ histogram_modify_val (Histogram    *hist,
 static inline void
 histogram_modify_vals (Histogram    *hist,
                        const gint32 *src,
-                       gint          bpp,
                        gint          stride,
-                       gboolean      has_alpha,
                        gint          xmin,
                        gint          ymin,
                        gint          xmax,
                        gint          ymax,
                        gint          diff)
 {
+  gint     n_components       = hist->n_components;
+  gint     n_color_components = hist->n_color_components;
+  gboolean has_alpha          = n_color_components < n_components;
   gint x;
   gint y;
 
   if (xmin > xmax || ymin > ymax)
     return;
 
-  src += ymin * stride + xmin * bpp;
+  src += ymin * stride + xmin * n_components;
 
-  for (y = ymin; y <= ymax; y++, src += stride)
+  if (n_color_components == 3)
     {
-      const gint32 *pixel = src;
+      if (has_alpha)
+        {
+          for (y = ymin; y <= ymax; y++, src += stride)
+            {
+              const gint32 *pixel = src;
 
-      for (x = xmin; x <= xmax; x++, pixel += bpp)
+              for (x = xmin; x <= xmax; x++, pixel += n_components)
+                {
+                  histogram_modify_val (hist, pixel, diff, 3, TRUE);
+                }
+            }
+        }
+      else
         {
-          histogram_modify_val (hist, pixel, has_alpha, diff);
+          for (y = ymin; y <= ymax; y++, src += stride)
+            {
+              const gint32 *pixel = src;
+
+              for (x = xmin; x <= xmax; x++, pixel += n_components)
+                {
+                  histogram_modify_val (hist, pixel, diff, 3, FALSE);
+                }
+            }
+        }
+    }
+  else
+    {
+      if (has_alpha)
+        {
+          for (y = ymin; y <= ymax; y++, src += stride)
+            {
+              const gint32 *pixel = src;
+
+              for (x = xmin; x <= xmax; x++, pixel += n_components)
+                {
+                  histogram_modify_val (hist, pixel, diff, 1, TRUE);
+                }
+            }
+        }
+      else
+        {
+          for (y = ymin; y <= ymax; y++, src += stride)
+            {
+              const gint32 *pixel = src;
+
+              for (x = xmin; x <= xmax; x++, pixel += n_components)
+                {
+                  histogram_modify_val (hist, pixel, diff, 1, FALSE);
+                }
+            }
         }
     }
 }
@@ -190,9 +264,7 @@ histogram_modify_vals (Histogram    *hist,
 static inline void
 histogram_update (Histogram                  *hist,
                   const gint32               *src,
-                  gint                        bpp,
                   gint                        stride,
-                  gboolean                    has_alpha,
                   GeglMedianBlurNeighborhood  neighborhood,
                   gint                        radius,
                   const gint                 *neighborhood_outline,
@@ -206,36 +278,36 @@ histogram_update (Histogram                  *hist,
       switch (dir)
         {
           case LEFT_TO_RIGHT:
-            histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+            histogram_modify_vals (hist, src, stride,
                                    -radius - 1, -radius,
                                    -radius - 1, +radius,
                                    -1);
 
-            histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+            histogram_modify_vals (hist, src, stride,
                                    +radius, -radius,
                                    +radius, +radius,
                                    +1);
             break;
 
           case RIGHT_TO_LEFT:
-            histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+            histogram_modify_vals (hist, src, stride,
                                    +radius + 1, -radius,
                                    +radius + 1, +radius,
                                    -1);
 
-            histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+            histogram_modify_vals (hist, src, stride,
                                    -radius, -radius,
                                    -radius, +radius,
                                    +1);
             break;
 
           case TOP_TO_BOTTOM:
-            histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+            histogram_modify_vals (hist, src, stride,
                                    -radius, -radius - 1,
                                    +radius, -radius - 1,
                                    -1);
 
-            histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+            histogram_modify_vals (hist, src, stride,
                                    -radius, +radius,
                                    +radius, +radius,
                                    +1);
@@ -249,31 +321,31 @@ histogram_update (Histogram                  *hist,
           case LEFT_TO_RIGHT:
             for (i = 0; i < radius; i++)
               {
-                histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+                histogram_modify_vals (hist, src, stride,
                                        -i - 1, -neighborhood_outline[i],
                                        -i - 1, -neighborhood_outline[i + 1] - 1,
                                        -1);
-                histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+                histogram_modify_vals (hist, src, stride,
                                        -i - 1, +neighborhood_outline[i + 1] + 1,
                                        -i - 1, +neighborhood_outline[i],
                                        -1);
 
-                histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+                histogram_modify_vals (hist, src, stride,
                                        +i, -neighborhood_outline[i],
                                        +i, -neighborhood_outline[i + 1] - 1,
                                        +1);
-                histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+                histogram_modify_vals (hist, src, stride,
                                        +i, +neighborhood_outline[i + 1] + 1,
                                        +i, +neighborhood_outline[i],
                                        +1);
               }
 
-            histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+            histogram_modify_vals (hist, src, stride,
                                    -i - 1, -neighborhood_outline[i],
                                    -i - 1, +neighborhood_outline[i],
                                    -1);
 
-            histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+            histogram_modify_vals (hist, src, stride,
                                    +i, -neighborhood_outline[i],
                                    +i, +neighborhood_outline[i],
                                    +1);
@@ -283,31 +355,31 @@ histogram_update (Histogram                  *hist,
           case RIGHT_TO_LEFT:
             for (i = 0; i < radius; i++)
               {
-                histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+                histogram_modify_vals (hist, src, stride,
                                        +i + 1, -neighborhood_outline[i],
                                        +i + 1, -neighborhood_outline[i + 1] - 1,
                                        -1);
-                histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+                histogram_modify_vals (hist, src, stride,
                                        +i + 1, +neighborhood_outline[i + 1] + 1,
                                        +i + 1, +neighborhood_outline[i],
                                        -1);
 
-                histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+                histogram_modify_vals (hist, src, stride,
                                        -i, -neighborhood_outline[i],
                                        -i, -neighborhood_outline[i + 1] - 1,
                                        +1);
-                histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+                histogram_modify_vals (hist, src, stride,
                                        -i, +neighborhood_outline[i + 1] + 1,
                                        -i, +neighborhood_outline[i],
                                        +1);
               }
 
-            histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+            histogram_modify_vals (hist, src, stride,
                                    +i + 1, -neighborhood_outline[i],
                                    +i + 1, +neighborhood_outline[i],
                                    -1);
 
-            histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+            histogram_modify_vals (hist, src, stride,
                                    -i, -neighborhood_outline[i],
                                    -i, +neighborhood_outline[i],
                                    +1);
@@ -317,31 +389,31 @@ histogram_update (Histogram                  *hist,
           case TOP_TO_BOTTOM:
             for (i = 0; i < radius; i++)
               {
-                histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+                histogram_modify_vals (hist, src, stride,
                                        -neighborhood_outline[i],         -i - 1,
                                        -neighborhood_outline[i + 1] - 1, -i - 1,
                                        -1);
-                histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+                histogram_modify_vals (hist, src, stride,
                                        +neighborhood_outline[i + 1] + 1, -i - 1,
                                        +neighborhood_outline[i],         -i - 1,
                                        -1);
 
-                histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+                histogram_modify_vals (hist, src, stride,
                                        -neighborhood_outline[i],         +i,
                                        -neighborhood_outline[i + 1] - 1, +i,
                                        +1);
-                histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+                histogram_modify_vals (hist, src, stride,
                                        +neighborhood_outline[i + 1] + 1, +i,
                                        +neighborhood_outline[i],         +i,
                                        +1);
               }
 
-            histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+            histogram_modify_vals (hist, src, stride,
                                    -neighborhood_outline[i], -i - 1,
                                    +neighborhood_outline[i], -i - 1,
                                    -1);
 
-            histogram_modify_vals (hist, src, bpp, stride, has_alpha,
+            histogram_modify_vals (hist, src, stride,
                                    -neighborhood_outline[i], +i,
                                    +neighborhood_outline[i], +i,
                                    +1);
@@ -380,31 +452,151 @@ init_neighborhood_outline (GeglMedianBlurNeighborhood  neighborhood,
 }
 
 static void
-convert_values_to_bins (gint32   *src,
-                        gint      bpp,
-                        gboolean  has_alpha,
-                        gint      count)
+sort_input_values (InputValue **values,
+                   InputValue **scratch,
+                   gint         n_values)
 {
-  gint components = 3;
-  gint c;
+  const InputValue *in     = *values;
+  InputValue       *out    = *scratch;
+  gint              size;
 
-  if (has_alpha)
-    components++;
+  for (size = 1; size < n_values; size *= 2)
+    {
+      InputValue *temp;
+      gint        i;
+
+      for (i = 0; i < n_values; )
+        {
+          gint l     = i;
+          gint l_end = MIN (l + size, n_values);
+          gint r     = l_end;
+          gint r_end = MIN (r + size, n_values);
+
+          while (l < l_end && r < r_end)
+            {
+              if (in[r].value < in[l].value)
+                out[i] = in[r++];
+              else
+                out[i] = in[l++];
+
+              i++;
+            }
+
+          memcpy (&out[i], &in[l], (l_end - l) * sizeof (InputValue));
+          i += l_end - l;
+
+          memcpy (&out[i], &in[r], (r_end - r) * sizeof (InputValue));
+          i += r_end - r;
+        }
+
+      temp = (InputValue *) in;
+      in   = out;
+      out  = temp;
+    }
+
+  *values  = (InputValue *) in;
+  *scratch = out;
+}
+
+static void
+convert_values_to_bins (Histogram *hist,
+                        gint32    *src,
+                        gint       n_pixels,
+                        gboolean   quantize)
+{
+  gint     n_components       = hist->n_components;
+  gint     n_color_components = hist->n_color_components;
+  gboolean has_alpha          = n_color_components < n_components;
+  gint     i;
+  gint     c;
+
+  if (quantize)
+    {
+      for (c = 0; c < n_components; c++)
+        {
+          hist->components[c].bins       = g_new0 (gint, DEFAULT_N_BINS);
+          hist->components[c].bin_values = default_bin_values;
+        }
+
+      while (n_pixels--)
+        {
+          for (c = 0; c < n_components; c++)
+            {
+              gfloat value = ((gfloat *) src)[c];
+              gint   bin;
 
-  while (count--)
+              bin = floorf (SAFE_CLAMP (value, 0.0f, 1.0f) * (DEFAULT_N_BINS - 1) + 0.5f);
+
+              src[c] = bin;
+            }
+
+          src += n_components;
+        }
+
+      hist->alpha_values = default_alpha_values;
+    }
+  else
     {
-      for (c = 0; c < components; c++)
+      InputValue *values       = g_new (InputValue, n_pixels);
+      InputValue *scratch      = g_new (InputValue, n_pixels);
+      gint       *alpha_values = NULL;
+
+      if (has_alpha)
         {
-          gfloat value = ((gfloat *) src)[c];
-          gint   bin;
+          alpha_values = g_new (gint, n_pixels);
 
-          bin = (gint) (value * (N_BINS - 1) + 0.5f);
-          bin = CLAMP (bin, 0, N_BINS - 1);
+          hist->alpha_values = alpha_values;
+        }
 
-          src[c] = bin;
+      for (i = 0; i < n_pixels; i++)
+        {
+          values[i].value = ((gfloat *) src)[i * n_components];
+          values[i].index = i;
         }
 
-      src += bpp;
+      for (c = 0; c < n_components; c++)
+        {
+          gint    bin        = 0;
+          gfloat  prev_value = values[0].value;
+          gfloat *bin_values;
+
+          bin_values    = g_new (gfloat, n_pixels);
+          bin_values[0] = prev_value;
+          if (c == n_color_components)
+            {
+              alpha_values[0] = floorf (SAFE_CLAMP (prev_value, 0.0f, 1.0f) *
+                                        (gfloat) (1 << 10) + 0.5f);
+            }
+
+          sort_input_values (&values, &scratch, n_pixels);
+
+          for (i = 0; i < n_pixels; i++)
+            {
+              gint32 *p = &src[values[i].index * n_components + c];
+
+              if (values[i].value != prev_value)
+                {
+                  bin++;
+                  prev_value      = values[i].value;
+                  bin_values[bin] = prev_value;
+                  if (c == n_color_components)
+                    {
+                      alpha_values[bin] = floorf (SAFE_CLAMP (prev_value, 0.0f, 1.0f) *
+                                                  (gfloat) (1 << 10) + 0.5f);
+                    }
+                }
+
+              *p = bin;
+              if (c < n_components - 1)
+                values[i].value = ((gfloat *) p)[1];
+            }
+
+          hist->components[c].bins       = g_new0 (gint, bin + 1);
+          hist->components[c].bin_values = bin_values;
+        }
+
+      g_free (scratch);
+      g_free (values);
     }
 }
 
@@ -414,22 +606,108 @@ prepare (GeglOperation *operation)
   GeglOperationAreaFilter *area      = GEGL_OPERATION_AREA_FILTER (operation);
   GeglProperties          *o         = GEGL_PROPERTIES (operation);
   const Babl              *in_format = gegl_operation_get_source_format (operation, "input");
-  const Babl              *format    = babl_format ("R'G'B' float");
+  const Babl              *format    = NULL;
+  gboolean                 exact     = o->exact;
+  UserData                *data;
 
   area->left   =
   area->right  =
   area->top    =
   area->bottom = o->radius;
 
-  o->user_data = g_renew (gint, o->user_data, o->radius + 1);
-  init_neighborhood_outline (o->neighborhood, o->radius, o->user_data);
+  if (! o->user_data)
+    o->user_data = g_slice_new0 (UserData);
+
+  data                       = o->user_data;
+  data->quantize             = ! exact;
+  data->neighborhood_outline = g_renew (gint, data->neighborhood_outline,
+                                        o->radius + 1);
+  init_neighborhood_outline (o->neighborhood, o->radius,
+                             data->neighborhood_outline);
 
   if (in_format)
     {
-      if (babl_format_has_alpha (in_format))
+      const Babl *model = babl_format_get_model (in_format);
+
+      if (exact)
+        {
+          if (model == babl_model ("Y"))
+            format = babl_format ("Y float");
+          else if (model == babl_model ("Y'"))
+            format = babl_format ("Y' float");
+          else if (model == babl_model ("YA") || model == babl_model ("YaA"))
+            format = babl_format ("YA float");
+          else if (model == babl_model ("Y'A") || model == babl_model ("Y'aA"))
+            format = babl_format ("Y'A float");
+          else if (model == babl_model ("RGB"))
+            format = babl_format ("RGB float");
+          else if (model == babl_model ("R'G'B'"))
+            format = babl_format ("R'G'B' float");
+          else if (model == babl_model ("RGBA") || model == babl_model ("RaGaBaA"))
+            format = babl_format ("RGBA float");
+          else if (model == babl_model ("R'G'B'A") || model == babl_model ("R'aG'aB'aA"))
+            format = babl_format ("R'G'B'A float");
+
+          if (format)
+            {
+              gint n_components = babl_format_get_n_components (in_format);
+              gint i;
+
+              data->quantize = TRUE;
+
+              for (i = 0; i < n_components; i++)
+                {
+                  if (babl_format_get_type (in_format, i) != babl_type ("u8"))
+                    {
+                      data->quantize = FALSE;
+                      break;
+                    }
+                }
+            }
+        }
+      else
+        {
+          if (model == babl_model ("Y") || model == babl_model ("Y'"))
+            format = babl_format ("Y' float");
+          else if (model == babl_model ("YA")  || model == babl_model ("YaA") ||
+                   model == babl_model ("Y'A") || model == babl_model ("Y'aA"))
+            format = babl_format ("Y'A float");
+          else if (model == babl_model ("RGB") || model == babl_model ("R'G'B'"))
+            format = babl_format ("R'G'B' float");
+          else if (model == babl_model ("RGBA")    || model == babl_model ("RaGaBaA") ||
+                   model == babl_model ("R'G'B'A") || model == babl_model ("R'aG'aB'aA"))
+            format = babl_format ("R'G'B'A float");
+        }
+
+      if (format == NULL)
+        {
+          if (babl_format_has_alpha (in_format))
+            format = babl_format ("R'G'B'A float");
+          else
+            format = babl_format ("R'G'B' float");
+        }
+    }
+  else
+    {
+      if (exact)
+        format = babl_format ("RGBA float");
+      else
         format = babl_format ("R'G'B'A float");
     }
 
+  if (data->quantize && ! g_atomic_int_get (&default_values_initialized))
+    {
+      gint i;
+
+      for (i = 0; i < DEFAULT_N_BINS; i++)
+        {
+          default_bin_values[i]   = (gfloat) i / (gfloat) (DEFAULT_N_BINS - 1);
+          default_alpha_values[i] = i;
+        }
+
+      g_atomic_int_set (&default_values_initialized, TRUE);
+    }
+
   gegl_operation_set_format (operation, "input", format);
   gegl_operation_set_format (operation, "output", format);
 }
@@ -456,14 +734,16 @@ process (GeglOperation       *operation,
          const GeglRectangle *roi,
          gint                 level)
 {
-  GeglProperties *o = GEGL_PROPERTIES (operation);
+  GeglProperties *o    = GEGL_PROPERTIES (operation);
+  UserData       *data = o->user_data;
 
   gdouble         percentile           = o->percentile       / 100.0;
   gdouble         alpha_percentile     = o->alpha_percentile / 100.0;
-  const gint     *neighborhood_outline = o->user_data;
+  const gint     *neighborhood_outline = data->neighborhood_outline;
 
   const Babl     *format               = gegl_operation_get_format (operation, "input");
   gint            n_components         = babl_format_get_n_components (format);
+  gint            n_color_components   = n_components;
   gboolean        has_alpha            = babl_format_has_alpha (format);
 
   G_STATIC_ASSERT (sizeof (gint32) == sizeof (gfloat));
@@ -472,7 +752,8 @@ process (GeglOperation       *operation,
   GeglRectangle   src_rect;
   gint            src_stride;
   gint            dst_stride;
-  gint            n_pixels;
+  gint            n_src_pixels;
+  gint            n_dst_pixels;
 
   Histogram      *hist;
 
@@ -484,20 +765,55 @@ process (GeglOperation       *operation,
   gint            i;
   gint            c;
 
-  src_rect   = gegl_operation_get_required_for_output (operation, "input", roi);
-  src_stride = src_rect.width * n_components;
-  dst_stride = roi->width * n_components;
-  n_pixels   = roi->width * roi->height;
-  dst_buf = g_new0 (gfloat, n_pixels                         * n_components);
-  src_buf = g_new0 (gint32, src_rect.width * src_rect.height * n_components);
+  if (! data->quantize &&
+      (roi->width > MAX_CHUNK_WIDTH || roi->height > MAX_CHUNK_HEIGHT))
+    {
+      gint n_x = (roi->width  + MAX_CHUNK_WIDTH  - 1) / MAX_CHUNK_WIDTH;
+      gint n_y = (roi->height + MAX_CHUNK_HEIGHT - 1) / MAX_CHUNK_HEIGHT;
+      gint x;
+      gint y;
 
-  gegl_buffer_get (input, &src_rect, 1.0, format, src_buf,
-                   GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP);
-  convert_values_to_bins (src_buf, n_components, has_alpha,
-                          src_rect.width * src_rect.height);
+      for (y = 0; y < n_y; y++)
+        {
+          for (x = 0; x < n_x; x++)
+            {
+              GeglRectangle chunk;
+
+              chunk.x      = roi->x + roi->width  * x       / n_x;
+              chunk.y      = roi->y + roi->height * y       / n_y;
+              chunk.width  = roi->x + roi->width  * (x + 1) / n_x - chunk.x;
+              chunk.height = roi->y + roi->height * (y + 1) / n_y - chunk.y;
+
+              if (! process (operation, input, output, &chunk, level))
+                return FALSE;
+            }
+        }
+
+      return TRUE;
+    }
+
+  if (has_alpha)
+    n_color_components--;
+
+  g_return_val_if_fail (n_color_components == 1 || n_color_components == 3, FALSE);
 
   hist = g_slice_new0 (Histogram);
 
+  hist->n_components       = n_components;
+  hist->n_color_components = n_color_components;
+
+  src_rect     = gegl_operation_get_required_for_output (operation, "input", roi);
+  src_stride   = src_rect.width * n_components;
+  dst_stride   = roi->width * n_components;
+  n_src_pixels = src_rect.width * src_rect.height;
+  n_dst_pixels = roi->width * roi->height;
+  src_buf = g_new (gint32, n_src_pixels * n_components);
+  dst_buf = g_new (gfloat, n_dst_pixels * n_components);
+
+  gegl_buffer_get (input, &src_rect, 1.0, format, src_buf,
+                   GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP);
+  convert_values_to_bins (hist, src_buf, n_src_pixels, data->quantize);
+
   src = src_buf + o->radius * (src_rect.width + 1) * n_components;
   dst = dst_buf;
 
@@ -505,7 +821,7 @@ process (GeglOperation       *operation,
 
   for (i = -o->radius; i <= o->radius; i++)
     {
-      histogram_modify_vals (hist, src, n_components, src_stride, has_alpha,
+      histogram_modify_vals (hist, src, src_stride,
                              i, -neighborhood_outline[abs (i)],
                              i, +neighborhood_outline[abs (i)],
                              +1);
@@ -513,19 +829,18 @@ process (GeglOperation       *operation,
       hist->size += 2 * neighborhood_outline[abs (i)] + 1;
     }
 
-  for (c = 0; c < 3; c++)
+  for (c = 0; c < n_color_components; c++)
     dst[c] = histogram_get_median (hist, c, percentile);
-
   if (has_alpha)
-    dst[3] = histogram_get_median (hist, 3, alpha_percentile);
+    dst[c] = histogram_get_median (hist, c, alpha_percentile);
 
   dst_x = 0;
   dst_y = 0;
 
-  n_pixels--;
+  n_dst_pixels--;
   dir = LEFT_TO_RIGHT;
 
-  while (n_pixels--)
+  while (n_dst_pixels--)
     {
       /* move the src coords based on current direction and positions */
       if (dir == LEFT_TO_RIGHT)
@@ -578,19 +893,29 @@ process (GeglOperation       *operation,
             }
         }
 
-      histogram_update (hist, src, n_components, src_stride, has_alpha,
+      histogram_update (hist, src, src_stride,
                         o->neighborhood, o->radius, neighborhood_outline,
                         dir);
 
-      for (c = 0; c < 3; c++)
+      for (c = 0; c < n_color_components; c++)
         dst[c] = histogram_get_median (hist, c, percentile);
-
       if (has_alpha)
-        dst[3] = histogram_get_median (hist, 3, alpha_percentile);
+        dst[c] = histogram_get_median (hist, c, alpha_percentile);
     }
 
   gegl_buffer_set (output, roi, 0, format, dst_buf, GEGL_AUTO_ROWSTRIDE);
 
+  for (c = 0; c < n_components; c++)
+    {
+      g_free (hist->components[c].bins);
+
+      if (! data->quantize)
+        g_free (hist->components[c].bin_values);
+    }
+
+  if (! data->quantize && has_alpha)
+    g_free (hist->alpha_values);
+
   g_slice_free (Histogram, hist);
   g_free (dst_buf);
   g_free (src_buf);
@@ -604,7 +929,13 @@ finalize (GObject *object)
   GeglOperation  *op = (void*) object;
   GeglProperties *o  = GEGL_PROPERTIES (op);
 
-  g_clear_pointer (&o->user_data, g_free);
+  if (o->user_data)
+    {
+      UserData *data = o->user_data;
+
+      g_free (data->neighborhood_outline);
+      g_slice_free (UserData, data);
+    }
 
   G_OBJECT_CLASS (gegl_op_parent_class)->finalize (object);
 }
@@ -626,12 +957,12 @@ gegl_op_class_init (GeglOpClass *klass)
   operation_class->get_bounding_box = get_bounding_box;
 
   gegl_operation_class_set_keys (operation_class,
-    "name",        "gegl:median-blur",
-    "title",       _("Median Blur"),
-    "categories",  "blur",
-    "reference-hash", "bd34e0f3b290d67713d6ab79492d9f1e",
-    "description", _("Blur resulting from computing the median "
-                     "color in the neighborhood of each pixel."),
+    "name",           "gegl:median-blur",
+    "title",          _("Median Blur"),
+    "categories",     "blur",
+    "reference-hash", "1865918d2f3b95690359534bbd58b513",
+    "description",    _("Blur resulting from computing the median "
+                        "color in the neighborhood of each pixel."),
     NULL);
 }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]