[gegl] transform: add a special case for nearest sampler
- From: Øyvind Kolås <ok src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl] transform: add a special case for nearest sampler
- Date: Tue, 9 Jan 2018 16:55:14 +0000 (UTC)
commit efea6460745133846054bbdd640bb0ab6e225380
Author: Øyvind Kolås <pippin gimp org>
Date: Tue Jan 9 15:36:55 2018 +0100
transform: add a special case for nearest sampler
There is some geometric computations currently done that are discarded by the
nearest sampler, instead of using a nearest sampler we can get efficiently away
with using gegl_buffer_get on 1x1 pixels.
operations/transform/transform-core.c | 186 ++++++++++++++++++++++++++++++++-
1 files changed, 183 insertions(+), 3 deletions(-)
---
diff --git a/operations/transform/transform-core.c b/operations/transform/transform-core.c
index 7802c9e..776ffc7 100644
--- a/operations/transform/transform-core.c
+++ b/operations/transform/transform-core.c
@@ -971,6 +971,10 @@ transform_generic (GeglOperation *operation,
{
GeglRectangle *roi = &i->roi[0];
/*
+ * Obsolete comment and optimizationz, the samplers are now adaptive
+ * to access patterns.
+ *
+ * XXX XXX XXX XXX
* This code uses a variant of the (novel?) method of ensuring
* that scanlines stay, as much as possible, within an input
* "tile", given that these wider than tall "tiles" are biased
@@ -1099,6 +1103,179 @@ transform_generic (GeglOperation *operation,
g_object_unref (sampler);
}
+static void
+transform_nearest (GeglOperation *operation,
+ GeglBuffer *dest,
+ GeglBuffer *src,
+ GeglMatrix3 *matrix,
+ const GeglRectangle *roi,
+ gint level)
+{
+ const Babl *format = gegl_buffer_get_format (dest);
+ gint factor = 1 << level;
+ GeglBufferIterator *i;
+ GeglMatrix3 inverse;
+
+ GeglRectangle dest_extent = *roi;
+ dest_extent.x >>= level;
+ dest_extent.y >>= level;
+ dest_extent.width >>= level;
+ dest_extent.height >>= level;
+
+ /*
+ * Construct an output tile iterator.
+ */
+ i = gegl_buffer_iterator_new (dest,
+ &dest_extent,
+ level,
+ format,
+ GEGL_ACCESS_WRITE,
+ GEGL_ABYSS_NONE);
+
+ gegl_matrix3_copy_into (&inverse, matrix);
+
+ if (factor)
+ {
+ inverse.coeff[0][0] /= factor;
+ inverse.coeff[0][1] /= factor;
+ inverse.coeff[0][2] /= factor;
+ inverse.coeff[1][0] /= factor;
+ inverse.coeff[1][1] /= factor;
+ inverse.coeff[1][2] /= factor;
+ }
+
+ gegl_matrix3_invert (&inverse);
+
+ /*
+ * Fill the output tiles.
+ */
+ while (gegl_buffer_iterator_next (i))
+ {
+ GeglRectangle *roi = &i->roi[0];
+ /*
+ * Obsolete comment and optimization, the samplers are now adaptive
+ * to access patterns.
+ *
+ * XXX XXX XXX XXX
+ * This code uses a variant of the (novel?) method of ensuring
+ * that scanlines stay, as much as possible, within an input
+ * "tile", given that these wider than tall "tiles" are biased
+ * so that there is more elbow room at the bottom and right than
+ * at the top and left, explained in the transform_affine
+ * function. It is not as foolproof because perspective
+ * transformations change the orientation of scanlines, and
+ * consequently what's good at the bottom may not be best at the
+ * top.
+ */
+ /*
+ * Determine whether tile access should be "flipped". First, in
+ * the y direction, because this is the one we can afford most
+ * not to get right.
+ */
+ const gdouble u_start_y =
+ inverse.coeff [0][0] * (roi->x + (gdouble) 0.5) +
+ inverse.coeff [0][1] * (roi->y + (gdouble) 0.5) +
+ inverse.coeff [0][2];
+ const gdouble v_start_y =
+ inverse.coeff [1][0] * (roi->x + (gdouble) 0.5) +
+ inverse.coeff [1][1] * (roi->y + (gdouble) 0.5) +
+ inverse.coeff [1][2];
+ const gdouble w_start_y =
+ inverse.coeff [2][0] * (roi->x + (gdouble) 0.5) +
+ inverse.coeff [2][1] * (roi->y + (gdouble) 0.5) +
+ inverse.coeff [2][2];
+
+ const gdouble u_float_y =
+ u_start_y + inverse.coeff [0][1] * (roi->height - (gint) 1);
+ const gdouble v_float_y =
+ v_start_y + inverse.coeff [1][1] * (roi->height - (gint) 1);
+ const gdouble w_float_y =
+ w_start_y + inverse.coeff [2][1] * (roi->height - (gint) 1);
+
+ /*
+ * Check whether the next scanline is likely to fall within the
+ * biased tile.
+ */
+ const gint bflip_y =
+ (u_float_y+v_float_y)/w_float_y < (u_start_y+v_start_y)/w_start_y
+ ?
+ (gint) 1
+ :
+ (gint) 0;
+
+ /*
+ * Determine whether to flip in the horizontal direction. Done
+ * last because this is the most important one, and consequently
+ * we want to use the likely "initial scanline" to at least get
+ * that one about right.
+ */
+ const gdouble u_start_x = bflip_y ? u_float_y : u_start_y;
+ const gdouble v_start_x = bflip_y ? v_float_y : v_start_y;
+ const gdouble w_start_x = bflip_y ? w_float_y : w_start_y;
+
+ const gdouble u_float_x =
+ u_start_x + inverse.coeff [0][0] * (roi->width - (gint) 1);
+ const gdouble v_float_x =
+ v_start_x + inverse.coeff [1][0] * (roi->width - (gint) 1);
+ const gdouble w_float_x =
+ w_start_x + inverse.coeff [2][0] * (roi->width - (gint) 1);
+
+ const gint bflip_x =
+ (u_float_x + v_float_x)/w_float_x < (u_start_x + v_start_x)/w_start_x
+ ?
+ (gint) 1
+ :
+ (gint) 0;
+
+ gfloat * restrict dest_ptr =
+ (gfloat *)i->data[0] +
+ (gint) 4 * ( bflip_x * (roi->width - (gint) 1) +
+ bflip_y * (roi->height - (gint) 1) * roi->width );
+
+ gdouble u_start = bflip_x ? u_float_x : u_start_x;
+ gdouble v_start = bflip_x ? v_float_x : v_start_x;
+ gdouble w_start = bflip_x ? w_float_x : w_start_x;
+
+ const gint flip_x = (gint) 1 - (gint) 2 * bflip_x;
+ const gint flip_y = (gint) 1 - (gint) 2 * bflip_y;
+
+ /*
+ * Assumes that height and width are > 0.
+ */
+ gint y = roi->height;
+ do {
+ gdouble u_float = u_start;
+ gdouble v_float = v_start;
+ gdouble w_float = w_start;
+
+ gint x = roi->width;
+ do {
+ gdouble w_recip = (gdouble) 1.0 / w_float;
+ gdouble u = u_float * w_recip;
+ gdouble v = v_float * w_recip;
+
+ gegl_buffer_get (src,
+ GEGL_RECTANGLE((int)u, (int)v, 1, 1),
+ 1.0,
+ format,
+ dest_ptr,
+ 0,
+ GEGL_ABYSS_NONE);
+
+ dest_ptr += flip_x * (gint) 4;
+ u_float += flip_x * inverse.coeff [0][0];
+ v_float += flip_x * inverse.coeff [1][0];
+ w_float += flip_x * inverse.coeff [2][0];
+ } while (--x);
+
+ dest_ptr += (gint) 4 * (flip_y - flip_x) * roi->width;
+ u_start += flip_y * inverse.coeff [0][1];
+ v_start += flip_y * inverse.coeff [1][1];
+ w_start += flip_y * inverse.coeff [2][1];
+ } while (--y);
+ }
+}
+
/*
* Use to determine if key transform matrix coefficients are close
* enough to zero or integers.
@@ -1205,6 +1382,9 @@ gegl_transform_process (GeglOperation *operation,
}
else
{
+ /*
+ * For other cases, do a proper resampling
+ */
void (*func) (GeglOperation *operation,
GeglBuffer *dest,
GeglBuffer *src,
@@ -1215,9 +1395,9 @@ gegl_transform_process (GeglOperation *operation,
if (gegl_matrix3_is_affine (&matrix))
func = transform_affine;
- /*
- * For all other cases, do a proper resampling
- */
+ if (transform->sampler == GEGL_SAMPLER_NEAREST)
+ func = transform_nearest;
+
input = (GeglBuffer*) gegl_operation_context_dup_object (context, "input");
output = gegl_operation_context_get_target (context, "output");
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]