[gegl] buffer: fix incorrect interpoaltion format in linear/cubic samplers box filtering
- From: Ell <ell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl] buffer: fix incorrect interpoaltion format in linear/cubic samplers box filtering
- Date: Wed, 27 Feb 2019 21:45:50 +0000 (UTC)
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]