[gegl] operations: Port apply-lens filter from GIMP
- From: Daniel Sabo <daniels src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl] operations: Port apply-lens filter from GIMP
- Date: Tue, 18 Feb 2014 15:03:12 +0000 (UTC)
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]