[gegl] operations: Port apply-lens filter from GIMP



commit 776a96b8ac090727259159aec6fff3cf9141ee40
Author: Pascal Giessler <pascal giessler student kit edu>
Date:   Mon Feb 17 18:56:16 2014 +0100

    operations: Port apply-lens filter from GIMP

 operations/common/Makefile.am  |    1 +
 operations/common/apply-lens.c |  308 ++++++++++++++++++++++++++++++++++++++++
 po/POTFILES.in                 |    1 +
 3 files changed, 310 insertions(+), 0 deletions(-)
---
diff --git a/operations/common/Makefile.am b/operations/common/Makefile.am
index d28c60b..17fd937 100644
--- a/operations/common/Makefile.am
+++ b/operations/common/Makefile.am
@@ -12,6 +12,7 @@ opdir = $(ext_dir)
 op_LTLIBRARIES = \
        alien-map.la \
        antialias.la \
+       apply-lens.la \
        bilateral-filter-fast.la \
        bilateral-filter.la \
        box-blur.la \
diff --git a/operations/common/apply-lens.c b/operations/common/apply-lens.c
new file mode 100644
index 0000000..9f9b38a
--- /dev/null
+++ b/operations/common/apply-lens.c
@@ -0,0 +1,308 @@
+/* This file is an image processing operation for GEGL
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This operation is a port of the GIMP Apply lens plug-in
+ * Copyright (C) 1997 Morten Eriksen mortene pvv ntnu no
+ *
+ * Porting to GEGL:
+ * Copyright 2013 Emanuel Schrade <emanuel schrade student kit edu>
+ * Copyright 2013 Stephan Seifermann <stephan seifermann student kit edu>
+ * Copyright 2013 Bastian Pirk <bastian pirk student kit edu>
+ * Copyright 2013 Pascal Giessler <pascal giessler student kit edu>
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+#ifdef GEGL_CHANT_PROPERTIES
+
+gegl_chant_double (refraction_index, _("Lens refraction index"),
+                   1.0, 100.0, 1.7,
+                   _("Lens refraction index"))
+
+gegl_chant_boolean (keep_surroundings, _("Keep original surroundings"),
+                    FALSE,
+                    _("Keep image unchanged, where not affected by the lens."))
+
+gegl_chant_color (background_color, _("Background Color"),
+                  "none",
+                  _("Background Color (defaults to 'none')"))
+#else
+
+#define GEGL_CHANT_TYPE_FILTER
+#define GEGL_CHANT_C_FILE "apply-lens.c"
+
+#include "gegl-chant.h"
+#include <math.h>
+#include <stdio.h>
+
+/**
+ * Computes the projected position (projx, projy) of the
+ * original point (x, y) after passing through the lens
+ * which is given by its center and its refraction index.
+ * See: Ellipsoid formula: x^2/a^2 + y^2/b^2 + z^2/c^2 = 1.
+ *
+ * @param a2 semiaxis a
+ * @param b2 semiaxis b
+ * @param c2 semiaxis c
+ * @param x coordinate x
+ * @param y coordinate y
+ * @param refraction refraction index
+ * @param projy inout of the projection x
+ * @param projy inout of the projection y
+ */
+static void
+find_projected_pos (gfloat  a2,
+                    gfloat  b2,
+                    gfloat  c2,
+                    gfloat  x,
+                    gfloat  y,
+                    gfloat  refraction,
+                    gfloat *projx,
+                    gfloat *projy)
+{
+  gfloat z;
+  gfloat nxangle, nyangle, theta1, theta2;
+  gfloat ri1 = 1.0;
+  gfloat ri2 = refraction;
+
+  z = sqrt ((1 - x * x / a2 - y * y / b2) * c2);
+
+  nxangle = acos (x / sqrt(x * x + z * z));
+  theta1 = G_PI / 2 - nxangle;
+  theta2 = asin (sin (theta1) * ri1 / ri2);
+  theta2 = G_PI / 2 - nxangle - theta2;
+  *projx = x - tan (theta2) * z;
+
+  nyangle = acos (y / sqrt (y * y + z * z));
+  theta1 = G_PI / 2 - nyangle;
+  theta2 = asin (sin (theta1) * ri1 / ri2);
+  theta2 = G_PI / 2 - nyangle - theta2;
+  *projy = y - tan (theta2) * z;
+}
+
+/**
+ * Prepare function of gegl filter.
+ * @param operation given Gegl operation
+ */
+static void
+prepare (GeglOperation *operation)
+{
+  const Babl *format = gegl_operation_get_source_format (operation, "input");
+
+  gegl_operation_set_format (operation, "input", format);
+  gegl_operation_set_format (operation, "output", format);
+}
+
+/**
+ * Returns the cached region. This is an area filter, which acts on the whole image.
+ * @param operation given Gegl operation
+ * @param roi the rectangle of interest
+ * @return result the new rectangle
+ */
+static GeglRectangle
+get_cached_region (GeglOperation       *operation,
+                   const GeglRectangle *roi)
+{
+  GeglRectangle result = *gegl_operation_source_get_bounding_box (operation, "input");
+  return result;
+}
+
+/**
+ * Process the gegl filter
+ * @param operation the given Gegl operation
+ * @param input the input buffer.
+ * @param output the output buffer.
+ * @param result the region of interest.
+ * @param level the level of detail
+ * @return True, if the filter was successfull applied.
+ */
+static gboolean
+process (GeglOperation       *operation,
+         GeglBuffer          *input,
+         GeglBuffer          *output,
+         const GeglRectangle *result,
+         gint                 level)
+{
+  GeglChantO  *o = GEGL_CHANT_PROPERTIES (operation);
+  const Babl  *input_format = gegl_buffer_get_format (input);
+  const int bytes_per_pixel = babl_format_get_bytes_per_pixel (input_format);
+
+  /**
+   * src_buf, dst_buf:
+   * Input- and output-buffers, containing the whole selection,
+   * the filter should be applied on.
+   *
+   * src_pixel, dst_pixel:
+   * pointers to the current source- and destination-pixel.
+   * Using these pointers, the reading and writing can be delayed till the
+   * end of the loop. Hence src_pixel can also point to background_color if
+   * necessary.
+   */
+  guint8    *src_buf, *dst_buf, *src_pixel, *dst_pixel, background_color[bytes_per_pixel];
+
+  /**
+   * (x, y): Position of the pixel we compute at the moment.
+   * (width, height): Dimensions of the lens
+   * pixel_offset: For calculating the pixels position in src_buf / dst_buf.
+   */
+  gint         x, y,
+               width, height,
+               pixel_offset, projected_pixel_offset;
+
+  /**
+   * Further parameters that are needed to calculate the position of a projected
+   * further pixel at (x, y).
+   */
+  gfloat       a, b, c,
+               asqr, bsqr, csqr,
+               dx, dy, dysqr,
+               projected_x, projected_y,
+               refraction_index;
+
+  gboolean     keep_surroundings;
+
+  width = result->width;
+  height = result->height;
+
+  refraction_index = o->refraction_index;
+  keep_surroundings = o->keep_surroundings;
+  gegl_color_get_pixel (o->background_color, input_format, background_color);
+
+  a = 0.5 * width;
+  b = 0.5 * height;
+  c = MIN (a, b);
+  asqr = a * a;
+  bsqr = b * b;
+  csqr = c * c;
+
+  /**
+   * Todo: We might want to change the buffers sizes, as the memory consumption
+   * could be rather large.However, due to the lens, it might happen, that one pixel from the
+   * images center gets stretched to the whole image, so we will still need
+   * at least a src_buf of size (width/2) * (height/2) * 4 to be able to
+   * process one quarter of the image.
+   */
+  src_buf = gegl_malloc (width * height * bytes_per_pixel);
+  dst_buf = gegl_malloc (width * height * bytes_per_pixel);
+
+  gegl_buffer_get (input, result, 1.0, input_format, src_buf,
+                   GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+  for (y = 0; y < height; y++)
+    {
+      /* dy is the current pixels distance (in y-direction) of the lenses center */
+      dy = -((gfloat)y - b + 0.5);
+
+      /**
+       * To determine, whether the pixel is within the elliptical region affected
+       * by the lens, we furthermore need the squared distance. So we calculate it
+       * once for each row.
+       */
+      dysqr = dy * dy;
+      for (x = 0; x < width; x++)
+        {
+          /* Again we need to calculate the pixels distance from the lens dx. */
+          dx = (gfloat)x - a + 0.5;
+
+          /* Given x and y we can now determine the pixels offset in our buffer. */
+          pixel_offset = (x + y * width) * bytes_per_pixel;
+
+          /* As described above, we only read and write image data once. */
+          dst_pixel = &dst_buf[pixel_offset];
+
+          if (dysqr < (bsqr - (bsqr * dx * dx) / asqr))
+            {
+              /**
+               * If (x, y) is inside the affected region, we can find its projected
+               * position, calculate the projected_pixel_offset and set the src_pixel
+               * to where we want to copy the pixel-data from.
+               */
+              find_projected_pos (asqr, bsqr, csqr, dx, dy, refraction_index,
+                                  &projected_x, &projected_y);
+
+              projected_pixel_offset = ( (gint)(-projected_y + b) * width +
+                                         (gint)( projected_x + a) ) * bytes_per_pixel;
+
+              src_pixel = &src_buf[projected_pixel_offset];
+            }
+          else
+            {
+              /**
+               * Otherwise (that is for pixels outside the lens), we could either leave
+               * the image data unchanged, or set it to a specified 'background_color',
+               * depending on the user input.
+               */
+              if (keep_surroundings)
+                src_pixel = &src_buf[pixel_offset];
+              else
+                src_pixel = background_color;
+            }
+
+          /**
+           * At the end, we can copy the src_pixel (which was determined above), to
+           * dst_pixel.
+           */
+          memcpy (dst_pixel, src_pixel, bytes_per_pixel);
+        }
+  }
+
+  gegl_buffer_set (output, result, 1.0, gegl_buffer_get_format (output), dst_buf,
+                   GEGL_AUTO_ROWSTRIDE);
+
+  gegl_free (dst_buf);
+  gegl_free (src_buf);
+
+  return TRUE;
+}
+
+static void
+gegl_chant_class_init (GeglChantClass *klass)
+{
+  GeglOperationClass       *operation_class;
+  GeglOperationFilterClass *filter_class;
+  gchar                    *composition =
+    "<?xml version='1.0' encoding='UTF-8'?>"
+    "<gegl>"
+    "<node operation='gegl:apply-lens'>"
+    "  <params>"
+    "    <param name='refraction_index'>1.7</param>"
+    "    <param name='keep_surroundings'>false</param>"
+    "    <param name='background_color'>rgba(0, 0.50196, 0.50196, 0.75)</param>"
+    "  </params>"
+    "</node>"
+    "<node operation='gegl:load'>"
+    "  <params>"
+    "    <param name='path'>standard-input.png</param>"
+    "  </params>"
+    "</node>"
+    "</gegl>";
+
+  operation_class = GEGL_OPERATION_CLASS (klass);
+  filter_class    = GEGL_OPERATION_FILTER_CLASS (klass);
+
+  operation_class->prepare                 = prepare;
+  operation_class->get_cached_region       = get_cached_region;
+  filter_class->process                    = process;
+
+  gegl_operation_class_set_keys (operation_class,
+    "name",        "gegl:apply-lens",
+    "categories",  "Distorts",
+    "description", _("After applying this filter, a part of the active layer is rendered through a spherical 
lens."),
+    "reference-composition", composition,
+    NULL);
+}
+
+#endif
diff --git a/po/POTFILES.in b/po/POTFILES.in
index ce317b3..4f2c26a 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -5,6 +5,7 @@ gegl/gegl-init.c
 gegl/module/geglmodule.c
 operations/common/alien-map.c
 operations/common/antialias.c
+operations/common/apply-lens.c
 operations/common/bilateral-filter.c
 operations/common/bilateral-filter-fast.c
 operations/common/box-blur.c


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]