[gegl] buffer: fix incorrect interpoaltion format in linear/cubic samplers box filtering



commit 535ceb02b27904a3a7a9dce40d7028a81c689e3b
Author: Ell <ell_se yahoo com>
Date:   Wed Feb 27 15:22:07 2019 -0500

    buffer: fix incorrect interpoaltion format in linear/cubic samplers box filtering
    
    Add a GeglSampler::interpolate() function, which subclasses should
    implement if they use the generic box-filter algorithm.  This
    function is similar to GeglSampler::get(), except that it always
    performs point sampling (and therefore doesn't take a scale
    matrix), and, in particular, should return its result using the
    sampler's premultiplied interpolation format, rather than its
    output format.
    
    Use interpolate(), instead of get(), in _gegl_sampler_box_get(),
    to avoid erroneously performing box filtering using the sampler's
    output format.
    
    Implement interpolate() in the linear and cubic samplers.

 gegl/buffer/gegl-sampler-cubic.c  | 184 +++++++++++++++++---------------
 gegl/buffer/gegl-sampler-linear.c | 216 +++++++++++++++++++++-----------------
 gegl/buffer/gegl-sampler.c        |  11 +-
 gegl/buffer/gegl-sampler.h        |  49 ++++++---
 4 files changed, 258 insertions(+), 202 deletions(-)
---
diff --git a/gegl/buffer/gegl-sampler-cubic.c b/gegl/buffer/gegl-sampler-cubic.c
index fffd53742..8acf14bd1 100644
--- a/gegl/buffer/gegl-sampler-cubic.c
+++ b/gegl/buffer/gegl-sampler-cubic.c
@@ -35,24 +35,29 @@ enum
   PROP_LAST
 };
 
-static void gegl_sampler_cubic_finalize (      GObject         *gobject);
-static void gegl_sampler_cubic_get      (      GeglSampler     *sampler,
-                                         const gdouble          absolute_x,
-                                         const gdouble          absolute_y,
-                                               GeglBufferMatrix2*scale,
-                                               void            *output,
-                                               GeglAbyssPolicy  repeat_mode);
-static void get_property                (      GObject         *gobject,
-                                               guint            prop_id,
-                                               GValue          *value,
-                                               GParamSpec      *pspec);
-static void set_property                (      GObject         *gobject,
-                                               guint            prop_id,
-                                         const GValue          *value,
-                                               GParamSpec      *pspec);
-static inline gfloat cubicKernel        (const gfloat           x,
-                                         const gfloat           b,
-                                         const gfloat           c);
+static void            gegl_sampler_cubic_finalize    (      GObject               *gobject);
+static inline void     gegl_sampler_cubic_interpolate (      GeglSampler* restrict  self,
+                                                        const gdouble               absolute_x,
+                                                        const gdouble               absolute_y,
+                                                              gfloat*     restrict  output,
+                                                              GeglAbyssPolicy       repeat_mode);
+static void            gegl_sampler_cubic_get         (      GeglSampler* restrict  self,
+                                                       const gdouble                absolute_x,
+                                                       const gdouble                absolute_y,
+                                                             GeglBufferMatrix2*     scale,
+                                                             void*        restrict  output,
+                                                             GeglAbyssPolicy        repeat_mode);
+static void            get_property                   (      GObject               *gobject,
+                                                             guint                  prop_id,
+                                                             GValue                *value,
+                                                             GParamSpec            *pspec);
+static void            set_property                   (      GObject               *gobject,
+                                                             guint                  prop_id,
+                                                       const GValue                *value,
+                                                             GParamSpec            *pspec);
+static inline gfloat   cubicKernel                    (const gfloat                 x,
+                                                       const gfloat                 b,
+                                                       const gfloat                 c);
 
 
 G_DEFINE_TYPE (GeglSamplerCubic, gegl_sampler_cubic, GEGL_TYPE_SAMPLER)
@@ -67,7 +72,8 @@ gegl_sampler_cubic_class_init (GeglSamplerCubicClass *klass)
   object_class->get_property = get_property;
   object_class->finalize     = gegl_sampler_cubic_finalize;
 
