[gegl] operations: add a way to impact each component individually for stretch-contrast



commit 80f0f4f7eb33d05b66bbffd6d10ca7c8db2357cb
Author: Téo Mazars <teomazars gmail com>
Date:   Fri Nov 15 18:34:07 2013 +0100

    operations: add a way to impact each component individually for stretch-contrast
    
    for GIMP's compatibility sake

 opencl/stretch-contrast.cl           |   63 +++++-----
 opencl/stretch-contrast.cl.h         |   63 +++++-----
 operations/common/stretch-contrast.c |  208 ++++++++++++++++++++++------------
 3 files changed, 202 insertions(+), 132 deletions(-)
---
diff --git a/opencl/stretch-contrast.cl b/opencl/stretch-contrast.cl
index 83f7ff9..cda7b6d 100644
--- a/opencl/stretch-contrast.cl
+++ b/opencl/stretch-contrast.cl
@@ -17,21 +17,21 @@
  */
 
 
-__kernel void init_stretch (__global float *out_min,
-                            __global float *out_max)
+__kernel void init_stretch (__global float4 *out_min,
+                            __global float4 *out_max)
 {
   int gid = get_global_id (0);
 
-  out_min[gid] =  FLT_MAX;
-  out_max[gid] = -FLT_MAX;
+  out_min[gid] = (float4)( FLT_MAX);
+  out_max[gid] = (float4)(-FLT_MAX);
 }
 
 __kernel void two_stages_local_min_max_reduce (__global const float4 *in,
-                                               __global       float  *out_min,
-                                               __global       float  *out_max,
-                                               __local        float  *aux_min,
-                                               __local        float  *aux_max,
-                                                              int    n_pixels)
+                                               __global       float4 *out_min,
+                                               __global       float4 *out_max,
+                                               __local        float4 *aux_min,
+                                               __local        float4 *aux_max,
+                                                              int     n_pixels)
 {
   int    gid   = get_global_id(0);
   int    gsize = get_global_size(0);
@@ -40,20 +40,20 @@ __kernel void two_stages_local_min_max_reduce (__global const float4 *in,
   float4 min_v = (float4)( FLT_MAX);
   float4 max_v = (float4)(-FLT_MAX);
   float4 in_v;
-  float  aux0, aux1;
+  float4 aux0, aux1;
   int    it;
 
   /* Loop sequentially over chunks of input vector */
   for (it = gid; it < n_pixels; it += gsize)
     {
       in_v  =  in[it];
-      min_v =  fmin (min_v, in_v);
-      max_v =  fmax (max_v, in_v);
+      min_v =  min (min_v, in_v);
+      max_v =  max (max_v, in_v);
     }
 
   /* Perform parallel reduction */
-  aux_min[lid] = min (min (min_v.x, min_v.y), min_v.z);
-  aux_max[lid] = max (max (max_v.x, max_v.y), max_v.z);
+  aux_min[lid] = min_v;
+  aux_max[lid] = max_v;
 
   barrier (CLK_LOCAL_MEM_FENCE);
 
@@ -63,11 +63,11 @@ __kernel void two_stages_local_min_max_reduce (__global const float4 *in,
         {
           aux0         = aux_min[lid + it];
           aux1         = aux_min[lid];
-          aux_min[lid] = fmin (aux0, aux1);
+          aux_min[lid] = min (aux0, aux1);
 
           aux0         = aux_max[lid + it];
           aux1         = aux_max[lid];
-          aux_max[lid] = fmax (aux0, aux1);
+          aux_max[lid] = max (aux0, aux1);
         }
       barrier (CLK_LOCAL_MEM_FENCE);
   }
@@ -85,21 +85,21 @@ __kernel void two_stages_local_min_max_reduce (__global const float4 *in,
       int nb_wg = gsize / lsize;
       for (it = nb_wg; it < lsize; it++)
         {
-          out_min[it] =  FLT_MAX;
-          out_max[it] = -FLT_MAX;
+          out_min[it] = (float4)( FLT_MAX);
+          out_max[it] = (float4)(-FLT_MAX);
         }
     }
 }
 
-__kernel void global_min_max_reduce (__global float *in_min,
-                                     __global float *in_max,
-                                     __global float *out_min_max)
+__kernel void global_min_max_reduce (__global float4 *in_min,
+                                     __global float4 *in_max,
+                                     __global float4 *out_min_max)
 {
-  int   gid   = get_global_id(0);
-  int   lid   = get_local_id(0);
-  int   lsize = get_local_size(0);
-  float aux0, aux1;
-  int   it;
+  int    gid   = get_global_id(0);
+  int    lid   = get_local_id(0);
+  int    lsize = get_local_size(0);
+  float4 aux0, aux1;
+  int    it;
 
   /* Perform parallel reduction */
   for (it = lsize / 2; it > 0; it >>= 1)
@@ -108,11 +108,11 @@ __kernel void global_min_max_reduce (__global float *in_min,
         {
           aux0        = in_min[lid + it];
           aux1        = in_min[lid];
-          in_min[gid] = fmin (aux0, aux1);
+          in_min[gid] = min (aux0, aux1);
 
           aux0        = in_max[lid + it];
           aux1        = in_max[lid];
-          in_max[gid] = fmax (aux0, aux1);
+          in_max[gid] = max (aux0, aux1);
         }
       barrier (CLK_GLOBAL_MEM_FENCE);
   }
@@ -125,12 +125,13 @@ __kernel void global_min_max_reduce (__global float *in_min,
 
 __kernel void cl_stretch_contrast (__global const float4 *in,
                                    __global       float4 *out,
-                                                  float   min,
-                                                  float   diff)
+                                                  float4  min,
+                                                  float4  diff)
 {
   int    gid  = get_global_id(0);
   float4 in_v = in[gid];
 
-  in_v.xyz = (in_v.xyz - min) / diff;
+  in_v = (in_v - min) / diff;
+
   out[gid] = in_v;
 }
diff --git a/opencl/stretch-contrast.cl.h b/opencl/stretch-contrast.cl.h
index c4bd39e..954ad1d 100644
--- a/opencl/stretch-contrast.cl.h
+++ b/opencl/stretch-contrast.cl.h
@@ -18,21 +18,21 @@ static const char* stretch_contrast_cl_source =
 " */                                                                           \n"
 "                                                                              \n"
 "                                                                              \n"
-"__kernel void init_stretch (__global float *out_min,                          \n"
-"                            __global float *out_max)                          \n"
+"__kernel void init_stretch (__global float4 *out_min,                         \n"
+"                            __global float4 *out_max)                         \n"
 "{                                                                             \n"
 "  int gid = get_global_id (0);                                                \n"
 "                                                                              \n"
-"  out_min[gid] =  FLT_MAX;                                                    \n"
-"  out_max[gid] = -FLT_MAX;                                                    \n"
+"  out_min[gid] = (float4)( FLT_MAX);                                          \n"
+"  out_max[gid] = (float4)(-FLT_MAX);                                          \n"
 "}                                                                             \n"
 "                                                                              \n"
 "__kernel void two_stages_local_min_max_reduce (__global const float4 *in,     \n"
-"                                               __global       float  *out_min,\n"
-"                                               __global       float  *out_max,\n"
-"                                               __local        float  *aux_min,\n"
-"                                               __local        float  *aux_max,\n"
-"                                                              int    n_pixels)\n"
+"                                               __global       float4 *out_min,\n"
+"                                               __global       float4 *out_max,\n"
+"                                               __local        float4 *aux_min,\n"
+"                                               __local        float4 *aux_max,\n"
+"                                                              int     n_pixels)\n"
 "{                                                                             \n"
 "  int    gid   = get_global_id(0);                                            \n"
 "  int    gsize = get_global_size(0);                                          \n"
@@ -41,20 +41,20 @@ static const char* stretch_contrast_cl_source =
 "  float4 min_v = (float4)( FLT_MAX);                                          \n"
 "  float4 max_v = (float4)(-FLT_MAX);                                          \n"
 "  float4 in_v;                                                                \n"
-"  float  aux0, aux1;                                                          \n"
+"  float4 aux0, aux1;                                                          \n"
 "  int    it;                                                                  \n"
 "                                                                              \n"
 "  /* Loop sequentially over chunks of input vector */                         \n"
 "  for (it = gid; it < n_pixels; it += gsize)                                  \n"
 "    {                                                                         \n"
 "      in_v  =  in[it];                                                        \n"
-"      min_v =  fmin (min_v, in_v);                                            \n"
-"      max_v =  fmax (max_v, in_v);                                            \n"
+"      min_v =  min (min_v, in_v);                                             \n"
+"      max_v =  max (max_v, in_v);                                             \n"
 "    }                                                                         \n"
 "                                                                              \n"
 "  /* Perform parallel reduction */                                            \n"
-"  aux_min[lid] = min (min (min_v.x, min_v.y), min_v.z);                       \n"
-"  aux_max[lid] = max (max (max_v.x, max_v.y), max_v.z);                       \n"
+"  aux_min[lid] = min_v;                                                       \n"
+"  aux_max[lid] = max_v;                                                       \n"
 "                                                                              \n"
 "  barrier (CLK_LOCAL_MEM_FENCE);                                              \n"
 "                                                                              \n"
@@ -64,11 +64,11 @@ static const char* stretch_contrast_cl_source =
 "        {                                                                     \n"
 "          aux0         = aux_min[lid + it];                                   \n"
 "          aux1         = aux_min[lid];                                        \n"
-"          aux_min[lid] = fmin (aux0, aux1);                                   \n"
+"          aux_min[lid] = min (aux0, aux1);                                    \n"
 "                                                                              \n"
 "          aux0         = aux_max[lid + it];                                   \n"
 "          aux1         = aux_max[lid];                                        \n"
-"          aux_max[lid] = fmax (aux0, aux1);                                   \n"
+"          aux_max[lid] = max (aux0, aux1);                                    \n"
 "        }                                                                     \n"
 "      barrier (CLK_LOCAL_MEM_FENCE);                                          \n"
 "  }                                                                           \n"
@@ -86,21 +86,21 @@ static const char* stretch_contrast_cl_source =
 "      int nb_wg = gsize / lsize;                                              \n"
 "      for (it = nb_wg; it < lsize; it++)                                      \n"
 "        {                                                                     \n"
-"          out_min[it] =  FLT_MAX;                                             \n"
-"          out_max[it] = -FLT_MAX;                                             \n"
+"          out_min[it] = (float4)( FLT_MAX);                                   \n"
+"          out_max[it] = (float4)(-FLT_MAX);                                   \n"
 "        }                                                                     \n"
 "    }                                                                         \n"
 "}                                                                             \n"
 "                                                                              \n"
-"__kernel void global_min_max_reduce (__global float *in_min,                  \n"
-"                                     __global float *in_max,                  \n"
-"                                     __global float *out_min_max)             \n"
+"__kernel void global_min_max_reduce (__global float4 *in_min,                 \n"
+"                                     __global float4 *in_max,                 \n"
+"                                     __global float4 *out_min_max)            \n"
 "{                                                                             \n"
-"  int   gid   = get_global_id(0);                                             \n"
-"  int   lid   = get_local_id(0);                                              \n"
-"  int   lsize = get_local_size(0);                                            \n"
-"  float aux0, aux1;                                                           \n"
-"  int   it;                                                                   \n"
+"  int    gid   = get_global_id(0);                                            \n"
+"  int    lid   = get_local_id(0);                                             \n"
+"  int    lsize = get_local_size(0);                                           \n"
+"  float4 aux0, aux1;                                                          \n"
+"  int    it;                                                                  \n"
 "                                                                              \n"
 "  /* Perform parallel reduction */                                            \n"
 "  for (it = lsize / 2; it > 0; it >>= 1)                                      \n"
@@ -109,11 +109,11 @@ static const char* stretch_contrast_cl_source =
 "        {                                                                     \n"
 "          aux0        = in_min[lid + it];                                     \n"
 "          aux1        = in_min[lid];                                          \n"
-"          in_min[gid] = fmin (aux0, aux1);                                    \n"
+"          in_min[gid] = min (aux0, aux1);                                     \n"
 "                                                                              \n"
 "          aux0        = in_max[lid + it];                                     \n"
 "          aux1        = in_max[lid];                                          \n"
-"          in_max[gid] = fmax (aux0, aux1);                                    \n"
+"          in_max[gid] = max (aux0, aux1);                                     \n"
 "        }                                                                     \n"
 "      barrier (CLK_GLOBAL_MEM_FENCE);                                         \n"
 "  }                                                                           \n"
@@ -126,13 +126,14 @@ static const char* stretch_contrast_cl_source =
 "                                                                              \n"
 "__kernel void cl_stretch_contrast (__global const float4 *in,                 \n"
 "                                   __global       float4 *out,                \n"
-"                                                  float   min,                \n"
-"                                                  float   diff)               \n"
+"                                                  float4  min,                \n"
+"                                                  float4  diff)               \n"
 "{                                                                             \n"
 "  int    gid  = get_global_id(0);                                             \n"
 "  float4 in_v = in[gid];                                                      \n"
 "                                                                              \n"
-"  in_v.xyz = (in_v.xyz - min) / diff;                                         \n"
+"  in_v = (in_v - min) / diff;                                                 \n"
+"                                                                              \n"
 "  out[gid] = in_v;                                                            \n"
 "}                                                                             \n"
 ;
diff --git a/operations/common/stretch-contrast.c b/operations/common/stretch-contrast.c
index f68f78f..179cc96 100644
--- a/operations/common/stretch-contrast.c
+++ b/operations/common/stretch-contrast.c
@@ -19,48 +19,71 @@
 #include "config.h"
 #include <glib/gi18n-lib.h>
 
-
 #ifdef GEGL_CHANT_PROPERTIES
 
+gegl_chant_boolean (keep_colors, _("Keep colors"), TRUE,
+                    _("Impact each channel with the same amount"))
+
 #else
 
 #define GEGL_CHANT_TYPE_FILTER
 #define GEGL_CHANT_C_FILE       "stretch-contrast.c"
 
 #include "gegl-chant.h"
+#include <math.h>
 
 static void
 buffer_get_min_max (GeglBuffer *buffer,
-                    gdouble    *min,
-                    gdouble    *max)
+                    gfloat     *min,
+                    gfloat     *max)
 {
-  gfloat tmin =  G_MAXFLOAT;
-  gfloat tmax = -G_MAXFLOAT;
-
   GeglBufferIterator *gi;
+  gint c;
   gi = gegl_buffer_iterator_new (buffer, NULL, 0, babl_format ("R'G'B' float"),
                                  GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
+  for (c = 0; c < 3; c++)
+    {
+      min[c] =  G_MAXFLOAT;
+      max[c] = -G_MAXFLOAT;
+    }
 
   while (gegl_buffer_iterator_next (gi))
     {
       gfloat *buf = gi->data[0];
 
       gint i;
-      for (i = 0; i < gi->length * 3; i++)
+      for (i = 0; i < gi->length; i++)
         {
-          gfloat val = buf [i];
-
-          if (val < tmin)
-            tmin = val;
-          if (val > tmax)
-            tmax = val;
+          for (c = 0; c < 3; c++)
+            {
+              min[c] = MIN (buf [i * 3 + c], min[c]);
+              max[c] = MAX (buf [i * 3 + c], max[c]);
+            }
         }
     }
+}
+
+static void
+reduce_min_max_global (gfloat *min,
+                       gfloat *max)
+{
+  gfloat vmin, vmax;
+  gint   c;
+
+  vmin= min[0];
+  vmax= max[0];
+
+  for (c = 1; c < 3; c++)
+    {
+      vmin = MIN (min[c], vmin);
+      vmax = MAX (max[c], vmax);
+    }
 
-  if (min)
-    *min = tmin;
-  if (max)
-    *max = tmax;
+  for (c = 0; c < 3; c++)
+    {
+      min[c] = vmin;
+      max[c] = vmax;
+    }
 }
 
 static void prepare (GeglOperation *operation)
@@ -115,8 +138,8 @@ static gboolean
 cl_buffer_get_min_max (cl_mem               in_tex,
                        size_t               global_worksize,
                        const GeglRectangle *roi,
-                       gfloat              *min,
-                       gfloat              *max)
+                       gfloat              min[4],
+                       gfloat              max[4])
 {
   cl_int   cl_err      = 0;
   size_t   local_ws, max_local_ws;
@@ -126,12 +149,12 @@ cl_buffer_get_min_max (cl_mem               in_tex,
   cl_mem   cl_aux_max  = NULL;
   cl_mem   cl_min_max  = NULL;
   cl_int   n_pixels    = (cl_int)global_worksize;
-  cl_float min_max_buf[2];
+  cl_float4 min_max_buf[2];
 
   if (global_worksize < 1)
     {
-      *min = G_MAXFLOAT;
-      *max = G_MINFLOAT;
+      min[0] = min[1] = min[2] = min[3] =  G_MAXFLOAT;
+      max[0] = max[1] = max[2] = max[3] = -G_MAXFLOAT;
       return FALSE;
     }
 
@@ -155,17 +178,17 @@ cl_buffer_get_min_max (cl_mem               in_tex,
 
   cl_aux_min = gegl_clCreateBuffer (gegl_cl_get_context (),
                                     CL_MEM_READ_WRITE,
-                                    local_ws * sizeof(cl_float),
+                                    local_ws * sizeof(cl_float4),
                                     NULL, &cl_err);
   CL_CHECK;
   cl_aux_max = gegl_clCreateBuffer (gegl_cl_get_context (),
                                     CL_MEM_READ_WRITE,
-                                    local_ws * sizeof(cl_float),
+                                    local_ws * sizeof(cl_float4),
                                     NULL, &cl_err);
   CL_CHECK;
   cl_min_max = gegl_clCreateBuffer (gegl_cl_get_context (),
                                     CL_MEM_WRITE_ONLY,
-                                    2 * sizeof(cl_float),
+                                    2 * sizeof(cl_float4),
                                     NULL, &cl_err);
   CL_CHECK;
 
@@ -196,10 +219,10 @@ cl_buffer_get_min_max (cl_mem               in_tex,
                                (void*)&cl_aux_max);
   CL_CHECK;
   cl_err = gegl_clSetKernelArg(cl_data->kernel[0], 3,
-                               sizeof(cl_float) * local_ws, NULL);
+                               sizeof(cl_float4) * local_ws, NULL);
   CL_CHECK;
   cl_err = gegl_clSetKernelArg(cl_data->kernel[0], 4,
-                               sizeof(cl_float) * local_ws, NULL);
+                               sizeof(cl_float4) * local_ws, NULL);
   CL_CHECK;
   cl_err = gegl_clSetKernelArg(cl_data->kernel[0], 5, sizeof(cl_int),
                                (void*)&n_pixels);
@@ -231,12 +254,19 @@ cl_buffer_get_min_max (cl_mem               in_tex,
   /* Read the memory buffer, probably better to keep it in GPU memory */
   cl_err = gegl_clEnqueueReadBuffer (gegl_cl_get_command_queue (),
                                      cl_min_max, CL_TRUE, 0,
-                                     2 * sizeof (cl_float), &min_max_buf, 0,
+                                     2 * sizeof (cl_float4), &min_max_buf, 0,
                                      NULL, NULL);
   CL_CHECK;
 
-  *min = min_max_buf[0];
-  *max = min_max_buf[1];
+  min[0] = min_max_buf[0].x;
+  min[1] = min_max_buf[0].y;
+  min[2] = min_max_buf[0].z;
+  min[3] = min_max_buf[0].w;
+
+  max[0] = min_max_buf[1].x;
+  max[1] = min_max_buf[1].y;
+  max[2] = min_max_buf[1].z;
+  max[3] = min_max_buf[1].w;
 
   cl_err = gegl_clReleaseMemObject (cl_aux_min);
   CL_CHECK_ONLY (cl_err);
@@ -264,37 +294,33 @@ cl_stretch_contrast (cl_mem               in_tex,
                      cl_mem               out_tex,
                      size_t               global_worksize,
                      const GeglRectangle *roi,
-                     gfloat               min,
-                     gfloat               diff)
+                     cl_float4            min,
+                     cl_float4            diff)
 {
-  cl_int   cl_err  = 0;
-  cl_float cl_min  = min;
-  cl_float cl_diff = diff;
+  cl_int cl_err  = 0;
 
-  {
   cl_err = gegl_clSetKernelArg(cl_data->kernel[2], 0, sizeof(cl_mem),
                                (void*)&in_tex);
   CL_CHECK;
   cl_err = gegl_clSetKernelArg(cl_data->kernel[2], 1, sizeof(cl_mem),
                                (void*)&out_tex);
   CL_CHECK;
-  cl_err = gegl_clSetKernelArg(cl_data->kernel[2], 2, sizeof(cl_float),
-                               (void*)&cl_min);
+  cl_err = gegl_clSetKernelArg(cl_data->kernel[2], 2, sizeof(cl_float4),
+                               (void*)&min);
   CL_CHECK;
-  cl_err = gegl_clSetKernelArg(cl_data->kernel[2], 3, sizeof(cl_float),
-                               (void*)&cl_diff);
+  cl_err = gegl_clSetKernelArg(cl_data->kernel[2], 3, sizeof(cl_float4),
+                               (void*)&diff);
   CL_CHECK;
 
   cl_err = gegl_clEnqueueNDRangeKernel(gegl_cl_get_command_queue (),
-                                         cl_data->kernel[2], 1,
-                                         NULL, &global_worksize, NULL,
-                                         0, NULL, NULL);
+                                       cl_data->kernel[2], 1,
+                                       NULL, &global_worksize, NULL,
+                                       0, NULL, NULL);
   CL_CHECK;
 
-  }
   return FALSE;
 
-error:
+ error:
   return TRUE;
 }
 
@@ -307,12 +333,16 @@ cl_process (GeglOperation       *operation,
   const Babl *in_format  = gegl_operation_get_format (operation, "input");
   const Babl *out_format = gegl_operation_get_format (operation, "output");
 
-  gfloat min = 1.0f;
-  gfloat max = 0.0f;
-  gfloat i_min, i_max, diff;
-  cl_int err = 0;
-  gint read;
+  gfloat    min[] = {1.0f, 1.0f, 1.0f, 1.0f};
+  gfloat    max[] = {0.0f, 0.0f, 0.0f, 0.0f};
+  gfloat    i_min[4], i_max[4], diff[4];
+  cl_int    err = 0;
+  gint      read, c;
   GeglBufferClIterator *i;
+  GeglChantO           *o;
+  cl_float4 cl_min, cl_diff;
+
+  o = GEGL_CHANT_PROPERTIES (operation);
 
   if (cl_build_kernels ())
     return FALSE;
@@ -327,24 +357,50 @@ cl_process (GeglOperation       *operation,
       err = cl_buffer_get_min_max (i->tex[0],
                                    i->size[0],
                                    &i->roi[0],
-                                   &i_min,
-                                   &i_max);
+                                   i_min,
+                                   i_max);
       if (err)
         {
           gegl_buffer_cl_iterator_stop (i);
           break;
         }
 
-      if (i_min < min)
-        min = i_min;
-      if (i_max > max)
-        max = i_max;
+      for (c = 0; c < 3; c++)
+        {
+          if (i_min[c] < min[c])
+            min[c] = i_min[c];
+          if (i_max[c] > max[c])
+            max[c] = i_max[c];
+        }
     }
 
   if (err)
     return FALSE;
 
-  diff = max-min;
+  if (o->keep_colors)
+    reduce_min_max_global (min, max);
+
+  for (c = 0; c < 3; c ++)
+    {
+      diff[c] = max[c] - min[c];
+
+      /* Avoid a divide by zero error if the image is a solid color */
+      if (diff[c] < 1e-3)
+        {
+          min[c]  = 0.0;
+          diff[c] = 1.0;
+        }
+    }
+
+  cl_diff.x = diff[0];
+  cl_diff.y = diff[1];
+  cl_diff.z = diff[2];
+  cl_diff.w = 1.0;
+
+  cl_min.x = min[0];
+  cl_min.y = min[1];
+  cl_min.z = min[2];
+  cl_min.w = 0.0;
 
   i = gegl_buffer_cl_iterator_new (output,
                                    result,
@@ -368,8 +424,8 @@ cl_process (GeglOperation       *operation,
                                  i->tex[0],
                                  i->size[0],
                                  &i->roi[0],
-                                 min,
-                                 diff);
+                                 cl_min,
+                                 cl_diff);
 
       if (err)
         {
@@ -388,21 +444,33 @@ process (GeglOperation       *operation,
          const GeglRectangle *result,
          gint                 level)
 {
-  gdouble  min, max, diff;
+  gfloat  min[3], max[3], diff[3];
   GeglBufferIterator *gi;
+  GeglChantO         *o;
+  gint                c;
 
   if (gegl_cl_is_accelerated ())
     if (cl_process (operation, input, output, result))
       return TRUE;
 
-  buffer_get_min_max (input, &min, &max);
-  diff = max - min;
+  o = GEGL_CHANT_PROPERTIES (operation);
 
-  /* Avoid a divide by zero error if the image is a solid color */
-  if (diff == 0.0){
-    gegl_buffer_copy (input, NULL, output, NULL);
-    return TRUE;
-  }
+  buffer_get_min_max (input, min, max);
+
+  if (o->keep_colors)
+    reduce_min_max_global (min, max);
+
+  for (c = 0; c < 3; c++)
+    {
+      diff[c] = max[c] - min[c];
+
+      /* Avoid a divide by zero error if the image is a solid color */
+      if (diff[c] < 1e-3)
+        {
+          min[c]  = 0.0;
+          diff[c] = 1.0;
+        }
+    }
 
   gi = gegl_buffer_iterator_new (input, result, 0, babl_format ("R'G'B'A float"),
                                  GEGL_BUFFER_READ, GEGL_ABYSS_NONE);
@@ -418,9 +486,9 @@ process (GeglOperation       *operation,
       gint o;
       for (o = 0; o < gi->length; o++)
         {
-          out[0] = (in[0] - min) / diff;
-          out[1] = (in[1] - min) / diff;
-          out[2] = (in[2] - min) / diff;
+          for (c = 0; c < 3; c++)
+            out[c] = (in[c] - min[c]) / diff[c];
+
           out[3] = in[3];
 
           in  += 4;


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