[gegl] workshop: Add a gegl operation for GIMP's "Whirl and Pinch" plugin
- From: Barak Itkin <barakitkin src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl] workshop: Add a gegl operation for GIMP's "Whirl and Pinch" plugin
- Date: Sat, 6 Nov 2010 10:44:50 +0000 (UTC)
commit ac8fe1d697a8639bffdec8e5ad5d2e832771fbcd
Author: Barak Itkin <lightningismyname gmail com>
Date: Sat Nov 6 12:33:26 2010 +0200
workshop: Add a gegl operation for GIMP's "Whirl and Pinch" plugin
operations/workshop/whirl-pinch.c | 252 +++++++++++++++++++++++++++++++++++++
1 files changed, 252 insertions(+), 0 deletions(-)
---
diff --git a/operations/workshop/whirl-pinch.c b/operations/workshop/whirl-pinch.c
new file mode 100644
index 0000000..8f3760c
--- /dev/null
+++ b/operations/workshop/whirl-pinch.c
@@ -0,0 +1,252 @@
+/* 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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2010 Barak Itkin <lightningismyname gmail org>
+ *
+ * Based on "Whirl and Pinch" GIMP plugin
+ * Copyright (C) 1997 Federico Mena Quintero
+ * Copyright (C) 1997 Scott Goehring
+ *
+ * The workshop/mirrors.c operation by Alexia Death and �yvind Kolås
+ * was used as a template for this op file.
+ */
+
+/* TODO: Find some better algorithm to calculate the roi for each dest
+ * rectangle. Right now it simply asks for the entire image...
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+
+#ifdef GEGL_CHANT_PROPERTIES
+
+gegl_chant_double (whirl, _("Whirl"), -G_MAXDOUBLE, G_MAXDOUBLE, 90, _("Whirl angle (degrees)"))
+
+gegl_chant_double (pinch, _("Pinch"), -1, 1, 0, _("Pinch amount"))
+
+gegl_chant_double (radius, _("Radius"), 0, 2, 1, _("Radius (1.0 is the largest circle that fits in the image, and 2.0 goes all the way to the corners)"))
+
+#else
+
+#define GEGL_CHANT_TYPE_FILTER
+#define GEGL_CHANT_C_FILE "whirl-pinch.c"
+
+#include "gegl-chant.h"
+#include <math.h>
+
+/* This function is a slightly modified version from the one in the original plugin */
+static gboolean
+calc_undistorted_coords (gdouble wx, gdouble wy,
+ gdouble cen_x, gdouble cen_y,
+ gdouble scale_x, gdouble scale_y,
+ gdouble whirl, gdouble pinch, gdouble wpradius,
+ gdouble *x, gdouble *y)
+{
+ gdouble dx, dy;
+ gdouble d, factor;
+ gdouble dist;
+ gdouble ang, sina, cosa;
+ gboolean inside;
+
+ gdouble radius = MAX(cen_x, cen_y);
+ gdouble radius2 = radius * radius * wpradius;
+ /* Distances to center, scaled */
+
+ dx = (wx - cen_x) * scale_x;
+ dy = (wy - cen_y) * scale_y;
+
+ /* Distance^2 to center of *circle* (scaled ellipse) */
+
+ d = dx * dx + dy * dy;
+
+ /* If we are inside circle, then distort.
+ * Else, just return the same position
+ */
+
+ inside = (d < radius2);
+
+ /* If d is 0, then we are exactly at the center, so the transform has
+ * no effect. The original version of the gimp plugin simply created
+ * an offset of the original points to avoid this issue, but that is
+ * less accurate...
+ */
+ if (inside && d > 0)
+ {
+ dist = sqrt(d / wpradius) / radius;
+
+ /* Pinch */
+
+ factor = pow (sin (G_PI_2 * dist), -pinch);
+
+ dx *= factor;
+ dy *= factor;
+
+ /* Whirl */
+
+ factor = 1.0 - dist;
+
+ ang = whirl * factor * factor;
+
+ sina = sin (ang);
+ cosa = cos (ang);
+
+ *x = (cosa * dx - sina * dy) / scale_x + cen_x;
+ *y = (sina * dx + cosa * dy) / scale_y + cen_y;
+ }
+ else
+ {
+ *x = wx;
+ *y = wy;
+ }
+
+ return inside;
+}
+
+/* Apply the actual transform */
+
+static void
+apply_whirl_pinch (gdouble whirl, gdouble pinch, gdouble radius,
+ gdouble cen_x, gdouble cen_y,
+ Babl *format,
+ GeglBuffer *src,
+ GeglRectangle *in_boundary,
+ GeglBuffer *dst,
+ GeglRectangle *boundary,
+ const GeglRectangle *roi)
+{
+ gfloat *dst_buf;
+ gint row, col;
+ gdouble scale_x, scale_y;
+ gdouble cx, cy;
+
+ /* Get buffer in which to place dst pixels. */
+ dst_buf = g_new0 (gfloat, roi->width * roi->height * 4);
+
+ whirl = whirl * G_PI / 180;
+
+ scale_x = 1.0;
+ scale_y = roi->width / (gdouble) roi->height;
+
+ for (row = 0; row < roi->height; row++) {
+ for (col = 0; col < roi->width; col++) {
+
+ calc_undistorted_coords (roi->x + col, roi->y + row,
+ cen_x, cen_y,
+ scale_x, scale_y,
+ whirl, pinch, radius,
+ &cx, &cy);
+
+ gegl_buffer_sample (src, cx, cy, 1.0, &dst_buf[(row * roi->width + col) * 4], format, GEGL_INTERPOLATION_LINEAR);
+ } /* for */
+ } /* for */
+
+ gegl_buffer_sample_cleanup (src);
+
+ /* Store dst pixels. */
+ gegl_buffer_set (dst, roi, format, dst_buf, GEGL_AUTO_ROWSTRIDE);
+
+ gegl_buffer_flush(dst);
+
+ g_free (dst_buf);
+
+}
+
+/*****************************************************************************/
+
+/* Compute the region for which this operation is defined.
+ */
+static GeglRectangle
+get_bounding_box (GeglOperation *operation)
+{
+ GeglRectangle result = {0,0,0,0};
+ GeglRectangle *in_rect = gegl_operation_source_get_bounding_box (operation, "input");
+
+ if (!in_rect){
+ return result;
+ }
+ else
+ return *in_rect;
+}
+
+/* Compute the input rectangle required to compute the specified region of interest (roi).
+ */
+static GeglRectangle
+get_required_for_output (GeglOperation *operation,
+ const gchar *input_pad,
+ const GeglRectangle *roi)
+{
+ GeglRectangle *result = gegl_operation_source_get_bounding_box (operation, "input");
+
+ return *result;
+}
+
+/* Specify the input and output buffer formats.
+ */
+static void
+prepare (GeglOperation *operation)
+{
+ gegl_operation_set_format (operation, "input", babl_format ("RaGaBaA float"));
+ gegl_operation_set_format (operation, "output", babl_format ("RaGaBaA float"));
+}
+
+/* Perform the specified operation.
+ */
+static gboolean
+process (GeglOperation *operation,
+ GeglBuffer *input,
+ GeglBuffer *output,
+ const GeglRectangle *result)
+{
+ GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
+ GeglRectangle boundary = gegl_operation_get_bounding_box (operation);
+ Babl *format = babl_format ("RaGaBaA float");
+
+ apply_whirl_pinch (o->whirl,
+ o->pinch,
+ o->radius,
+ boundary.width / 2.0,
+ boundary.height / 2.0,
+ format,
+ input,
+ &boundary,
+ output,
+ &boundary,
+ result);
+ return TRUE;
+}
+
+
+static void
+gegl_chant_class_init (GeglChantClass *klass)
+{
+ GeglOperationClass *operation_class;
+ GeglOperationFilterClass *filter_class;
+
+ operation_class = GEGL_OPERATION_CLASS (klass);
+ filter_class = GEGL_OPERATION_FILTER_CLASS (klass);
+
+ filter_class->process = process;
+ operation_class->prepare = prepare;
+ operation_class->get_bounding_box = get_bounding_box;
+ operation_class->get_required_for_output = get_required_for_output;
+
+ operation_class->name = "gegl:whirl-pinch";
+ operation_class->categories = "distort";
+ operation_class->description =
+ _("Applies whirling and pinching on the image");
+}
+
+#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]