-  sampler_class->get     = gegl_sampler_cubic_get;
+  sampler_class->get         = gegl_sampler_cubic_get;
+  sampler_class->interpolate = gegl_sampler_cubic_interpolate;
 
   g_object_class_install_property ( object_class, PROP_B,
     g_param_spec_double ("b",
@@ -152,7 +158,76 @@ gegl_sampler_cubic_init (GeglSamplerCubic *self)
     }
 }
 
-void
+static inline void
+gegl_sampler_cubic_interpolate (      GeglSampler     *self,
+                                const gdouble          absolute_x,
+                                const gdouble          absolute_y,
+                                      gfloat          *output,
+                                      GeglAbyssPolicy  repeat_mode)
+{
+  GeglSamplerCubic *cubic      = (GeglSamplerCubic*)(self);
+  gint              components = self->interpolate_components;
+  gfloat            cubic_b    = cubic->b;
+  gfloat            cubic_c    = cubic->c;
+  gfloat           *sampler_bptr;
+  gfloat            factor_i[4];
+  gint              c;
+  gint              i;
+  gint              j;
+
+  /*
+   * The "-1/2"s are there because we want the index of the pixel
+   * center to the left and top of the location, and with GIMP's
+   * convention the top left of the top left pixel is located at
+   * (0,0), and its center is at (1/2,1/2), so that anything less than
+   * 1/2 needs to go negative. Another way to look at this is that we
+   * are converting from a coordinate system in which the origin is at
+   * the top left corner of the pixel with index (0,0), to a
+   * coordinate system in which the origin is at the center of the
+   * same pixel.
+   */
+  const double iabsolute_x = (double) absolute_x - 0.5;
+  const double iabsolute_y = (double) absolute_y - 0.5;
+
+  const gint ix = floorf (iabsolute_x);
+  const gint iy = floorf (iabsolute_y);
+
+  /*
+   * x is the x-coordinate of the sampling point relative to the
+   * position of the center of the top left pixel. Similarly for
+   * y. Range of values: [0,1].
+   */
+  const gfloat x = iabsolute_x - ix;
+  const gfloat y = iabsolute_y - iy;
+
+  sampler_bptr = gegl_sampler_get_ptr (self, ix, iy, repeat_mode) -
+                 (GEGL_SAMPLER_MAXIMUM_WIDTH + 1) * components;
+
+  for (c = 0; c < components; c++)
+    output[c] = 0.0f;
+
+  for (i = 0; i < 4; i++)
+    factor_i[i] = cubicKernel (x - (i - 1), cubic_b, cubic_c);
+
+  for (j = 0; j < 4; j++)
+    {
+      gfloat factor_j = cubicKernel (y - (j - 1), cubic_b, cubic_c);
+
+      for (i = 0; i < 4; i++)
+        {
+          const gfloat factor = factor_j * factor_i[i];
+
+          for (c = 0; c < components; c++)
+            output[c] += factor * sampler_bptr[c];
+
+          sampler_bptr += components;
+        }
+
+      sampler_bptr += (GEGL_SAMPLER_MAXIMUM_WIDTH - 4) * components;
+    }
+}
+
+static void
 gegl_sampler_cubic_get (      GeglSampler       *self,
                         const gdouble            absolute_x,
                         const gdouble            absolute_y,
@@ -163,69 +238,12 @@ gegl_sampler_cubic_get (      GeglSampler       *self,
   if (! _gegl_sampler_box_get (self, absolute_x, absolute_y, scale,
                                output, repeat_mode, 5))
   {
-    GeglSamplerCubic *cubic      = (GeglSamplerCubic*)(self);
-    gint              components = self->interpolate_components;
-    gfloat            cubic_b    = cubic->b;
-    gfloat            cubic_c    = cubic->c;
-    gfloat           *sampler_bptr;
-    gfloat            factor_i[4];
-    gfloat            newval[components];
-    gint              c;
-    gint              i;
-    gint              j;
-
-    /*
-     * The "-1/2"s are there because we want the index of the pixel
-     * center to the left and top of the location, and with GIMP's
-     * convention the top left of the top left pixel is located at
-     * (0,0), and its center is at (1/2,1/2), so that anything less than
-     * 1/2 needs to go negative. Another way to look at this is that we
-     * are converting from a coordinate system in which the origin is at
-     * the top left corner of the pixel with index (0,0), to a
-     * coordinate system in which the origin is at the center of the
-     * same pixel.
-     */
-    const double iabsolute_x = (double) absolute_x - 0.5;
-    const double iabsolute_y = (double) absolute_y - 0.5;
-
-    const gint ix = floorf (iabsolute_x);
-    const gint iy = floorf (iabsolute_y);
-
-    /*
-     * x is the x-coordinate of the sampling point relative to the
-     * position of the center of the top left pixel. Similarly for
-     * y. Range of values: [0,1].
-     */
-    const gfloat x = iabsolute_x - ix;
-    const gfloat y = iabsolute_y - iy;
-
-    sampler_bptr = gegl_sampler_get_ptr (self, ix, iy, repeat_mode) -
-                   (GEGL_SAMPLER_MAXIMUM_WIDTH + 1) * components;
-
-    for (c = 0; c < components; c++)
-      newval[c] = 0.0f;
-
-    for (i = 0; i < 4; i++)
-      factor_i[i] = cubicKernel (x - (i - 1), cubic_b, cubic_c);
-
-    for (j = 0; j < 4; j++)
-      {
-        gfloat factor_j = cubicKernel (y - (j - 1), cubic_b, cubic_c);
-
-        for (i = 0; i < 4; i++)
-          {
-            const gfloat factor = factor_j * factor_i[i];
-
-            for (c = 0; c < components; c++)
-              newval[c] += factor * sampler_bptr[c];
-
-            sampler_bptr += components;
-          }
-
-        sampler_bptr += (GEGL_SAMPLER_MAXIMUM_WIDTH - 4) * components;
-      }
-
-    babl_process (self->fish, newval, output, 1);
+    gfloat result[5];
+
+    gegl_sampler_cubic_interpolate (self, absolute_x, absolute_y, result,
+                                    repeat_mode);
+
+    babl_process (self->fish, result, output, 1);
   }
 }
 
diff --git a/gegl/buffer/gegl-sampler-linear.c b/gegl/buffer/gegl-sampler-linear.c
index cc76f86e3..dc4cf758e 100644
--- a/gegl/buffer/gegl-sampler-linear.c
+++ b/gegl/buffer/gegl-sampler-linear.c
@@ -31,12 +31,18 @@ enum
   PROP_LAST
 };
 
