Commit 535ceb02 authored by Ell's avatar Ell

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.
parent e6e67340
......@@ -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);
}
}
......
......@@ -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);
}
}
......@@ -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
......
......@@ -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];
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment