[gegl] long-shadow: add "fading-fixed-length" and "fading-fixed-rate" styles
- From: Ell <ell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl] long-shadow: add "fading-fixed-length" and "fading-fixed-rate" styles
- Date: Wed, 22 Aug 2018 08:46:04 +0000 (UTC)
commit 7febad8a98cb4e779977bcce84e656a351457284
Author: Ell <ell_se yahoo com>
Date: Wed Aug 22 04:37:39 2018 -0400
long-shadow: add "fading-fixed-length" and "fading-fixed-rate" styles
Add two finite fading shadow styles: "fading-fixed-length", and
"fading-fixed-rate". Both styles produce the same result for
fully-opaque input pixels, but diverge for partially-transparent
pixels. "fading-fixed-length" produces a shadow of the same length
regardless of the input pixel's alpha value, such that the shadow
fading rate of pixels with lower alpha values is lower, while
"fading-fixed-rate" produces a shadow with a fixed fade rate,
regardless of the input pixel's alpha value, such that the shadow
of pixels with lower alpha values is shorter.
Currently, finite fading shadows don't support user-defined
midpoint values, and their drop-off curve is always linear (as if
the midpoint were 0.5, in relative terms). Nonlinear fixed-length
fading shadows break some of the algorithm's invariants, and
addressing that would increase the computational complexity.
operations/common/long-shadow.c | 313 +++++++++++++++++++++++++++++++---------
1 file changed, 243 insertions(+), 70 deletions(-)
---
diff --git a/operations/common/long-shadow.c b/operations/common/long-shadow.c
index aa3cab9f4..848b347fb 100644
--- a/operations/common/long-shadow.c
+++ b/operations/common/long-shadow.c
@@ -22,9 +22,11 @@
#ifdef GEGL_PROPERTIES
enum_start (gegl_long_shadow_style)
- enum_value (GEGL_LONG_SHADOW_STYLE_FINITE, "finite", N_("Finite"))
- enum_value (GEGL_LONG_SHADOW_STYLE_INFINITE, "infinite", N_("Infinite"))
- enum_value (GEGL_LONG_SHADOW_STYLE_FADING, "fading", N_("Fading"))
+ enum_value (GEGL_LONG_SHADOW_STYLE_FINITE, "finite", N_("Finite"))
+ enum_value (GEGL_LONG_SHADOW_STYLE_INFINITE, "infinite", N_("Infinite"))
+ enum_value (GEGL_LONG_SHADOW_STYLE_FADING, "fading", N_("Fading"))
+ enum_value (GEGL_LONG_SHADOW_STYLE_FADING_FIXED_LENGTH, "fading-fixed-length", N_("Fading (fixed length)"))
+ enum_value (GEGL_LONG_SHADOW_STYLE_FADING_FIXED_RATE, "fading-fixed-rate", N_("Fading (fixed rate)"))
enum_end (GeglLongShadowStyle)
enum_start (gegl_long_shadow_composition)
@@ -48,7 +50,9 @@ property_double (length, _("Length"), 100.0)
description (_("Shadow length"))
value_range (0.0, G_MAXDOUBLE)
ui_range (0.0, 1000.0)
- ui_meta ("visible", "style {finite}")
+ ui_meta ("visible", "style {finite, "
+ " fading-fixed-length, "
+ " fading-fixed-rate}")
property_double (midpoint, _("Midpoint"), 100.0)
description (_("Shadow fade midpoint"))
@@ -126,45 +130,81 @@ typedef struct
gfloat fade_rate;
- GeglRectangle input_bounds;
- GeglRectangle roi;
- GeglRectangle area;
+ GeglRectangle input_bounds;
+ GeglRectangle roi;
+ GeglRectangle area;
/* in screen coordinates */
- gint u0, u1;
+ gint u0, u1;
- gpointer screen;
- gint pixel_size;
- gint active_u0;
- gint active_u1;
+ 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;
- 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];
+ 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 inline gboolean
+is_finite (const GeglProperties *options)
+{
+ switch (options->style)
+ {
+ case GEGL_LONG_SHADOW_STYLE_FINITE:
+ case GEGL_LONG_SHADOW_STYLE_FADING_FIXED_LENGTH:
+ case GEGL_LONG_SHADOW_STYLE_FADING_FIXED_RATE:
+ return TRUE;
+
+ case GEGL_LONG_SHADOW_STYLE_INFINITE:
+ case GEGL_LONG_SHADOW_STYLE_FADING:
+ return FALSE;
+ }
+
+ g_return_val_if_reached (FALSE);
+}
+
+static inline gboolean
+is_fading (const GeglProperties *options)
+{
+ switch (options->style)
+ {
+ case GEGL_LONG_SHADOW_STYLE_FADING:
+ case GEGL_LONG_SHADOW_STYLE_FADING_FIXED_LENGTH:
+ case GEGL_LONG_SHADOW_STYLE_FADING_FIXED_RATE:
+ return TRUE;
+
+ case GEGL_LONG_SHADOW_STYLE_FINITE:
+ case GEGL_LONG_SHADOW_STYLE_INFINITE:
+ return FALSE;
+ }
+
+ g_return_val_if_reached (FALSE);
+}
+
static void
init_options (Context *ctx,
const GeglProperties *options,
@@ -183,8 +223,6 @@ 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;
@@ -222,19 +260,14 @@ init_geometry (Context *ctx)
ctx->tan_angle = tan (ctx->options.angle);
- shadow_height = cos (ctx->options.angle) * ctx->options.length;
+ if (is_finite (&ctx->options))
+ {
+ gdouble shadow_height;
- ctx->shadow_height = ceil (shadow_height);
- ctx->shadow_remainder = 1.0 - (ctx->shadow_height - shadow_height);
+ shadow_height = cos (ctx->options.angle) * ctx->options.length;
- if (ctx->options.midpoint > EPSILON)
- {
- ctx->fade_rate = pow (0.5, 1.0 / (cos (ctx->options.angle) *
- ctx->options.midpoint));
- }
- else
- {
- ctx->fade_rate = 0.0f;
+ ctx->shadow_height = ceil (shadow_height);
+ ctx->shadow_remainder = 1.0 - (ctx->shadow_height - shadow_height);
}
}
@@ -412,6 +445,42 @@ get_affecting_filter_range (Context *ctx,
if (fx1) *fx1 = ceil (project_to_filter (ctx, u1 - 0.5, fy + 0.5));
}
+static void
+init_fade (Context *ctx)
+{
+ switch (ctx->options.style)
+ {
+ case GEGL_LONG_SHADOW_STYLE_FADING:
+ if (ctx->options.midpoint > EPSILON)
+ {
+ ctx->fade_rate = pow (0.5, 1.0 / (cos (ctx->options.angle) *
+ ctx->options.midpoint));
+ }
+ else
+ {
+ ctx->fade_rate = 0.0f;
+ }
+ break;
+
+ case GEGL_LONG_SHADOW_STYLE_FADING_FIXED_LENGTH:
+ case GEGL_LONG_SHADOW_STYLE_FADING_FIXED_RATE:
+ if (ctx->shadow_height > 0)
+ {
+ ctx->fade_rate = 1.0 / (cos (ctx->options.angle) *
+ ctx->options.length);
+ }
+ else
+ {
+ ctx->fade_rate = 1.0f;
+ }
+ break;
+
+ case GEGL_LONG_SHADOW_STYLE_FINITE:
+ case GEGL_LONG_SHADOW_STYLE_INFINITE:
+ break;
+ }
+}
+
static void
init_area (Context *ctx,
GeglOperation *operation,
@@ -439,7 +508,7 @@ init_area (Context *ctx,
ctx->area = ctx->roi;
- if (ctx->options.style == GEGL_LONG_SHADOW_STYLE_FINITE)
+ if (is_finite (&ctx->options))
{
gint u0;
@@ -467,10 +536,19 @@ init_area (Context *ctx,
static void
init_screen (Context *ctx)
{
- if (ctx->options.style == GEGL_LONG_SHADOW_STYLE_FINITE)
- ctx->pixel_size = sizeof (Pixel);
- else
- ctx->pixel_size = sizeof (gfloat);
+ switch (ctx->options.style)
+ {
+ case GEGL_LONG_SHADOW_STYLE_FINITE:
+ case GEGL_LONG_SHADOW_STYLE_FADING_FIXED_LENGTH:
+ ctx->pixel_size = sizeof (Pixel);
+ break;
+
+ case GEGL_LONG_SHADOW_STYLE_INFINITE:
+ case GEGL_LONG_SHADOW_STYLE_FADING:
+ case GEGL_LONG_SHADOW_STYLE_FADING_FIXED_RATE:
+ ctx->pixel_size = sizeof (gfloat);
+ break;
+ }
ctx->screen = g_malloc0 (ctx->pixel_size * (ctx->u1 - ctx->u0));
ctx->screen = (guchar *) ctx->screen - ctx->pixel_size * ctx->u0;
@@ -500,7 +578,7 @@ clear_pixel_queue (Context *ctx,
static void
cleanup_screen (Context *ctx)
{
- if (ctx->options.style == GEGL_LONG_SHADOW_STYLE_FINITE)
+ if (ctx->pixel_size == sizeof (Pixel))
{
Pixel *p = ctx->screen;
gint u;
@@ -575,6 +653,22 @@ init_row (Context *ctx,
}
}
+static inline gfloat
+shadow_value (Context *ctx,
+ const Shadow *shadow,
+ gint fy)
+{
+ if (ctx->options.style == GEGL_LONG_SHADOW_STYLE_FINITE ||
+ ! shadow->value)
+ {
+ return shadow->value;
+ }
+ else
+ {
+ return shadow->value * (1.0f - ctx->fade_rate * (fy - shadow->fy));
+ }
+}
+
static inline gboolean
shift_pixel (Context *ctx,
Pixel *pixel)
@@ -602,30 +696,75 @@ static void
trim_shadow (Context *ctx,
gint fy)
{
+ if (fy <= ctx->roi.y && ! is_fading (&ctx->options))
+ return;
+
if (ctx->active_u0 >= ctx->active_u1)
return;
switch (ctx->options.style)
{
case GEGL_LONG_SHADOW_STYLE_FINITE:
+ case GEGL_LONG_SHADOW_STYLE_FADING_FIXED_LENGTH:
{
- Pixel *p = ctx->screen;
- gint active_u0 = ctx->u1;
- gint active_u1 = ctx->u0;
+ Pixel *p = ctx->screen;
+ gint active_u0 = ctx->u1;
+ gint active_u1 = ctx->u0;
+ gint fy_shadow_height = fy - ctx->shadow_height;
gint u;
- fy -= ctx->shadow_height;
-
for (u = ctx->active_u0; u < ctx->active_u1; u++)
{
Pixel *pixel = &p[u];
gboolean active = (pixel->shadow.value != 0.0);
- if (active && pixel->shadow.fy < fy)
+ if (active && pixel->shadow.fy < fy_shadow_height)
active = shift_pixel (ctx, pixel);
if (active)
{
+ if (ctx->options.style ==
+ GEGL_LONG_SHADOW_STYLE_FADING_FIXED_LENGTH &&
+ pixel->queue)
+ {
+ ShadowItem *item = pixel->queue->prev;
+ ShadowItem *last_item = item;
+ gfloat prev_value;
+
+ prev_value = shadow_value (ctx, &item->shadow, fy);
+
+ item = item->prev;
+
+ while (item != last_item)
+ {
+ ShadowItem *prev = item->prev;
+ gfloat value;
+
+ value = shadow_value (ctx, &item->shadow, fy);
+
+ if (value <= prev_value)
+ {
+ if (item->prev != last_item)
+ item->prev->next = item->next;
+ else
+ pixel->queue = item->next;
+
+ item->next->prev = item->prev;
+
+ g_slice_free (ShadowItem, item);
+ }
+ else
+ {
+ prev_value = value;
+ }
+
+ item = prev;
+ }
+
+ if (shadow_value (ctx, &pixel->shadow, fy) <= prev_value)
+ shift_pixel (ctx, pixel);
+ }
+
active_u0 = MIN (active_u0, u);
active_u1 = u + 1;
}
@@ -645,9 +784,42 @@ trim_shadow (Context *ctx,
for (u = ctx->active_u0; u < ctx->active_u1; u++)
{
+ if (! p[u])
+ continue;
+
p[u] *= ctx->fade_rate;
- if (p[u] < EPSILON)
+ if (p[u] <= EPSILON)
+ {
+ p[u] = 0.0f;
+ }
+ else
+ {
+ 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_FIXED_RATE:
+ {
+ gfloat *p = ctx->screen;
+ gint active_u0 = ctx->u1;
+ gint active_u1 = ctx->u0;
+ gint u;
+
+ for (u = ctx->active_u0; u < ctx->active_u1; u++)
+ {
+ if (! p[u])
+ continue;
+
+ p[u] -= ctx->fade_rate;
+
+ if (p[u] <= EPSILON)
{
p[u] = 0.0f;
}
@@ -663,7 +835,7 @@ trim_shadow (Context *ctx,
}
break;
- default:
+ case GEGL_LONG_SHADOW_STYLE_INFINITE:
break;
}
}
@@ -683,7 +855,7 @@ add_shadow (Context *ctx,
u0 = MAX (u0, ctx->u0);
u1 = MIN (u1, ctx->u1);
- if (ctx->options.style == GEGL_LONG_SHADOW_STYLE_FINITE)
+ if (ctx->pixel_size == sizeof (Pixel))
{
Pixel *p = ctx->screen;
@@ -691,7 +863,7 @@ add_shadow (Context *ctx,
{
Pixel *pixel = &p[u];
- if (value >= pixel->shadow.value)
+ if (value >= shadow_value (ctx, &pixel->shadow, fy))
{
pixel->shadow.value = value;
pixel->shadow.fy = fy;
@@ -721,7 +893,7 @@ add_shadow (Context *ctx,
do
{
- if (value < item->shadow.value)
+ if (value < shadow_value (ctx, &item->shadow, fy))
break;
g_slice_free (ShadowItem, new_item);
@@ -780,17 +952,18 @@ get_pixel_shadow (Context *ctx,
gconstpointer pixel,
gint fy)
{
- if (ctx->options.style == GEGL_LONG_SHADOW_STYLE_FINITE)
+ if (ctx->pixel_size == sizeof (Pixel))
{
const Pixel *p = pixel;
- gfloat value = p->shadow.value;
+ gfloat value = shadow_value (ctx, &p->shadow, fy);
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;
+ value += (1.0 - ctx->shadow_remainder) *
+ shadow_value (ctx, &p->queue->shadow, fy);
}
return value;
@@ -962,7 +1135,7 @@ get_required_for_output (GeglOperation *operation,
GeglProperties *o = GEGL_PROPERTIES (operation);
GeglRectangle result = {};
- if (o->style == GEGL_LONG_SHADOW_STYLE_FINITE)
+ if (is_finite (o))
{
Context ctx;
@@ -994,7 +1167,7 @@ get_invalidated_by_change (GeglOperation *operation,
GeglProperties *o = GEGL_PROPERTIES (operation);
GeglRectangle result = {};
- if (o->style == GEGL_LONG_SHADOW_STYLE_FINITE)
+ if (is_finite (o))
{
Context ctx;
gint u1;
@@ -1044,7 +1217,7 @@ get_bounding_box (GeglOperation *operation)
{
GeglProperties *o = GEGL_PROPERTIES (operation);
- if (o->style == GEGL_LONG_SHADOW_STYLE_FINITE)
+ if (is_finite (o))
result = get_invalidated_by_change (operation, "input", in_rect);
else
result = *in_rect;
@@ -1059,7 +1232,7 @@ get_cached_region (GeglOperation *operation,
{
GeglProperties *o = GEGL_PROPERTIES (operation);
- if (o->style == GEGL_LONG_SHADOW_STYLE_FINITE)
+ if (is_finite (o))
return *roi;
else
return get_bounding_box (operation);
@@ -1077,6 +1250,7 @@ process (GeglOperation *operation,
init_options (&ctx, GEGL_PROPERTIES (operation), level);
init_geometry (&ctx);
+ init_fade (&ctx);
init_area (&ctx, operation, roi);
init_screen (&ctx);
init_buffers (&ctx, input, output);
@@ -1091,8 +1265,7 @@ process (GeglOperation *operation,
get_row (&ctx, fy);
- if (fy > ctx.roi.y)
- trim_shadow (&ctx, fy);
+ trim_shadow (&ctx, fy);
input_pixel = ctx.input_row0 +
(ctx.row_fx0 - ctx.area.x) * ctx.row_step;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]