-static void gegl_sampler_linear_get (      GeglSampler* restrict  self,
-                                     const gdouble                absolute_x,
-                                     const gdouble                absolute_y,
-                                           GeglBufferMatrix2     *scale,
-                                           void*        restrict  output,
-                                           GeglAbyssPolicy        repeat_mode);
+
+static inline void   gegl_sampler_linear_interpolate (      GeglSampler* restrict  self,
+                                                      const gdouble                absolute_x,
+                                                      const gdouble                absolute_y,
+                                                            gfloat*      restrict  output,
+                                                            GeglAbyssPolicy        repeat_mode);
+static void          gegl_sampler_linear_get         (      GeglSampler* restrict  self,
+                                                      const gdouble                absolute_x,
+                                                      const gdouble                absolute_y,
+                                                            GeglBufferMatrix2     *scale,
+                                                            void*        restrict  output,
+                                                            GeglAbyssPolicy        repeat_mode);
 
 G_DEFINE_TYPE (GeglSamplerLinear, gegl_sampler_linear, GEGL_TYPE_SAMPLER)
 
@@ -45,7 +51,8 @@ gegl_sampler_linear_class_init (GeglSamplerLinearClass *klass)
 {
   GeglSamplerClass *sampler_class = GEGL_SAMPLER_CLASS (klass);
 
-  sampler_class->get = gegl_sampler_linear_get;
+  sampler_class->get         = gegl_sampler_linear_get;
+  sampler_class->interpolate = gegl_sampler_linear_interpolate;
 }
 
 /*
@@ -72,98 +79,93 @@ gegl_sampler_linear_init (GeglSamplerLinear *self)
   GEGL_SAMPLER (self)->level[0].context_rect.height =  3 + 2*LINEAR_EXTRA_ELBOW_ROOM;
 }
 
-void
-gegl_sampler_linear_get (     GeglSampler       *self,
-                        const gdouble            absolute_x,
-                        const gdouble            absolute_y,
-                              GeglBufferMatrix2 *scale,
-                              void              *output,
-                              GeglAbyssPolicy    repeat_mode)
+static inline void
+gegl_sampler_linear_interpolate (      GeglSampler       *self,
+                                 const gdouble            absolute_x,
+                                 const gdouble            absolute_y,
+                                       gfloat            *output,
+                                       GeglAbyssPolicy    repeat_mode)
 {
-  gint nc = self->interpolate_components;
-  if (! _gegl_sampler_box_get (self, absolute_x, absolute_y, scale,
-                               output, repeat_mode, 4))
+  gint       nc                    = self->interpolate_components;
+  const gint pixels_per_buffer_row = GEGL_SAMPLER_MAXIMUM_WIDTH;
+
+  /*
+   * The "-1/2"s are there because we want the index of the pixel to
+   * the left and top of the location, and with GIMP's convention the
+   * top left of the top left pixel is located at
+   * (1/2,1/2). Basically, we are converting from a coordinate system
+   * in which the origin is at the top left pixel of the pixel with
+   * index (0,0), to a coordinate system in which the origin is at the
+   * center of the same pixel.
+   */
+  const float iabsolute_x = (float) absolute_x - 0.5;
+  const float iabsolute_y = (float) absolute_y - 0.5;
+
+  const gint ix = floorf (iabsolute_x);
+  const gint iy = floorf (iabsolute_y);
+
+  /*
+   * Point the data tile pointer to the first channel of the top_left
+   * pixel value:
+   */
+  const gfloat* restrict in_bptr =
+    gegl_sampler_get_ptr (self, ix, iy, repeat_mode);
+
+  /*
+   * x is the x-coordinate of the sampling point relative to the
+   * position of the center of the top left pixel. Similarly for
+   * y. Range of values: [0,1].
+   */
+  const gfloat x = iabsolute_x - ix;
+  const gfloat y = iabsolute_y - iy;
+
+  /*
+   * First bilinear weight:
+   */
+  const gfloat x_times_y = x * y;
+
+  /*
+   * Load top row:
+   */
+  gfloat top_left[nc];
+  gfloat top_rite[nc];
+
+  for (gint c = 0; c < nc; c++)
+    top_left[c] = *in_bptr++;
+
+  for (gint c = 0; c < nc; c++)
+    top_rite[c] = *in_bptr++;
+
+  in_bptr += ( pixels_per_buffer_row - 2 ) * nc;
+
+  {
+  /*
+   * More bilinear weights:
+   *
+   * (Note: w = 1-x and z = 1-y.)
+   */
+  const gfloat w_times_y = y - x_times_y;
+  const gfloat x_times_z = x - x_times_y;
+
+  /*
+   * Load bottom row:
+   */
+  gfloat bot_left[5];
+  gfloat bot_rite[5];
+  for (gint c = 0; c < nc; c++)
+    bot_left[c] = *in_bptr++;
+  for (gint c = 0; c < nc; c++)
+    bot_rite[c] = *in_bptr++;
+
+  /*
+   * Last bilinear weight:
+   */
   {
-    const gint pixels_per_buffer_row = GEGL_SAMPLER_MAXIMUM_WIDTH;
-
-    /*
-     * The "-1/2"s are there because we want the index of the pixel to
-     * the left and top of the location, and with GIMP's convention the
-     * top left of the top left pixel is located at
-     * (1/2,1/2). Basically, we are converting from a coordinate system
-     * in which the origin is at the top left pixel of the pixel with
-     * index (0,0), to a coordinate system in which the origin is at the
-     * center of the same pixel.
-     */
-    const float iabsolute_x = (float) absolute_x - 0.5;
-    const float iabsolute_y = (float) absolute_y - 0.5;
-
-    const gint ix = floorf (iabsolute_x);
-    const gint iy = floorf (iabsolute_y);
-
-    /*
-     * Point the data tile pointer to the first channel of the top_left
-     * pixel value:
-     */
-    const gfloat* restrict in_bptr =
-      gegl_sampler_get_ptr (self, ix, iy, repeat_mode);
-
-    /*
-     * x is the x-coordinate of the sampling point relative to the
-     * position of the center of the top left pixel. Similarly for
-     * y. Range of values: [0,1].
-     */
-    const gfloat x = iabsolute_x - ix;
-    const gfloat y = iabsolute_y - iy;
-
-    /*
-     * First bilinear weight:
-     */
-    const gfloat x_times_y = x * y;
-
-    /*
-     * Load top row:
-     */
-    gfloat top_left[nc];
-    gfloat top_rite[nc];
-
-    for (gint c = 0; c < nc; c++)
-      top_left[c] = *in_bptr++;
-
-    for (gint c = 0; c < nc; c++)
-      top_rite[c] = *in_bptr++;
-
-    in_bptr += ( pixels_per_buffer_row - 2 ) * nc;
+  const gfloat w_times_z = (gfloat) 1. - ( x + w_times_y );
 
+  for (gint c = 0; c < nc; c++)
     {
-    /*
-     * More bilinear weights:
-     *
-     * (Note: w = 1-x and z = 1-y.)
-     */
-    const gfloat w_times_y = y - x_times_y;
-    const gfloat x_times_z = x - x_times_y;
-
-    /*
-     * Load bottom row:
-     */
-    gfloat bot_left[5];
-    gfloat bot_rite[5];
-    for (gint c = 0; c < nc; c++)
-      bot_left[c] = *in_bptr++;
-    for (gint c = 0; c < nc; c++)
-      bot_rite[c] = *in_bptr++;
-
-    /*
-     * Last bilinear weight:
-     */
-    {
-    const gfloat w_times_z = (gfloat) 1. - ( x + w_times_y );
-
-    gfloat newval[5];
-
-      for (gint c = 0; c < nc; c++)
-        newval[c] =
+      output[c] =
         x_times_y * bot_rite[c]
         +
         w_times_y * bot_left[c]
@@ -171,9 +173,27 @@ gegl_sampler_linear_get (     GeglSampler       *self,
         x_times_z * top_rite[c]
         +
         w_times_z * top_left[c];
-
-      babl_process (self->fish, newval, output, 1);
-    }
     }
   }
+  }
+}
+
+static void
+gegl_sampler_linear_get (      GeglSampler       *self,
+                         const gdouble            absolute_x,
+                         const gdouble            absolute_y,
+                               GeglBufferMatrix2 *scale,
+                               void              *output,
+                               GeglAbyssPolicy    repeat_mode)
+{
+  if (! _gegl_sampler_box_get (self, absolute_x, absolute_y, scale,
+                               output, repeat_mode, 4))
+  {
+    gfloat result[5];
+
+    gegl_sampler_linear_interpolate (self, absolute_x, absolute_y, result,
+                                     repeat_mode);
+
+    babl_process (self->fish, result, output, 1);
+  }
 }
