[gegl] workshop: Add a gegl operation for GIMP's "Whirl and Pinch" plugin



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]