[gegl] operations/workshop: Add simple 2-stop gradient-map operation



commit e12e26b9649c9e37899d6004d663d58134515ae9
Author: Jon Nordby <jononor gmail com>
Date:   Fri May 16 12:31:01 2014 +0200

    operations/workshop: Add simple 2-stop gradient-map operation

 operations/workshop/Makefile.am    |    3 +-
 operations/workshop/gradient-map.c |  192 ++++++++++++++++++++++++++++++++++++
 2 files changed, 194 insertions(+), 1 deletions(-)
---
diff --git a/operations/workshop/Makefile.am b/operations/workshop/Makefile.am
index c259a31..1b45ed4 100644
--- a/operations/workshop/Makefile.am
+++ b/operations/workshop/Makefile.am
@@ -28,4 +28,5 @@ op_LTLIBRARIES =    \
        mandelbrot.la \
        rawbayer-load.la \
        snn-percentile.la \
-       unpremul.la
+       unpremul.la \
+       gradient-map.la
diff --git a/operations/workshop/gradient-map.c b/operations/workshop/gradient-map.c
new file mode 100644
index 0000000..40bf414
--- /dev/null
+++ b/operations/workshop/gradient-map.c
@@ -0,0 +1,192 @@
+/* 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 2006-2014 Øyvind Kolås <pippin gimp org>
+ * Copyright 2014 The Grid, Jon Nordby <jononor gmail com>
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+#include "math.h"
+
+
+#ifdef GEGL_CHANT_PROPERTIES
+gegl_chant_color(color1, _("Color 1"), "black",
+                  _("Starting color for gradient"))
+gegl_chant_color(color2, _("Color 2"), "white",
+                  _("End color for gradient"))
+#else
+
+#define GEGL_CHANT_TYPE_POINT_FILTER
+#define GEGL_CHANT_C_FILE "gradient-map.c"
+
+#include "gegl-chant.h"
+
+
+// TODO: create a custom GeglGradient type
+// - take this as input
+// - should have a number of GeglColor stops
+// - should be deserializable from a (JSON) array of GeglColor values
+
+typedef struct GradientMapProperties_ {
+    gdouble *gradient;
+    gdouble cached_c1[4];
+    gdouble cached_c2[4];
+} GradientMapProperties;
+
+static gdouble *
+create_linear_gradient(GeglColor *color1, GeglColor *color2, gint gradient_len, gint gradient_channels)
+{
+    gdouble *samples;
+    gdouble c1[4];
+    gdouble c2[4];
+    gegl_color_get_rgba(color1, &c1[0], &c1[1], &c1[2], &c1[3]);
+    gegl_color_get_rgba(color2, &c2[0], &c2[1], &c2[2], &c2[3]);
+
+    samples = (gdouble *)g_new(gdouble, gradient_len*gradient_channels);
+    for (int px=0; px < gradient_len; px++) {
+        const float weight = ((float)px)/gradient_len;
+        const size_t offset = px*gradient_channels;
+        samples[offset+0] = c1[0] + (weight * (c2[0] - c1[0]));
+        samples[offset+1] = c1[1] + (weight * (c2[1] - c1[1]));
+        samples[offset+2] = c1[2] + (weight * (c2[2] - c1[2]));
+        samples[offset+3] = c1[3] + (weight * (c2[3] - c1[3]));
+    }
+    return samples;
+}
+
+static gboolean
+is_cached(GradientMapProperties *props, GeglColor *color1, GeglColor *color2) {
+    gdouble c1[4];
+    gdouble c2[4];
+    gegl_color_get_rgba(color1, &c1[0], &c1[1], &c1[2], &c1[3]);
+    gegl_color_get_rgba(color2, &c2[0], &c2[1], &c2[2], &c2[3]);
+
+    return props->gradient && memcmp(props->cached_c1, c1, 4) == 0 && memcmp(props->cached_c2, c2, 4) == 0;
+}
+
+static void inline
+process_pixel_gradient_map(gfloat *in, gfloat *out, gdouble *gradient, gint gradient_len, gint 
gradient_channels) {
+    const gint index = CLAMP (in[0] * (gradient_len-1), 0, gradient_len-1);
+    gdouble *mapped = &(gradient[index * gradient_channels]);
+    out[0] = mapped[0];
+    out[1] = mapped[1];
+    out[2] = mapped[2];
+    out[3] = mapped[3] * in[1];
+}
+
+static void prepare (GeglOperation *operation)
+{
+  GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
+  GradientMapProperties *props = (GradientMapProperties*)o->chant_data;
+
+  gegl_operation_set_format (operation, "input", babl_format ("YA float"));
+  gegl_operation_set_format (operation, "output", babl_format ("RGBA float"));
+
+  if (!props)
+    {
+      props = g_new(GradientMapProperties, 1);
+      props->gradient = NULL;
+      o->chant_data = props;
+    }
+}
+
+static void finalize (GObject *object)
+{
+    GeglOperation *op = (void*)object;
+    GeglChantO *o = GEGL_CHANT_PROPERTIES (op);
+    if (o->chant_data) {
+      GradientMapProperties *props = (GradientMapProperties *) o->chant_data;
+      if (props->gradient) {
+        g_free(props->gradient);
+      }
+      o->chant_data = NULL;
+    }
+    G_OBJECT_CLASS(gegl_chant_parent_class)->finalize (object);
+}
+
+
+static gboolean
+process (GeglOperation       *op,
+         void                *in_buf,
+         void                *out_buf,
+         glong                n_pixels,
+         const GeglRectangle *roi,
+         gint                 level)
+{
+  GeglChantO *o = GEGL_CHANT_PROPERTIES (op);
+  gfloat     * GEGL_ALIGNED in_pixel = in_buf;
+  gfloat     * GEGL_ALIGNED out_pixel = out_buf;
+  const gint gradient_length = 2048;
+  const gint gradient_channels = 4; // RGBA
+  GradientMapProperties *props = (GradientMapProperties*)o->chant_data;
+  gdouble *cached_c1 = props->cached_c1;
+  gdouble *cached_c2 = props->cached_c2;
+
+  if (!is_cached(props, o->color1, o->color2)) {
+    if (props->gradient) {
+        g_free(props->gradient);
+    }
+    props->gradient = create_linear_gradient(o->color1, o->color2, gradient_length, gradient_channels);
+
+    gegl_color_get_rgba(o->color1, &cached_c1[0], &cached_c1[1], &cached_c1[2], &cached_c1[3]);
+    gegl_color_get_rgba(o->color2, &cached_c2[0], &cached_c2[1], &cached_c2[2], &cached_c2[3]);
+  }
+
+  for (int i=0; i<n_pixels; i++)
+    {
+      process_pixel_gradient_map(in_pixel, out_pixel, props->gradient, gradient_length, gradient_channels);
+      in_pixel  += 2;
+      out_pixel += 4;
+    }
+  return TRUE;
+}
+
+static void
+gegl_chant_class_init (GeglChantClass *klass)
+{
+  GeglOperationClass            *operation_class;
+  GeglOperationPointFilterClass *point_filter_class;
+  gchar                         *composition = "<?xml version='1.0' encoding='UTF-8'?>"
+    "<gegl>"
+    "<node operation='gegl:gradient-map'>"
+    "  <params>"
+    "    <param name='color1'>#x410404</param>"
+    "    <param name='color2'>#x22FFFA</param>"
+    "  </params>"
+    "</node>"
+    "<node operation='gegl:load'>"
+    "  <params>"
+    "    <param name='path'>standard-input.png</param>"
+    "  </params>"
+    "</node>"
+    "</gegl>";
+
+  operation_class    = GEGL_OPERATION_CLASS (klass);
+  point_filter_class = GEGL_OPERATION_POINT_FILTER_CLASS (klass);
+
+  G_OBJECT_CLASS (klass)->finalize = finalize;
+  operation_class->prepare = prepare;
+  point_filter_class->process = process;
+
+  gegl_operation_class_set_keys (operation_class,
+      "name",       "gegl:gradient-map",
+      "categories", "color",
+      "description", _("Applies a color gradient."),
+      "reference-composition", composition,
+      NULL);
+}
+
+#endif /* #ifdef GEGL_CHANT_PROPERTIES */


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