diff --git a/gegl/buffer/gegl-sampler.c b/gegl/buffer/gegl-sampler.c
index 93a33d609..394da9bf6 100644
--- a/gegl/buffer/gegl-sampler.c
+++ b/gegl/buffer/gegl-sampler.c
@@ -86,9 +86,10 @@ gegl_sampler_class_init (GeglSamplerClass *klass)
   object_class->dispose  = dispose;
   object_class->constructed  = constructed;
 
-  klass->prepare    = NULL;
-  klass->get        = NULL;
-  klass->set_buffer = set_buffer;
+  klass->prepare     = NULL;
+  klass->get         = NULL;
+  klass->interpolate = NULL;
+  klass->set_buffer  = set_buffer;
 
   object_class->set_property = set_property;
   object_class->get_property = get_property;
@@ -143,7 +144,9 @@ constructed (GObject *self)
 {
   GeglSampler *sampler = (void*)(self);
   GeglSamplerClass *klass = GEGL_SAMPLER_GET_CLASS (sampler);
-  sampler->get = klass->get;
+
+  sampler->get         = klass->get;
+  sampler->interpolate = klass->interpolate;
 }
 
 void
diff --git a/gegl/buffer/gegl-sampler.h b/gegl/buffer/gegl-sampler.h
index ef16917c6..132a03faa 100644
--- a/gegl/buffer/gegl-sampler.h
+++ b/gegl/buffer/gegl-sampler.h
@@ -56,6 +56,18 @@ G_BEGIN_DECLS
 #define GEGL_SAMPLER_MAXIMUM_HEIGHT 64
 #define GEGL_SAMPLER_MAXIMUM_WIDTH (GEGL_SAMPLER_MAXIMUM_HEIGHT)
 
