[gegl] transform: only rasterize inside the transformed polygon
- From: N/A <ell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl] transform: only rasterize inside the transformed polygon
- Date: Thu, 25 Jan 2018 18:03:44 +0000 (UTC)
commit 30c5af97b76d67a99c5a4ecf448caeef9fa6ea50
Author: Ell <ell_se yahoo com>
Date: Thu Jan 25 12:40:16 2018 -0500
transform: only rasterize inside the transformed polygon
Calculate the limits of each scanline that are inside the
transformed and clipped bounding-box of the input (with sufficient
headroom for the sampler context rect, and for box filtering), and
only rasterize inside those limits, zeroing the rest of the output.
This slightly improves performance, and avoids perspective-
transform artifacts when sampling close to the horizon.
operations/transform/transform-core.c | 435 +++++++++++++++++++++++++--------
1 files changed, 337 insertions(+), 98 deletions(-)
---
diff --git a/operations/transform/transform-core.c b/operations/transform/transform-core.c
index 50b894b..b7b2980 100644
--- a/operations/transform/transform-core.c
+++ b/operations/transform/transform-core.c
@@ -24,8 +24,6 @@
* 2012 Nicolas Robidoux
*/
-/* TODO: only calculate pixels inside transformed polygon */
-
#include "config.h"
#include <glib/gi18n-lib.h>
@@ -70,6 +68,13 @@ static gint gegl_transform_depth_clip (const GeglMatr
const gdouble *vertices,
gint n_vertices,
gdouble *output);
+static gboolean gegl_transform_scanline_limits (const GeglMatrix3 *inverse,
+ const GeglRectangle *bounding_box,
+ gdouble u0,
+ gdouble v0,
+ gdouble w0,
+ gint *first,
+ gint *last);
static gboolean gegl_transform_is_intermediate_node (OpTransform *transform);
static gboolean gegl_transform_is_composite_node (OpTransform *transform);
static void gegl_transform_get_source_matrix (OpTransform *transform,
@@ -449,6 +454,150 @@ gegl_transform_depth_clip (const GeglMatrix3 *matrix,
return n / 2;
}
+/*
+ * Calculate the limits, '[first, last)', of the scanline whose initial
+ * coordinates are '(u0, v0, w0)', given the inverse transform 'inverse', and
+ * the input bounding box 'bounding_box' (including any margins needed for
+ * the sampler).
+ *
+ * Returns TRUE if the scanline should be rasterized.
+ */
+static gboolean
+gegl_transform_scanline_limits (const GeglMatrix3 *inverse,
+ const GeglRectangle *bounding_box,
+ gdouble u0,
+ gdouble v0,
+ gdouble w0,
+ gint *first,
+ gint *last)
+{
+ const gdouble a = inverse->coeff[0][0];
+ const gdouble b = inverse->coeff[1][0];
+ const gdouble c = inverse->coeff[2][0];
+
+ const gdouble x1 = bounding_box->x;
+ const gdouble y1 = bounding_box->y;
+ const gdouble x2 = bounding_box->x + bounding_box->width;
+ const gdouble y2 = bounding_box->y + bounding_box->height;
+
+ gdouble i1 = *first;
+ gdouble i2 = *last;
+
+ /*
+ * Left edge.
+ */
+ if (a - x1 * c > GEGL_TRANSFORM_CORE_EPSILON)
+ {
+ const gdouble min_i = (x1 * w0 - u0) / (a - x1 * c);
+
+ i1 = MAX (i1, min_i);
+ }
+ else if (a - x1 * c < -GEGL_TRANSFORM_CORE_EPSILON)
+ {
+ const gdouble max_i = (x1 * w0 - u0) / (a - x1 * c);
+
+ i2 = MIN (i2, max_i);
+ }
+ else if (u0 < x1 * w0)
+ {
+ return FALSE;
+ }
+
+ /*
+ * Top edge.
+ */
+ if (b - y1 * c > GEGL_TRANSFORM_CORE_EPSILON)
+ {
+ const gdouble min_i = (y1 * w0 - v0) / (b - y1 * c);
+
+ i1 = MAX (i1, min_i);
+ }
+ else if (b - y1 * c < -GEGL_TRANSFORM_CORE_EPSILON)
+ {
+ const gdouble max_i = (y1 * w0 - v0) / (b - y1 * c);
+
+ i2 = MIN (i2, max_i);
+ }
+ else if (v0 < y1 * w0)
+ {
+ return FALSE;
+ }
+
+ /*
+ * Right edge.
+ */
+ if (a - x2 * c > GEGL_TRANSFORM_CORE_EPSILON)
+ {
+ const gdouble max_i = (x2 * w0 - u0) / (a - x2 * c);
+
+ i2 = MIN (i2, max_i);
+ }
+ else if (a - x2 * c < -GEGL_TRANSFORM_CORE_EPSILON)
+ {
+ const gdouble min_i = (x2 * w0 - u0) / (a - x2 * c);
+
+ i1 = MAX (i1, min_i);
+ }
+ else if (u0 > x2 * w0)
+ {
+ return FALSE;
+ }
+
+ /*
+ * Bottom edge.
+ */
+ if (b - y2 * c > GEGL_TRANSFORM_CORE_EPSILON)
+ {
+ const gdouble max_i = (y2 * w0 - v0) / (b - y2 * c);
+
+ i2 = MIN (i2, max_i);
+ }
+ else if (b - y2 * c < -GEGL_TRANSFORM_CORE_EPSILON)
+ {
+ const gdouble min_i = (y2 * w0 - v0) / (b - y2 * c);
+
+ i1 = MAX (i1, min_i);
+ }
+ else if (v0 > y2 * w0)
+ {
+ return FALSE;
+ }
+
+ /*
+ * Add a 1-pixel border, to accommodate for box filtering.
+ */
+ i1 = MAX (i1 - 1.0, *first);
+ i2 = MIN (i2 + 1.0, *last);
+
+ /*
+ * Horizon.
+ */
+ if (c > GEGL_TRANSFORM_CORE_EPSILON)
+ {
+ const gdouble min_i = (GEGL_TRANSFORM_CORE_EPSILON - w0) / c;
+
+ i1 = MAX (i1, min_i);
+ }
+ else if (c < -GEGL_TRANSFORM_CORE_EPSILON)
+ {
+ const gdouble max_i = (GEGL_TRANSFORM_CORE_EPSILON - w0) / c;
+
+ i2 = MIN (i2, max_i);
+ }
+ else if (w0 < GEGL_TRANSFORM_CORE_EPSILON)
+ {
+ return FALSE;
+ }
+
+ i1 = CLAMP (i1, G_MININT / 2, G_MAXINT / 2);
+ i2 = CLAMP (i2, G_MININT / 2, G_MAXINT / 2);
+
+ *first = ceil (i1);
+ *last = ceil (i2);
+
+ return *first < *last;
+}
+
static gboolean
gegl_transform_is_intermediate_node (OpTransform *transform)
{
@@ -927,16 +1076,26 @@ transform_affine (GeglOperation *operation,
level?GEGL_SAMPLER_NEAREST:transform->sampler,
level);
- GeglRectangle dest_extent = *roi;
GeglSamplerGetFun sampler_get_fun = gegl_sampler_get_fun (sampler);
- dest_extent.x >>= level;
- dest_extent.y >>= level;
- dest_extent.width >>= level;
- dest_extent.height >>= level;
+ GeglRectangle bounding_box = *gegl_buffer_get_abyss (src);
+ GeglRectangle context_rect = *gegl_sampler_get_context_rect (sampler);
+ GeglRectangle dest_extent = *roi;
+ bounding_box.x >>= level;
+ bounding_box.y >>= level;
+ bounding_box.width >>= level;
+ bounding_box.height >>= level;
+ bounding_box.x += context_rect.x;
+ bounding_box.y += context_rect.y;
+ bounding_box.width += context_rect.width - 1;
+ bounding_box.height += context_rect.height - 1;
+ dest_extent.x >>= level;
+ dest_extent.y >>= level;
+ dest_extent.width >>= level;
+ dest_extent.height >>= level;
/*
* XXX: fast paths as existing in files in the same dir as
@@ -1007,21 +1166,45 @@ transform_affine (GeglOperation *operation,
gint y = roi->height;
do {
- gdouble u_float = u_start;
- gdouble v_float = v_start;
-
- gint x = roi->width;
- do {
- sampler_get_fun (sampler,
- u_float, v_float,
- &inverse_jacobian,
- dest_ptr,
- GEGL_ABYSS_NONE);
- dest_ptr += (gint) 4;
-
- u_float += inverse_jacobian.coeff [0][0];
- v_float += inverse_jacobian.coeff [1][0];
- } while (--x);
+ gint x1 = 0;
+ gint x2 = roi->width;
+
+ if (gegl_transform_scanline_limits (&inverse, &bounding_box,
+ u_start, v_start, 1.0,
+ &x1, &x2))
+ {
+ gdouble u_float = u_start;
+ gdouble v_float = v_start;
+
+ gint x;
+
+ memset (dest_ptr, 0, (gint) 4 * sizeof (gfloat) * x1);
+ dest_ptr += (gint) 4 * x1;
+
+ u_float += x1 * inverse_jacobian.coeff [0][0];
+ v_float += x1 * inverse_jacobian.coeff [1][0];
+
+ for (x = x1; x < x2; x++)
+ {
+ sampler_get_fun (sampler,
+ u_float, v_float,
+ &inverse_jacobian,
+ dest_ptr,
+ GEGL_ABYSS_NONE);
+ dest_ptr += (gint) 4;
+
+ u_float += inverse_jacobian.coeff [0][0];
+ v_float += inverse_jacobian.coeff [1][0];
+ }
+
+ memset (dest_ptr, 0, (gint) 4 * sizeof (gfloat) * (roi->width - x2));
+ dest_ptr += (gint) 4 * (roi->width - x2);
+ }
+ else
+ {
+ memset (dest_ptr, 0, (gint) 4 * sizeof (gfloat) * roi->width);
+ dest_ptr += (gint) 4 * roi->width;
+ }
u_start += inverse_jacobian.coeff [0][1];
v_start += inverse_jacobian.coeff [1][1];
@@ -1052,11 +1235,24 @@ transform_generic (GeglOperation *operation,
level);
GeglSamplerGetFun sampler_get_fun = gegl_sampler_get_fun (sampler);
- GeglRectangle dest_extent = *roi;
- dest_extent.x >>= level;
- dest_extent.y >>= level;
- dest_extent.width >>= level;
- dest_extent.height >>= level;
+ GeglRectangle bounding_box = *gegl_buffer_get_abyss (src);
+ GeglRectangle context_rect = *gegl_sampler_get_context_rect (sampler);
+ GeglRectangle dest_extent = *roi;
+
+ bounding_box.x >>= level;
+ bounding_box.y >>= level;
+ bounding_box.width >>= level;
+ bounding_box.height >>= level;
+
+ bounding_box.x += context_rect.x;
+ bounding_box.y += context_rect.y;
+ bounding_box.width += context_rect.width - 1;
+ bounding_box.height += context_rect.height - 1;
+
+ dest_extent.x >>= level;
+ dest_extent.y >>= level;
+ dest_extent.width >>= level;
+ dest_extent.height >>= level;
/*
* Construct an output tile iterator.
@@ -1109,44 +1305,62 @@ transform_generic (GeglOperation *operation,
*/
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 {
- if (w_float >= GEGL_TRANSFORM_CORE_EPSILON)
- {
- gdouble w_recip = (gdouble) 1.0 / w_float;
- gdouble u = u_float * w_recip;
- gdouble v = v_float * w_recip;
-
- GeglMatrix2 inverse_jacobian;
- inverse_jacobian.coeff [0][0] =
- (inverse.coeff [0][0] - inverse.coeff [2][0] * u) * w_recip;
- inverse_jacobian.coeff [0][1] =
- (inverse.coeff [0][1] - inverse.coeff [2][1] * u) * w_recip;
- inverse_jacobian.coeff [1][0] =
- (inverse.coeff [1][0] - inverse.coeff [2][0] * v) * w_recip;
- inverse_jacobian.coeff [1][1] =
- (inverse.coeff [1][1] - inverse.coeff [2][1] * v) * w_recip;
-
- sampler_get_fun (sampler,
- u, v,
- &inverse_jacobian,
- dest_ptr,
- GEGL_ABYSS_NONE);
- }
- else
- {
- memset (dest_ptr, 0, 4 * sizeof (gfloat));
- }
+ gint x1 = 0;
+ gint x2 = roi->width;
- dest_ptr += (gint) 4;
- u_float += inverse.coeff [0][0];
- v_float += inverse.coeff [1][0];
- w_float += inverse.coeff [2][0];
- } while (--x);
+ if (gegl_transform_scanline_limits (&inverse, &bounding_box,
+ u_start, v_start, w_start,
+ &x1, &x2))
+ {
+ gdouble u_float = u_start;
+ gdouble v_float = v_start;
+ gdouble w_float = w_start;
+
+ gint x;
+
+ memset (dest_ptr, 0, (gint) 4 * sizeof (gfloat) * x1);
+ dest_ptr += (gint) 4 * x1;
+
+ u_float += x1 * inverse.coeff [0][0];
+ v_float += x1 * inverse.coeff [1][0];
+ w_float += x1 * inverse.coeff [2][0];
+
+ for (x = x1; x < x2; x++)
+ {
+ gdouble w_recip = (gdouble) 1.0 / w_float;
+ gdouble u = u_float * w_recip;
+ gdouble v = v_float * w_recip;
+
+ GeglMatrix2 inverse_jacobian;
+ inverse_jacobian.coeff [0][0] =
+ (inverse.coeff [0][0] - inverse.coeff [2][0] * u) * w_recip;
+ inverse_jacobian.coeff [0][1] =
+ (inverse.coeff [0][1] - inverse.coeff [2][1] * u) * w_recip;
+ inverse_jacobian.coeff [1][0] =
+ (inverse.coeff [1][0] - inverse.coeff [2][0] * v) * w_recip;
+ inverse_jacobian.coeff [1][1] =
+ (inverse.coeff [1][1] - inverse.coeff [2][1] * v) * w_recip;
+
+ sampler_get_fun (sampler,
+ u, v,
+ &inverse_jacobian,
+ dest_ptr,
+ GEGL_ABYSS_NONE);
+
+ dest_ptr += (gint) 4;
+ u_float += inverse.coeff [0][0];
+ v_float += inverse.coeff [1][0];
+ w_float += inverse.coeff [2][0];
+ }
+
+ memset (dest_ptr, 0, (gint) 4 * sizeof (gfloat) * (roi->width - x2));
+ dest_ptr += (gint) 4 * (roi->width - x2);
+ }
+ else
+ {
+ memset (dest_ptr, 0, (gint) 4 * sizeof (gfloat) * roi->width);
+ dest_ptr += (gint) 4 * roi->width;
+ }
u_start += inverse.coeff [0][1];
v_start += inverse.coeff [1][1];
@@ -1174,11 +1388,18 @@ transform_nearest (GeglOperation *operation,
level);
GeglSamplerGetFun sampler_get_fun = gegl_sampler_get_fun (sampler);
- GeglRectangle dest_extent = *roi;
- dest_extent.x >>= level;
- dest_extent.y >>= level;
- dest_extent.width >>= level;
- dest_extent.height >>= level;
+ GeglRectangle bounding_box = *gegl_buffer_get_abyss (src);
+ GeglRectangle dest_extent = *roi;
+
+ bounding_box.x >>= level;
+ bounding_box.y >>= level;
+ bounding_box.width >>= level;
+ bounding_box.height >>= level;
+
+ dest_extent.x >>= level;
+ dest_extent.y >>= level;
+ dest_extent.width >>= level;
+ dest_extent.height >>= level;
/*
* Construct an output tile iterator.
@@ -1231,34 +1452,52 @@ transform_nearest (GeglOperation *operation,
*/
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 {
- if (w_float >= GEGL_TRANSFORM_CORE_EPSILON)
- {
- gdouble w_recip = (gdouble) 1.0 / w_float;
- gdouble u = u_float * w_recip;
- gdouble v = v_float * w_recip;
-
- sampler_get_fun (sampler,
- u, v,
- NULL,
- dest_ptr,
- GEGL_ABYSS_NONE);
- }
- else
- {
- memset (dest_ptr, 0, px_size);
- }
+ gint x1 = 0;
+ gint x2 = roi->width;
- dest_ptr += px_size;
- u_float += inverse.coeff [0][0];
- v_float += inverse.coeff [1][0];
- w_float += inverse.coeff [2][0];
- } while (--x);
+ if (gegl_transform_scanline_limits (&inverse, &bounding_box,
+ u_start, v_start, w_start,
+ &x1, &x2))
+ {
+ gdouble u_float = u_start;
+ gdouble v_float = v_start;
+ gdouble w_float = w_start;
+
+ gint x;
+
+ memset (dest_ptr, 0, px_size * x1);
+ dest_ptr += px_size * x1;
+
+ u_float += x1 * inverse.coeff [0][0];
+ v_float += x1 * inverse.coeff [1][0];
+ w_float += x1 * inverse.coeff [2][0];
+
+ for (x = x1; x < x2; x++)
+ {
+ gdouble w_recip = (gdouble) 1.0 / w_float;
+ gdouble u = u_float * w_recip;
+ gdouble v = v_float * w_recip;
+
+ sampler_get_fun (sampler,
+ u, v,
+ NULL,
+ dest_ptr,
+ GEGL_ABYSS_NONE);
+
+ dest_ptr += px_size;
+ u_float += inverse.coeff [0][0];
+ v_float += inverse.coeff [1][0];
+ w_float += inverse.coeff [2][0];
+ }
+
+ memset (dest_ptr, 0, px_size * (roi->width - x2));
+ dest_ptr += px_size * (roi->width - x2);
+ }
+ else
+ {
+ memset (dest_ptr, 0, px_size * roi->width);
+ dest_ptr += px_size * roi->width;
+ }
u_start += inverse.coeff [0][1];
v_start += inverse.coeff [1][1];
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]