[gegl] lens-blur: new operation in common-cxx
- From: Ell <ell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl] lens-blur: new operation in common-cxx
- Date: Tue, 2 Jun 2020 20:21:45 +0000 (UTC)
commit 59fd2d12be0535683830b0f2cb60f22e5a6b2af8
Author: Ell <ell_se yahoo com>
Date: Tue Jun 2 22:33:43 2020 +0300
lens-blur: new operation in common-cxx
gegl:lens-blur simulates an out-of-focus lens blur, currently
limited to a round aperture. The op allows increasing the
relative highlight strength, creating a bokeh effect.
The op can produce either a fixed-radius blur, or a varying-radius
blur based on a mask given through an aux input.
operations/common-cxx/lens-blur.cc | 512 +++++++++++++++++++++++++++++++++++++
operations/common-cxx/meson.build | 1 +
po/POTFILES.in | 1 +
3 files changed, 514 insertions(+)
---
diff --git a/operations/common-cxx/lens-blur.cc b/operations/common-cxx/lens-blur.cc
new file mode 100644
index 000000000..a3fc9e441
--- /dev/null
+++ b/operations/common-cxx/lens-blur.cc
@@ -0,0 +1,512 @@
+/* This file is an image processing operation for GEGL
+ *
+ * GEGL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2017 Ell
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+#ifdef GEGL_PROPERTIES
+
+property_double (radius, _("Radius"), 10.0)
+ description (_("Blur radius"))
+ value_range (0.0, G_MAXDOUBLE)
+ ui_range (0.0, 100.0)
+ ui_gamma (2.0)
+ ui_meta ("unit", "pixel-distance")
+
+property_double (highlight_factor, _("Highlight factor"), 0.0)
+ description (_("Relative highlight strength"))
+ value_range (0.0, 1.0)
+
+property_double (highlight_threshold_low, _("Highlight threshold (low)"), 0.0)
+ ui_range (0.0, 1.0)
+ ui_meta ("role", "range-start")
+ ui_meta ("unit", "luminance")
+ ui_meta ("label", "range-label")
+ ui_meta ("range-label", _("Highlight threshold"))
+
+property_double (highlight_threshold_high, _("Highlight threshold (high)"), 1.0)
+ ui_range (0.0, 1.0)
+ ui_meta ("role", "range-end")
+ ui_meta ("unit", "luminance")
+ ui_meta ("label", "$highlight-threshold-low.label")
+
+property_boolean (clip, _("Clip to input extents"), TRUE)
+ description (_("Clip output to the input extents"))
+
+property_boolean (linear_mask, _("Linear mask"), FALSE)
+ description (_("Use linear mask values"))
+
+#else
+
+#define GEGL_OP_COMPOSER
+#define GEGL_OP_NAME lens_blur
+#define GEGL_OP_C_SOURCE lens-blur.cc
+
+#include "gegl-op.h"
+
+static void
+prepare (GeglOperation *operation)
+{
+ GeglProperties *o = GEGL_PROPERTIES (operation);
+ const Babl *space;
+ const Babl *format;
+
+ space = gegl_operation_get_source_space (operation, "input");
+ format = babl_format_with_space ("RGBA float", space);
+
+ gegl_operation_set_format (operation, "input", format);
+ gegl_operation_set_format (operation, "output", format);
+
+ gegl_operation_set_format (operation, "aux",
+ babl_format_with_space (
+ o->linear_mask ? "Y float" : "Y' float",
+ gegl_operation_get_source_space (operation,
+ "aux")));
+
+ o->user_data = (gpointer) babl_fish (format,
+ babl_format_with_space ("Y float",
+ space));
+}
+
+static GeglRectangle
+get_bounding_box (GeglOperation *operation)
+{
+ GeglProperties *o = GEGL_PROPERTIES (operation);
+ const GeglRectangle *in_rect;
+ GeglRectangle result = {};
+
+ in_rect = gegl_operation_source_get_bounding_box (operation, "input");
+
+ if (in_rect)
+ {
+ result = *in_rect;
+
+ if (! o->clip)
+ {
+ gint iradius = floor (o->radius + 0.5);
+
+ result.x -= iradius;
+ result.y -= iradius;
+ result.width += 2 * iradius;
+ result.height += 2 * iradius;
+ }
+ }
+
+ return result;
+}
+
+static GeglRectangle
+get_required_for_output (GeglOperation *operation,
+ const gchar *input_pad,
+ const GeglRectangle *roi)
+{
+ GeglProperties *o = GEGL_PROPERTIES (operation);
+ GeglRectangle result = *roi;
+ gint iradius = floor (o->radius + 0.5);
+
+ result.x -= iradius;
+ result.y -= iradius;
+ result.width += 2 * iradius;
+ result.height += 2 * iradius;
+
+ return result;
+}
+
+static gboolean
+operation_process (GeglOperation *operation,
+ GeglOperationContext *context,
+ const gchar *output_prop,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GeglProperties *o = GEGL_PROPERTIES (operation);
+
+ if (o->radius < 0.5)
+ {
+ gegl_operation_context_set_object (
+ context, "output",
+ gegl_operation_context_get_object (context, "input"));
+
+ return TRUE;
+ }
+
+ return GEGL_OPERATION_CLASS (gegl_op_parent_class)->process (
+ operation, context, output_prop, roi, level);
+}
+
+static gboolean
+process (GeglOperation *operation,
+ GeglBuffer *input,
+ GeglBuffer *aux,
+ GeglBuffer *output,
+ const GeglRectangle *roi,
+ gint level)
+{
+ GeglProperties *o = GEGL_PROPERTIES (operation);
+ const Babl *format = gegl_operation_get_format (operation, "input");
+ const Babl *aux_format = gegl_operation_get_format (operation, "aux");
+ const Babl *weight_fish = (const Babl *) o->user_data;
+ GeglRectangle rect;
+ gfloat *in;
+ gfloat *in_w;
+ gfloat *out;
+ gfloat *out_w;
+ gfloat *mask = NULL;
+ gfloat highlight_threshold_low;
+ gfloat highlight_threshold_high;
+ gfloat highlight_factor;
+ gfloat highlight_max;
+ gfloat radius = o->radius;
+ gint iradius = floorf (radius + 0.5f);
+ gint size = 2 * iradius + 1;
+ gint y;
+
+ highlight_threshold_low = o->highlight_threshold_low;
+ highlight_threshold_high = o->highlight_threshold_high;
+ highlight_factor = 10.0 * o->highlight_factor * G_LN2;
+ highlight_max = expf (highlight_factor);
+
+ rect.x = roi->x - iradius;
+ rect.y = roi->y - iradius;
+ rect.width = roi->width + 2 * iradius;
+ rect.height = roi->height + 2 * iradius;
+
+ if (o->clip)
+ {
+ gegl_rectangle_intersect (
+ &rect,
+ &rect,
+ gegl_operation_source_get_bounding_box (operation, "input"));
+ }
+
+ size = MIN (size, rect.height);
+
+ in = (gfloat *) gegl_malloc (4 * sizeof (gfloat) * rect.width * size);
+ in_w = (gfloat *) gegl_malloc ( sizeof (gfloat) * rect.width * size);
+ out = (gfloat *) gegl_malloc (4 * sizeof (gfloat) * roi->width);
+ out_w = (gfloat *) gegl_malloc ( sizeof (gfloat) * roi->width);
+
+ if (aux)
+ mask = (gfloat *) gegl_malloc (sizeof (gfloat) * rect.width * size);
+
+ auto row_index = [&] (gint y)
+ {
+ return (y - rect.y) % size;
+ };
+
+ auto weight = [&] (gfloat v)
+ {
+ v = (v - highlight_threshold_low) /
+ (highlight_threshold_high - highlight_threshold_low);
+
+ if (v <= 0.0f)
+ return 1.0f;
+ else if (v >= 1.0f)
+ return highlight_max;
+
+ return expf (v * highlight_factor);
+ };
+
+ auto read = [&] (gint y,
+ gint height)
+ {
+ gfloat *row;
+ gfloat *row_w;
+ gfloat *row_m = NULL;
+ gint n = rect.width * height;
+ gint r_i;
+ gint i;
+
+ r_i = row_index (y);
+
+ row = in + 4 * rect.width * r_i;
+ row_w = in_w + rect.width * r_i;
+
+ gegl_buffer_get (input,
+ GEGL_RECTANGLE (rect.x, y, rect.width, height),
+ 1.0,
+ format,
+ row,
+ GEGL_AUTO_ROWSTRIDE,
+ GEGL_ABYSS_NONE);
+
+ if (mask)
+ {
+ row_m = mask + rect.width * r_i;
+
+ gegl_buffer_get (aux,
+ GEGL_RECTANGLE (rect.x, y, rect.width, height),
+ 1.0,
+ aux_format,
+ row_m,
+ GEGL_AUTO_ROWSTRIDE,
+ GEGL_ABYSS_NONE);
+ }
+
+ if (highlight_factor)
+ {
+ babl_process (weight_fish, row, row_w, n);
+ }
+ else
+ {
+ gfloat w = 1.0f;
+
+ gegl_memset_pattern (row_w, &w, sizeof (gfloat), n);
+ }
+
+ for (i = 0; i < n; i++)
+ {
+ gint c;
+
+ if (highlight_factor)
+ row_w[i] = weight (row_w[i]);
+
+ if (mask)
+ {
+ row_m[i] = row_m[i] * radius + 0.5f;
+ row_m[i] *= row_m[i];
+
+ row_w[i] /= G_PI * row_m[i];
+ }
+
+ row[4 * i + 3] *= row_w[i];
+
+ for (c = 0; c < 3; c++)
+ row[4 * i + c] *= row[4 * i + 3];
+ }
+ };
+
+ read (rect.y, MIN (roi->y + iradius + 1 - rect.y, rect.height));
+
+ for (y = roi->y; y < roi->y + roi->height; y++)
+ {
+ gint r1, r2;
+ gint x;
+
+ memset (out, 0, 4 * sizeof (gfloat) * roi->width);
+ memset (out_w, 0, sizeof (gfloat) * roi->width);
+
+ r1 = MAX (-iradius, - (y - rect.y));
+ r2 = MIN (+iradius, (rect.height - 1) - (y - rect.y));
+
+ if (! mask)
+ {
+ gint r;
+
+ for (r = r1; r <= r2; r++)
+ {
+ gfloat *o = out;
+ gfloat *o_w = out_w;
+ const gfloat *row;
+ const gfloat *row_w;
+ gfloat accum[4] = {};
+ gfloat accum_w = 0.0f;
+ gint r_i;
+ gint s;
+ gint x1, x2;
+
+ r_i = row_index (y + r);
+
+ row = in + 4 * (rect.width * r_i + (roi->x - rect.x));
+ row_w = in_w + (rect.width * r_i + (roi->x - rect.x));
+
+ s = sqrtf ((radius + 0.5f) * (radius + 0.5f) - r * r);
+
+ x1 = MAX (-s, - (roi->x - rect.x));
+ x2 = MIN (+s, (rect.width - 1) - (roi->x - rect.x));
+
+ for (x = x1; x <= x2; x++)
+ {
+ gint c;
+
+ for (c = 0; c < 4; c++)
+ accum[c] += row[4 * x + c];
+
+ accum_w += row_w[x];
+ }
+
+ x1 = - (roi->x - rect.x) + s;
+ x2 = (rect.width - 1) - (roi->x - rect.x) - s;
+
+ for (x = 0; x < roi->width; x++)
+ {
+ gint c;
+
+ for (c = 0; c < 4; c++)
+ o[c] += accum[c];
+
+ *o_w += accum_w;
+
+ o += 4;
+ o_w++;
+
+ if (x >= x1)
+ {
+ for (c = 0; c < 4; c++)
+ accum[c] -= row[4 * (-s) + c];
+
+ accum_w -= row_w[-s];
+ }
+
+ row += 4;
+ row_w++;
+
+ if (x <= x2)
+ {
+ for (c = 0; c < 4; c++)
+ accum[c] += row[4 * (+s) + c];
+
+ accum_w += row_w[+s];
+ }
+ }
+ }
+ }
+ else
+ {
+ gint r;
+
+ for (r = r1; r <= r2; r++)
+ {
+ const gfloat *row;
+ const gfloat *row_w;
+ const gfloat *row_m;
+ gfloat r2 = r * r;
+ gint r_i;
+ gint s;
+ gint x1, x2;
+
+ r_i = row_index (y + r);
+
+ row = in + 4 * (rect.width * r_i + (roi->x - rect.x));
+ row_w = in_w + (rect.width * r_i + (roi->x - rect.x));
+ row_m = mask + (rect.width * r_i + (roi->x - rect.x));
+
+ s = sqrtf ((radius + 0.5f) * (radius + 0.5f) - r * r);
+
+ x1 = MAX ( - s,
+ - (roi->x - rect.x));
+ x2 = MIN ((roi->width - 1) + s,
+ (rect.width - 1) - (roi->x - rect.x));
+
+ row += 4 * x1;
+ row_w += x1;
+ row_m += x1;
+
+ for (x = x1; x <= x2; x++, row += 4, row_w++, row_m++)
+ {
+ gint xl, xr;
+ gint c;
+
+ if (*row_m < r2)
+ continue;
+
+ s = sqrtf (*row_m - r2);
+
+ xl = x - s;
+ xr = x + s + 1;
+
+ xl = MAX (xl, 0);
+
+ if (xl >= xr || xl >= roi->width)
+ continue;
+
+ for (c = 0; c < 4; c++)
+ out[4 * xl + c] += row[c];
+
+ out_w[xl] += *row_w;
+
+ if (xr < roi->width)
+ {
+ for (c = 0; c < 4; c++)
+ out[4 * xr + c] -= row[c];
+
+ out_w[xr] -= *row_w;
+ }
+ }
+ }
+
+ for (x = 1; x < roi->width; x++)
+ {
+ gint c;
+
+ for (c = 0; c < 4; c++)
+ out[4 * x + c] += out[4 * (x - 1) + c];
+
+ out_w[x] += out_w[x - 1];
+ }
+ }
+
+ for (x = 0; x < roi->width; x++)
+ {
+ gint c;
+
+ for (c = 0; c < 3; c++)
+ out[4 * x + c] /= out[4 * x + 3];
+
+ out[4 * x + 3] /= out_w[x];
+ }
+
+ gegl_buffer_set (output,
+ GEGL_RECTANGLE (roi->x,
+ y,
+ roi->width,
+ 1),
+ 0,
+ format,
+ out,
+ GEGL_AUTO_ROWSTRIDE);
+
+ if ((y + 1) + iradius < rect.y + rect.height)
+ read ((y + 1) + iradius, 1);
+ }
+
+ gegl_free (mask);
+ gegl_free (out_w);
+ gegl_free (out);
+ gegl_free (in_w);
+ gegl_free (in);
+
+ return TRUE;
+}
+
+static void
+gegl_op_class_init (GeglOpClass *klass)
+{
+ GeglOperationClass *operation_class;
+ GeglOperationComposerClass *composer_class;
+
+ operation_class = GEGL_OPERATION_CLASS (klass);
+ composer_class = GEGL_OPERATION_COMPOSER_CLASS (klass);
+
+ operation_class->prepare = prepare;
+ operation_class->get_bounding_box = get_bounding_box;
+ operation_class->get_required_for_output = get_required_for_output;
+ operation_class->get_invalidated_by_change = get_required_for_output;
+ operation_class->process = operation_process;
+
+ composer_class->process = process;
+
+ gegl_operation_class_set_keys (operation_class,
+ "name", "gegl:lens-blur",
+ "title", _("Lens Blur"),
+ "categories", "blur",
+ "description", _("Simulate out-of-focus lens blur"),
+ NULL);
+}
+
+#endif
diff --git a/operations/common-cxx/meson.build b/operations/common-cxx/meson.build
index fbbb0b120..4340c8a13 100644
--- a/operations/common-cxx/meson.build
+++ b/operations/common-cxx/meson.build
@@ -2,6 +2,7 @@
gegl_common_cxx_sources = files(
'distance-transform.cc',
'focus-blur.c',
+ 'lens-blur.cc',
'piecewise-blend.cc',
'variable-blur.c',
'warp.cc',
diff --git a/po/POTFILES.in b/po/POTFILES.in
index f3f0eb531..ffe9c341a 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -128,6 +128,7 @@ operations/common/weighted-blend.c
operations/common/write-buffer.c
operations/common-cxx/distance-transform.cc
operations/common-cxx/focus-blur.c
+operations/common-cxx/lens-blur.cc
operations/common-cxx/piecewise-blend.cc
operations/common-cxx/variable-blur.c
operations/common-cxx/warp.cc
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]