+/* samplers that use the generic box-filter algorithm should provide an
+ * interpolate() function, which should be similar to their get() function,
+ * except that it always performs point sampling (and therefore doesn't take a
+ * scale matrix), and should return its result using the sampler's
+ * interpolation format, rather than its output format.
+ */
+typedef void (* GeglSamplerInterpolateFun) (GeglSampler     *self,
+                                            gdouble          x,
+                                            gdouble          y,
+                                            gfloat          *output,
+                                            GeglAbyssPolicy  repeat_mode);
+
 typedef struct _GeglSamplerClass GeglSamplerClass;
 
 typedef struct GeglSamplerLevel
@@ -71,29 +83,32 @@ typedef struct GeglSamplerLevel
 
 struct _GeglSampler
 {
-  GObject            parent_instance;
-  GeglSamplerGetFun  get;
+  GObject                    parent_instance;
+
+  GeglSamplerGetFun          get;
+  GeglSamplerInterpolateFun  interpolate;
 
   /*< private >*/
-  GeglBuffer        *buffer;
-  gint               lvel;
-  const Babl        *format;
-  const Babl        *interpolate_format;
-  const Babl        *fish;
-  gint               interpolate_bpp;
-  gint               interpolate_components;
-
-  GeglSamplerLevel   level[GEGL_SAMPLER_MIPMAP_LEVELS];
+  GeglBuffer                *buffer;
+  gint                       lvel;
+  const Babl                *format;
+  const Babl                *interpolate_format;
+  const Babl                *fish;
+  gint                       interpolate_bpp;
+  gint                       interpolate_components;
+
+  GeglSamplerLevel           level[GEGL_SAMPLER_MIPMAP_LEVELS];
 };
 
 struct _GeglSamplerClass
 {
   GObjectClass  parent_class;
 
-  void (* prepare)   (GeglSampler     *self);
-  GeglSamplerGetFun   get;
-  void  (*set_buffer) (GeglSampler     *self,
-                       GeglBuffer      *buffer);
+  void                      (* prepare)    (GeglSampler *self);
+  GeglSamplerGetFun            get;
+  GeglSamplerInterpolateFun    interpolate;
+  void                      (* set_buffer) (GeglSampler *self,
+                                            GeglBuffer  *buffer);
 };
 
 GType gegl_sampler_get_type    (void) G_GNUC_CONST;
@@ -286,7 +301,7 @@ _gegl_sampler_box_get (GeglSampler*    restrict  self,
                     {
                       int c;
                       gfloat input[4];
-                      self->get (self, x, y, NULL, input, repeat_mode);
+                      self->interpolate (self, x, y, input, repeat_mode);
                       for (c = 0; c < 4; c++)
                         result[c] += input[c];
 
@@ -333,7 +348,7 @@ _gegl_sampler_box_get (GeglSampler*    restrict  self,
                         {
                           int c;
                           gfloat input[channels];
-                          self->get (self, x, y, NULL, input, repeat_mode);
+                          self->interpolate (self, x, y, input, repeat_mode);
                           for (c = 0; c < channels; c++)
                             result[c] += input[c];
 


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