[gegl/soc-2011-warp] add a wip operator warp in the workshop, and dedicated enum in gegl-types
- From: Michael Murà <mmure src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl/soc-2011-warp] add a wip operator warp in the workshop, and dedicated enum in gegl-types
- Date: Sat, 9 Jul 2011 17:17:13 +0000 (UTC)
commit 502b757af4547e283eb6bea52ed4415eae7b4928
Author: Michael Murà <mure michael gmail com>
Date: Sat Jul 9 19:15:54 2011 +0200
add a wip operator warp in the workshop, and dedicated enum in gegl-types
gegl/gegl-types.c | 20 +++
gegl/gegl-types.h | 12 ++
operations/workshop/warp.c | 368 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 400 insertions(+), 0 deletions(-)
---
diff --git a/gegl/gegl-types.c b/gegl/gegl-types.c
index 1bf2d39..0292ed2 100644
--- a/gegl/gegl-types.c
+++ b/gegl/gegl-types.c
@@ -37,3 +37,23 @@ gegl_sampler_type_get_type (void)
}
return etype;
}
+
+GType
+gegl_warp_behavior_get_type (void)
+{
+ static GType etype = 0;
+ if (etype == 0) {
+ static const GEnumValue values[] = {
+ { GEGL_WARP_BEHAVIOR_MOVE, "Move pixels", "move" },
+ { GEGL_WARP_BEHAVIOR_GROW, "Grow area", "grow" },
+ { GEGL_WARP_BEHAVIOR_SHRINK, "Shrink area", "shrink" },
+ { GEGL_WARP_BEHAVIOR_SWIRL_CW, "Swirl clockwise", "swirl_cw" },
+ { GEGL_WARP_BEHAVIOR_SWIRL_CCW, "Swirl counter-clockwise", "swirl_ccw" },
+ { GEGL_WARP_BEHAVIOR_ERASE, "Erase warping", "erase" },
+ { GEGL_WARP_BEHAVIOR_SMOOTH, "Smooth warping", "smooth" },
+ { 0, NULL, NULL }
+ };
+ etype = g_enum_register_static ("GeglWarpBehaviorType", values);
+ }
+ return etype;
+}
diff --git a/gegl/gegl-types.h b/gegl/gegl-types.h
index 6e1f594..da6456f 100644
--- a/gegl/gegl-types.h
+++ b/gegl/gegl-types.h
@@ -74,6 +74,18 @@ typedef enum {
GType gegl_sampler_type_get_type (void) G_GNUC_CONST;
#define GEGL_TYPE_SAMPLER_TYPE (gegl_sampler_type_get_type())
+typedef enum
+{
+ GEGL_WARP_BEHAVIOR_MOVE,
+ GEGL_WARP_BEHAVIOR_GROW,
+ GEGL_WARP_BEHAVIOR_SHRINK,
+ GEGL_WARP_BEHAVIOR_SWIRL_CW,
+ GEGL_WARP_BEHAVIOR_SWIRL_CCW,
+ GEGL_WARP_BEHAVIOR_ERASE,
+ GEGL_WARP_BEHAVIOR_SMOOTH
+} GeglWarpBehavior;
+GType gegl_warp_behavior_get_type (void) G_GNUC_CONST;
+#define GEGL_TYPE_WARP_BEHAVIOR (gegl_warp_behavior_get_type ())
G_END_DECLS
diff --git a/operations/workshop/warp.c b/operations/workshop/warp.c
new file mode 100644
index 0000000..1a06487
--- /dev/null
+++ b/operations/workshop/warp.c
@@ -0,0 +1,368 @@
+/* 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 2011 Michael Murà <batolettre gmail com>
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+#include <math.h>
+#include <stdio.h> /* for test only */
+
+#ifdef GEGL_CHANT_PROPERTIES
+
+gegl_chant_double (strength, _("Strength"), 0.0, 100.0, 1.0,
+ _("Effect Strength"))
+gegl_chant_double (size, _("Size"), 1.0, 10000.0, 40.0,
+ _("Effect Size"))
+gegl_chant_double (hardness, _("Harness"), 0.0, 1.0, 0.5,
+ _("Effect Harness"))
+gegl_chant_path (stroke, _("Stroke"), _("Effect Strength"))
+gegl_chant_enum (behavior, _("Behavior"), GeglWarpBehavior, GEGL_TYPE_WARP_BEHAVIOR,
+ GEGL_WARP_BEHAVIOR_MOVE, _("Behavior of the op"))
+
+#else
+
+#define GEGL_CHANT_TYPE_FILTER
+#define GEGL_CHANT_C_FILE "warp.c"
+
+#include "gegl-plugin.h"
+#include "gegl-path.h"
+static void path_changed (GeglPath *path,
+ const GeglRectangle *roi,
+ gpointer userdata);
+#include "gegl-chant.h"
+
+#ifdef HAVE_RINT
+#define RINT(x) rint(x)
+#else
+#define RINT(x) floor ((x) + 0.5)
+#endif
+
+typedef struct {
+ gfloat *lookup;
+ GeglBuffer *buffer;
+ gdouble last_x;
+ gdouble last_y;
+ gboolean last_point_set;
+} warp_private;
+
+static void
+path_changed (GeglPath *path,
+ const GeglRectangle *roi,
+ gpointer userdata)
+{
+ GeglRectangle rect = *roi;
+ GeglChantO *o = GEGL_CHANT_PROPERTIES (userdata);
+ /* invalidate the incoming rectangle */
+
+ rect.x -= o->size/2;
+ rect.y -= o->size/2;
+ rect.width += o->size;
+ rect.height += o->size;
+
+ gegl_operation_invalidate (userdata, &rect, FALSE);
+}
+
+static void
+prepare (GeglOperation *operation)
+{
+ GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
+ warp_private *priv;
+
+ Babl *format = babl_format_n (babl_type ("float"), 2);
+ gegl_operation_set_format (operation, "input", format);
+ gegl_operation_set_format (operation, "output", format);
+
+ if (!o->chant_data)
+ {
+ o->chant_data = g_slice_new (warp_private);
+ }
+
+ priv = (warp_private*) o->chant_data;
+ priv->last_point_set = FALSE;
+ priv->lookup = NULL
+ priv->buffer = NULL;
+}
+
+static void
+finalize (GObject *object)
+{
+ GeglChantO *o = GEGL_CHANT_PROPERTIES (object);
+
+ if (o->chant_data)
+ {
+ g_slice_free (warp_private, o->chant_data);
+ o->chant_data = NULL;
+ }
+
+ G_OBJECT_CLASS (gegl_chant_parent_class)->finalize (object);
+}
+
+static gdouble
+gauss (gdouble f)
+{
+ /* This is not a real gauss function. */
+ /* Approximation is valid if -1 < f < 1 */
+ if (f < -0.5)
+ {
+ f = -1.0 - f;
+ return (2.0 * f*f);
+ }
+
+ if (f < 0.5)
+ return (1.0 - 2.0 * f*f);
+
+ f = 1.0 - f;
+ return (2.0 * f*f);
+}
+
+/* set up lookup table */
+static void
+calc_lut (GeglChantO *o)
+{
+ warp_private *priv = (warp_private*) o->chant_data;
+ gint length;
+ gint x;
+ gdouble exponent;
+
+ length = ceil (0.5 * o->size + 1.0);
+
+ priv->lookup = g_malloc (length * sizeof (gfloat));
+
+ if ((1.0 - o->hardness) < 0.0000004)
+ exponent = 1000000.0;
+ else
+ exponent = 0.4 / (1.0 - o->hardness);
+
+ for (x = 0; x < length; x++)
+ {
+ priv->lookup[x] = gauss (pow (2.0 * x / o->size, exponent));
+ }
+}
+
+static gdouble
+get_influence (GeglChantO *o,
+ gdouble x,
+ gdouble y)
+{
+ warp_private *priv = (warp_private*) o->chant_data;
+ gfloat radius;
+
+ if (!priv->lookup)
+ {
+ calc_lut (o);
+ }
+
+ radius = sqrt(x*x+y*y);
+
+ if (radius < 0.5 * o->size + 1)
+ return o->strength * priv->lookup[(gint) RINT (radius)];
+ else
+ return 0.0;
+}
+
+static void
+stamp (GeglChantO *o,
+ gdouble x,
+ gdouble y)
+{
+ warp_private *priv = (warp_private*) o->chant_data;
+ GeglBufferIterator *it;
+ Babl *format;
+ gdouble influence;
+ gdouble x_mean = 0.0;
+ gdouble y_mean = 0.0;
+ gint x_iter, y_iter;
+ GeglRectangle area = {x - o->size / 2.0,
+ y - o->size / 2.0,
+ o->size,
+ o->size};
+
+ /* first point of the stroke */
+ if (!priv->last_point_set)
+ {
+ priv->last_x = x;
+ priv->last_y = y;
+ priv->last_point_set = TRUE;
+ return;
+ }
+
+ format = babl_format_n (babl_type ("float"), 2);
+
+ /* If needed, compute the mean deformation */
+ if (o->behavior == GEGL_WARP_BEHAVIOR_SMOOTH)
+ {
+ gint pixel_count = 0;
+
+ it = gegl_buffer_iterator_new (priv->buffer, &area, format, GEGL_BUFFER_READ);
+
+ while (gegl_buffer_iterator_next (it))
+ {
+ gint n_pixels = it->length;
+ gfloat *coords = it->data[0];
+
+ while (n_pixels--)
+ {
+ x_mean += coords[0];
+ y_mean += coords[1];
+ coords += 2;
+ }
+ pixel_count += it->roi->width * it->roi->height;
+ }
+ x_mean /= pixel_count;
+ y_mean /= pixel_count;
+ }
+
+ it = gegl_buffer_iterator_new (priv->buffer, &area, format, GEGL_BUFFER_READWRITE);
+
+ while (gegl_buffer_iterator_next (it))
+ {
+ /* iterate inside the stamp roi */
+ gint n_pixels = it->length;
+ gfloat *coords = it->data[0];
+
+ x_iter = it->roi->x; /* initial x */
+ y_iter = it->roi->y; /* and y coordinates */
+
+ while (n_pixels--)
+ {
+ influence = get_influence (o,
+ x_iter - x,
+ y_iter - y);
+
+ switch (o->behavior)
+ {
+ case GEGL_WARP_BEHAVIOR_MOVE:
+ coords[0] += influence * (priv->last_x - x);
+ coords[1] += influence * (priv->last_y - y);
+ break;
+ case GEGL_WARP_BEHAVIOR_GROW:
+ coords[0] -= influence * (x_iter - x) / o->size;
+ coords[1] -= influence * (y_iter - y) / o->size;
+ break;
+ case GEGL_WARP_BEHAVIOR_SHRINK:
+ coords[0] += influence * (x_iter - x) / o->size;
+ coords[1] += influence * (y_iter - y) / o->size;
+ break;
+ case GEGL_WARP_BEHAVIOR_SWIRL_CW:
+ coords[0] += influence * (y_iter - y) / o->size;
+ coords[1] -= influence * (x_iter - x) / o->size;
+ break;
+ case GEGL_WARP_BEHAVIOR_SWIRL_CCW:
+ coords[0] -= influence * (y_iter - y) / o->size;
+ coords[1] += influence * (x_iter - x) / o->size;
+ break;
+ case GEGL_WARP_BEHAVIOR_ERASE:
+ coords[0] *= 1.0 - MIN (influence, 1.0);
+ coords[1] *= 1.0 - MIN (influence, 1.0);
+ break;
+ case GEGL_WARP_BEHAVIOR_SMOOTH:
+ coords[0] -= influence * (coords[0] - x_mean);
+ coords[1] -= influence * (coords[1] - y_mean);
+ break;
+ }
+
+ coords += 2;
+
+ /* update x and y coordinates */
+ x_iter++;
+ if (x_iter >= (it->roi->x + it->roi->width))
+ {
+ x_iter = it->roi->x;
+ y_iter++;
+ }
+ }
+ }
+
+ /* Memorize the stamp location for movement dependant behavior like move */
+ priv->last_x = x;
+ priv->last_y = y;
+}
+
+static gboolean
+process (GeglOperation *operation,
+ GeglBuffer *input,
+ GeglBuffer *output,
+ const GeglRectangle *result)
+{
+ GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
+ warp_private *priv = (warp_private*) o->chant_data;
+ gdouble dist;
+ gdouble stamps;
+ gdouble spacing = MAX (o->size * 0.01, 0.5); /*1% spacing for starters*/
+
+ Point prev, next, lerp;
+ gulong i;
+ GeglPathList *event;
+
+ printf("Process %p\n", operation);
+
+ priv->buffer = gegl_buffer_dup (input);
+
+ event = gegl_path_get_path (o->stroke);
+
+ prev = *(event->d.point);
+
+ while (event->next)
+ {
+ event = event->next;
+ next = *(event->d.point);
+ dist = point_dist (&next, &prev);
+ stamps = dist / spacing;
+
+ if (stamps < 1)
+ {
+ stamp (o, next.x, next.y);
+ prev = next;
+ }
+ else
+ {
+ for (i = 0; i < stamps; i++)
+ {
+ point_lerp (&lerp, &prev, &next, (i * spacing) / dist);
+ stamp (o, lerp.x, lerp.y);
+ }
+ prev = lerp;
+ }
+ }
+
+ /* Affect the output buffer */
+ gegl_buffer_copy (priv->buffer, result, output, result);
+ gegl_buffer_set_extent (output, gegl_buffer_get_extent (input));
+ gegl_buffer_destroy (priv->buffer);
+
+ /* prepare for the recomputing of the op */
+ priv->last_point_set = FALSE;
+
+ return TRUE;
+}
+
+static void
+gegl_chant_class_init (GeglChantClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
+ GeglOperationFilterClass *filter_class = GEGL_OPERATION_FILTER_CLASS (klass);
+
+ object_class->finalize = finalize;
+ operation_class->prepare = prepare;
+ filter_class->process = process;
+
+ operation_class->name = "gegl:warp";
+ operation_class->categories = "transform";
+ operation_class->description = _("Compute a relative displacement mapping from a stroke");
+}
+#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]