[gegl] long-shadow: various fixes and improvements
- From: Ell <ell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl] long-shadow: various fixes and improvements
- Date: Thu, 9 Aug 2018 22:46:42 +0000 (UTC)
commit a5ce7c7051b98dc6b7149bd1c08d69bb79df0197
Author: Ell <ell_se yahoo com>
Date: Thu Aug 9 18:27:19 2018 -0400
long-shadow: various fixes and improvements
A bunch of logic fixes and speed improvements, before moving to
common.
operations/workshop/long-shadow.c | 392 ++++++++++++++++++++------------------
1 file changed, 205 insertions(+), 187 deletions(-)
---
diff --git a/operations/workshop/long-shadow.c b/operations/workshop/long-shadow.c
index 8e600ff68..3324016a1 100644
--- a/operations/workshop/long-shadow.c
+++ b/operations/workshop/long-shadow.c
@@ -74,6 +74,9 @@ property_enum (composition, _("Composition"),
#include "gegl-op.h"
#include <math.h>
+/* virtual screen resolution, as a factor of the image resolution. must be an
+ * integer.
+ */
#define SCREEN_RESOLUTION 16
#define EPSILON 1e-6
@@ -108,57 +111,58 @@ typedef struct
typedef struct
{
- GeglProperties options;
+ GeglProperties options;
/* image -> filter coordinate transformation */
- gboolean flip_horizontally;
- gboolean flip_vertically;
- gboolean flip_diagonally;
+ gboolean flip_horizontally;
+ gboolean flip_vertically;
+ gboolean flip_diagonally;
/* in filter coordinates */
- gdouble tan_angle;
-
- gdouble sample_offset_x;
- gdouble sample_offset_y;
+ gdouble tan_angle;
- gint shadow_height;
+ gint shadow_height;
+ gfloat shadow_remainder;
- gfloat fade_rate;
+ gfloat fade_rate;
- GeglRectangle input_bounds;
- GeglRectangle roi;
- GeglRectangle area;
+ GeglRectangle input_bounds;
+ GeglRectangle roi;
+ GeglRectangle area;
/* in screen coordinates */
- gint u0, u1;
-
- gpointer screen;
- gint pixel_size;
- gint n_active_pixels;
- gint prev_n_active_pixels;
+ gint u0, u1;
- gdouble filter_pixel_shadow_width;
+ gpointer screen;
+ gint pixel_size;
+ gint active_u0;
+ gint active_u1;
- GeglBuffer *input;
- GeglBuffer *output;
+ GeglBuffer *input;
+ GeglBuffer *output;
- const Babl *format;
+ const Babl *format;
- gfloat *input_row;
- gfloat *output_row;
+ gfloat *input_row;
+ gfloat *output_row;
- gfloat *input_row0;
- gfloat *output_row0;
- gint row_step;
+ gfloat *input_row0;
+ gfloat *output_row0;
+ gint row_step;
- GeglSampler *sampler;
- GeglSamplerGetFun sampler_get_fun;
+ gint row_fx0;
+ gint row_fx1;
+ gint row_u0;
+ gint row_input_pixel_offset0;
+ gint row_input_pixel_offset1;
+ gint row_output_pixel_span;
+ gfloat row_output_pixel_kernel[2 * SCREEN_RESOLUTION + 1];
- gfloat color[4];
+ gfloat color[4];
- gint level;
- gdouble scale;
- gdouble scale_inv;
+ gint level;
+ gdouble scale;
+ gdouble scale_inv;
} Context;
static void
@@ -179,6 +183,8 @@ init_options (Context *ctx,
static void
init_geometry (Context *ctx)
{
+ gdouble shadow_height;
+
ctx->flip_horizontally = FALSE;
ctx->flip_vertically = FALSE;
ctx->flip_diagonally = FALSE;
@@ -216,10 +222,10 @@ init_geometry (Context *ctx)
ctx->tan_angle = tan (ctx->options.angle);
- ctx->sample_offset_x = -sin (ctx->options.angle) * ctx->options.length;
- ctx->sample_offset_y = -cos (ctx->options.angle) * ctx->options.length;
+ shadow_height = cos (ctx->options.angle) * ctx->options.length;
- ctx->shadow_height = floor (-ctx->sample_offset_y);
+ ctx->shadow_height = ceil (shadow_height);
+ ctx->shadow_remainder = 1.0 - (ctx->shadow_height - shadow_height);
if (ctx->options.midpoint > EPSILON)
{
@@ -378,8 +384,8 @@ get_affected_filter_range (Context *ctx,
gint *fx0,
gint *fx1)
{
- if (fx0) *fx0 = floor (project_to_filter (ctx, u0, fy));
- if (fx1) *fx1 = ceil (project_to_filter (ctx, u1, fy));
+ if (fx0) *fx0 = floor (project_to_filter (ctx, u0, fy - 0.5));
+ if (fx1) *fx1 = ceil (project_to_filter (ctx, u1, fy + 0.5));
}
static inline void
@@ -390,8 +396,8 @@ get_affecting_screen_range (Context *ctx,
gint *u0,
gint *u1)
{
- if (u0) *u0 = floor (project_to_screen (ctx, fx0, fy));
- if (u1) *u1 = ceil (project_to_screen (ctx, fx1, fy));
+ if (u0) *u0 = floor (project_to_screen (ctx, fx0, fy + 0.5));
+ if (u1) *u1 = ceil (project_to_screen (ctx, fx1, fy - 0.5));
}
static inline void
@@ -469,10 +475,8 @@ init_screen (Context *ctx)
ctx->screen = g_malloc0 (ctx->pixel_size * (ctx->u1 - ctx->u0));
ctx->screen = (guchar *) ctx->screen - ctx->pixel_size * ctx->u0;
- ctx->n_active_pixels = 0;
- ctx->prev_n_active_pixels = 1;
-
- ctx->filter_pixel_shadow_width = SCREEN_RESOLUTION * (1.0 + ctx->tan_angle);
+ ctx->active_u0 = ctx->u1;
+ ctx->active_u1 = ctx->u0;
}
static inline void
@@ -510,7 +514,68 @@ cleanup_screen (Context *ctx)
g_free (ctx->screen);
}
-static inline void
+static void
+init_row (Context *ctx,
+ gint fy)
+{
+ gdouble u0, u1;
+ gdouble v0, v1;
+ gdouble b;
+ gint i;
+
+ get_affecting_filter_range (ctx,
+ ctx->u0, ctx->u1,
+ fy,
+ &ctx->row_fx0, &ctx->row_fx1);
+
+ ctx->row_fx0 = MAX (ctx->row_fx0, ctx->area.x);
+ ctx->row_fx1 = MIN (ctx->row_fx1, ctx->area.x + ctx->area.width);
+
+ u0 = project_to_screen (ctx, ctx->row_fx0, fy + 0.5);
+ u1 = project_to_screen (ctx, ctx->row_fx0 + 1.0, fy - 0.5);
+
+ ctx->row_u0 = floor (u0);
+
+ ctx->row_input_pixel_offset0 = floor (u0 + 0.5) - ctx->row_u0;
+ ctx->row_input_pixel_offset1 = floor (u1 + 0.5) - ctx->row_u0;
+
+ ctx->row_output_pixel_span = ceil (u1) - floor (u0);
+
+ b = ctx->tan_angle * SCREEN_RESOLUTION;
+
+ v0 = u0 + b;
+ v1 = u1 - b;
+
+ for (i = 0; i < ctx->row_output_pixel_span; i++)
+ {
+ gdouble w0, w1;
+ gdouble value = 0.0;
+
+ if (b > EPSILON)
+ {
+ w0 = CLAMP (ctx->row_u0 + i, u0, v0);
+ w1 = CLAMP (ctx->row_u0 + i + 1.0, u0, v0);
+ value += (w1 - w0) * (w0 + w1 - 2.0 * u0) / (2.0 * b);
+ }
+
+ w0 = CLAMP (ctx->row_u0 + i, v0, v1);
+ w1 = CLAMP (ctx->row_u0 + i + 1.0, v0, v1);
+ value += w1 - w0;
+
+ if (b > EPSILON)
+ {
+ w0 = CLAMP (ctx->row_u0 + i, v1, u1);
+ w1 = CLAMP (ctx->row_u0 + i + 1.0, v1, u1);
+ value += (w1 - w0) * (2.0 * u1 - w0 - w1) / (2.0 * b);
+ }
+
+ value /= SCREEN_RESOLUTION;
+
+ ctx->row_output_pixel_kernel[i] = value;
+ }
+}
+
+static inline gboolean
shift_pixel (Context *ctx,
Pixel *pixel)
{
@@ -520,9 +585,7 @@ shift_pixel (Context *ctx,
{
pixel->shadow.value = 0.0f;
- ctx->n_active_pixels--;
-
- return;
+ return FALSE;
}
pixel->shadow = item->shadow;
@@ -531,53 +594,72 @@ shift_pixel (Context *ctx,
pixel->queue->prev = item->prev;
g_slice_free (ShadowItem, item);
+
+ return TRUE;
}
static void
trim_shadow (Context *ctx,
gint fy)
{
- ctx->prev_n_active_pixels = ctx->n_active_pixels;
-
- if (! ctx->n_active_pixels)
+ if (ctx->active_u0 >= ctx->active_u1)
return;
switch (ctx->options.style)
{
case GEGL_LONG_SHADOW_STYLE_FINITE:
{
- Pixel *p = ctx->screen;
+ Pixel *p = ctx->screen;
+ gint active_u0 = ctx->u1;
+ gint active_u1 = ctx->u0;
gint u;
fy -= ctx->shadow_height;
- for (u = ctx->u0; u < ctx->u1; u++)
+ for (u = ctx->active_u0; u < ctx->active_u1; u++)
{
- Pixel *pixel = &p[u];
+ Pixel *pixel = &p[u];
+ gboolean active = (pixel->shadow.value != 0.0);
- while (pixel->shadow.value && pixel->shadow.fy < fy)
- shift_pixel (ctx, pixel);
+ if (active && pixel->shadow.fy < fy)
+ active = shift_pixel (ctx, pixel);
+
+ if (active)
+ {
+ active_u0 = MIN (active_u0, u);
+ active_u1 = u + 1;
+ }
}
+
+ ctx->active_u0 = active_u0;
+ ctx->active_u1 = active_u1;
}
break;
case GEGL_LONG_SHADOW_STYLE_FADING:
{
- gfloat *p = ctx->screen;
- gboolean active_pixels = FALSE;
- gint u;
+ gfloat *p = ctx->screen;
+ gint active_u0 = ctx->u1;
+ gint active_u1 = ctx->u0;
+ gint u;
- for (u = ctx->u0; u < ctx->u1; u++)
+ for (u = ctx->active_u0; u < ctx->active_u1; u++)
{
p[u] *= ctx->fade_rate;
if (p[u] < EPSILON)
- p[u] = 0.0f;
+ {
+ p[u] = 0.0f;
+ }
else
- active_pixels = TRUE;
+ {
+ active_u0 = MIN (active_u0, u);
+ active_u1 = u + 1;
+ }
}
- ctx->n_active_pixels = active_pixels;
+ ctx->active_u0 = active_u0;
+ ctx->active_u1 = active_u1;
}
break;
@@ -611,21 +693,25 @@ add_shadow (Context *ctx,
if (value >= pixel->shadow.value)
{
- ctx->n_active_pixels += ! pixel->shadow.value;
-
pixel->shadow.value = value;
pixel->shadow.fy = fy;
clear_pixel_queue (ctx, pixel);
+
+ ctx->active_u0 = MIN (ctx->active_u0, u0);
+ ctx->active_u1 = MAX (ctx->active_u1, u1);
}
else if (! pixel->queue)
{
- pixel->queue = g_slice_new (ShadowItem);
+ if (fy != pixel->shadow.fy)
+ {
+ pixel->queue = g_slice_new (ShadowItem);
- pixel->queue->shadow.value = value;
- pixel->queue->shadow.fy = fy;
- pixel->queue->next = NULL;
- pixel->queue->prev = pixel->queue;
+ pixel->queue->shadow.value = value;
+ pixel->queue->shadow.fy = fy;
+ pixel->queue->next = NULL;
+ pixel->queue->prev = pixel->queue;
+ }
}
else
{
@@ -647,6 +733,9 @@ add_shadow (Context *ctx,
if (! new_item)
{
+ if (fy == last_item->shadow.fy)
+ continue;
+
new_item = g_slice_new (ShadowItem);
new_item->prev = last_item;
@@ -668,19 +757,20 @@ add_shadow (Context *ctx,
for (u = u0; u < u1; u++)
p[u] = MAX (p[u], value);
- ctx->n_active_pixels = 1;
+ ctx->active_u0 = MIN (ctx->active_u0, u0);
+ ctx->active_u1 = MAX (ctx->active_u1, u1);
}
}
static inline void
add_shadow_at (Context *ctx,
- gdouble u0,
+ gint u,
gint fy,
gfloat value)
{
add_shadow (ctx,
- floor (u0 + 0.5),
- floor (u0 + ctx->filter_pixel_shadow_width + 0.5),
+ u + ctx->row_input_pixel_offset0,
+ u + ctx->row_input_pixel_offset1,
fy,
value);
}
@@ -692,9 +782,18 @@ get_pixel_shadow (Context *ctx,
{
if (ctx->options.style == GEGL_LONG_SHADOW_STYLE_FINITE)
{
- const Pixel *p = pixel;
+ const Pixel *p = pixel;
+ gfloat value = p->shadow.value;
- return p->shadow.value;
+ if (value && p->shadow.fy + ctx->shadow_height == fy)
+ {
+ value *= ctx->shadow_remainder;
+
+ if (p->queue)
+ value += (1.0 - ctx->shadow_remainder) * p->queue->shadow.value;
+ }
+
+ return value;
}
else
{
@@ -746,26 +845,11 @@ init_buffers (Context *ctx,
ctx->row_step = -4;
}
-
- if (ctx->options.style == GEGL_LONG_SHADOW_STYLE_FINITE)
- {
- ctx->sampler = gegl_buffer_sampler_new_at_level (input, ctx->format,
- GEGL_SAMPLER_LINEAR,
- ctx->level);
-
- ctx->sampler_get_fun = gegl_sampler_get_fun (ctx->sampler);
- }
- else
- {
- ctx->sampler = NULL;
- }
}
static void
cleanup_buffers (Context *ctx)
{
- g_clear_object (&ctx->sampler);
-
if (ctx->options.composition !=
GEGL_LONG_SHADOW_COMPOSITION_SHADOW_PLUS_IMAGE)
{
@@ -802,67 +886,30 @@ set_row (Context *ctx,
}
static gfloat
-get_shadow (Context *ctx,
- gdouble u0,
- gdouble u1,
- gint fx,
- gint fy)
+get_shadow_at (Context *ctx,
+ gint u,
+ gint fy)
{
gfloat result = 0.0f;
- if (ctx->n_active_pixels)
+ if (ctx->active_u0 < ctx->active_u1)
{
- gdouble u0i, u0f;
- gdouble u1i, u1f;
- gint a, b;
- const guchar *pixel;
- gint u;
-
- u0 = MAX (u0, ctx->u0);
- u1 = MIN (u1, ctx->u1);
+ const guchar *pixel = ctx->screen;
+ gint u0, u1;
+ gint i;
- u0i = ceil (u0);
- u0f = u0i - u0;
+ u0 = MAX (u, ctx->active_u0);
+ u1 = MIN (u + ctx->row_output_pixel_span, ctx->active_u1);
- u1i = floor (u1);
- u1f = u1 - u1i;
+ pixel += u0 * ctx->pixel_size;
- a = u0i;
- b = u1i;
-
- pixel = ctx->screen;
- pixel += (a - 1) * ctx->pixel_size;
-
- if (u0f)
- result += get_pixel_shadow (ctx, pixel, fy) * u0f;
- pixel += ctx->pixel_size;
-
- for (u = a; u < b; u++)
+ for (i = u0 - u; i < u1 - u; i++)
{
- result += get_pixel_shadow (ctx, pixel, fy);
- pixel += ctx->pixel_size;
- }
-
- if (u1f)
- result += get_pixel_shadow (ctx, pixel, fy) * u1f;
+ result += ctx->row_output_pixel_kernel[i] *
+ get_pixel_shadow (ctx, pixel, fy);
- result /= SCREEN_RESOLUTION;
- }
-
- if (ctx->sampler && (ctx->n_active_pixels || ctx->prev_n_active_pixels))
- {
- gdouble ix, iy;
- gfloat sample[4];
-
- transform_coords_to_image (ctx,
- fx + 0.5 + ctx->sample_offset_x,
- fy + 0.5 + ctx->sample_offset_y,
- &ix, &iy);
-
- ctx->sampler_get_fun (ctx->sampler,
- ix, iy, NULL, sample, GEGL_ABYSS_NONE);
-
- result = MAX (result, sample[3]);
+ pixel += ctx->pixel_size;
+ }
}
return result;
@@ -918,24 +965,12 @@ get_required_for_output (GeglOperation *operation,
if (o->style == GEGL_LONG_SHADOW_STYLE_FINITE)
{
Context ctx;
- gdouble sample_x;
- gdouble sample_y;
init_options (&ctx, o, 0);
init_geometry (&ctx);
init_area (&ctx, operation, roi);
- sample_x = ctx.roi.x + ctx.sample_offset_x;
- sample_y = ctx.roi.y + ctx.sample_offset_y;
-
- result.x = floor (sample_x);
- result.y = floor (sample_y);
- result.width = roi->width + (result.x < sample_x);
- result.height = roi->height + (result.y < sample_y);
-
- gegl_rectangle_bounding_box (&result, &result, &ctx.area);
-
- gegl_rectangle_intersect (&result, &result, &ctx.input_bounds);
+ gegl_rectangle_intersect (&result, &ctx.area, &ctx.input_bounds);
transform_rect_to_image (&ctx, &result, &result, TRUE);
}
@@ -981,10 +1016,8 @@ get_invalidated_by_change (GeglOperation *operation,
fx1++;
- result.width += ceil (-ctx.sample_offset_x);
- result.height += ceil (-ctx.sample_offset_y);
-
- result.width = MAX (result.width, fx1 - result.x);
+ result.width = fx1 - result.x;
+ result.height += ctx.shadow_height;
transform_rect_to_image (&ctx, &result, &result, TRUE);
}
@@ -1052,50 +1085,35 @@ process (GeglOperation *operation,
{
const gfloat *input_pixel;
gfloat *output_pixel;
- gdouble u0;
- gdouble shadow_offset;
- gint fx0, fx1;
+ gint u;
+
+ init_row (&ctx, fy);
get_row (&ctx, fy);
if (fy > ctx.roi.y)
trim_shadow (&ctx, fy);
- get_affecting_filter_range (&ctx,
- ctx.u0, ctx.u1,
- fy,
- &fx0, &fx1);
-
- fx0 = MAX (fx0, ctx.area.x);
- fx1 = MIN (fx1, ctx.area.x + ctx.area.width);
-
- u0 = project_to_screen (&ctx, fx0, fy);
-
- shadow_offset = project_to_screen (&ctx, fx0, fy + 0.5) - u0;
-
- input_pixel = ctx.input_row0 + (fx0 - ctx.area.x) * ctx.row_step;
+ input_pixel = ctx.input_row0 +
+ (ctx.row_fx0 - ctx.area.x) * ctx.row_step;
output_pixel = ctx.output_row0;
- for (fx = fx0; fx < fx1; fx++)
+ u = ctx.row_u0;
+
+ for (fx = ctx.row_fx0; fx < ctx.row_fx1; fx++)
{
- add_shadow_at (&ctx, u0 + shadow_offset, fy, input_pixel[3]);
+ add_shadow_at (&ctx, u, fy, input_pixel[3]);
if (fy >= ctx.roi.y && fx >= ctx.roi.x)
{
- gfloat shadow_value;
-
- shadow_value = get_shadow (&ctx,
- u0, u0 + SCREEN_RESOLUTION,
- fx, fy);
-
set_output_pixel (&ctx,
input_pixel, output_pixel,
- shadow_value);
+ get_shadow_at (&ctx, u, fy));
output_pixel += ctx.row_step;
}
- u0 += SCREEN_RESOLUTION;
+ u += SCREEN_RESOLUTION;
input_pixel += ctx.row_step;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]