[gegl] normal-map: new operation in workshop



commit 536472aec9bc18c3232be3ec8873eada079b0184
Author: Ell <ell_se yahoo com>
Date:   Wed Sep 18 19:53:45 2019 +0300

    normal-map: new operation in workshop
    
    Add a new gegl:normal-map operation to the workshop, which
    generates a normal map from a height map.

 operations/workshop/meson.build  |   1 +
 operations/workshop/normal-map.c | 249 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 250 insertions(+)
---
diff --git a/operations/workshop/meson.build b/operations/workshop/meson.build
index 8d8bbab05..0f7c30c93 100644
--- a/operations/workshop/meson.build
+++ b/operations/workshop/meson.build
@@ -15,6 +15,7 @@ libraries = [
   { 'name': 'gradient-map', },
   { 'name': 'hstack', },
   { 'name': 'integral-image', },
+  { 'name': 'normal-map', },
   { 'name': 'rawbayer-load', },
   { 'name': 'segment-kmeans', },
   { 'name': 'selective-hue-saturation', },
diff --git a/operations/workshop/normal-map.c b/operations/workshop/normal-map.c
new file mode 100644
index 000000000..147d7ccbe
--- /dev/null
+++ b/operations/workshop/normal-map.c
@@ -0,0 +1,249 @@
+/* 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 <https://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2019 Ell
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+#ifdef GEGL_PROPERTIES
+
+enum_start (gegl_normal_map_component)
+  enum_value (GEGL_NORMAL_MAP_COMPONENT_RED,   "red",   N_("Red"))
+  enum_value (GEGL_NORMAL_MAP_COMPONENT_GREEN, "green", N_("Green"))
+  enum_value (GEGL_NORMAL_MAP_COMPONENT_BLUE,  "blue",  N_("Blue"))
+enum_end (GeglNormalMapComponent)
+
+property_double (scale, _("Scale"), 10.0)
+  description (_("The amount by which to scale the height values"))
+  value_range (0.0, G_MAXDOUBLE)
+  ui_range    (0.0, 100.0)
+
+property_enum (x_component, _("X Component"),
+               GeglNormalMapComponent, gegl_normal_map_component,
+               GEGL_NORMAL_MAP_COMPONENT_RED)
+  description (_("The component used for the X coordinates"))
+
+property_enum (y_component, _("Y Component"),
+               GeglNormalMapComponent, gegl_normal_map_component,
+               GEGL_NORMAL_MAP_COMPONENT_GREEN)
+  description (_("The component used for the Y coordinates"))
+
+property_boolean (flip_x, _("Flip X"), FALSE)
+  description (_("Flip the X coordinates"))
+
+property_boolean (flip_y, _("Flip Y"), FALSE)
+  description (_("Flip the Y coordinates"))
+
+property_boolean (tileable, _("Tileable"), FALSE)
+  description (_("Generate a tileable map"))
+
+#else
+
+#define GEGL_OP_AREA_FILTER
+#define GEGL_OP_NAME     normal_map
+#define GEGL_OP_C_SOURCE normal-map.c
+
+#include <math.h>
+#include "gegl-op.h"
+
+static void
+prepare (GeglOperation *operation)
+{
+  GeglOperationAreaFilter *area = GEGL_OPERATION_AREA_FILTER (operation);
+
+  const Babl *format = gegl_operation_get_source_format (operation, "input");
+  const Babl *in_format;
+  const Babl *out_format;
+
+  area->left   =
+  area->right  =
+  area->top    =
+  area->bottom = 1;
+
+  in_format  = babl_format_with_space ("Y'A float", format);
+  out_format = babl_format_with_space ("R'G'B'A float", format);
+
+  gegl_operation_set_format (operation, "input", in_format);
+  gegl_operation_set_format (operation, "output", out_format);
+}
+
+static GeglRectangle
+get_bounding_box (GeglOperation *operation)
+{
+  GeglRectangle  result = { 0, 0, 0, 0 };
+  GeglRectangle *in_rect;
+
+  in_rect = gegl_operation_source_get_bounding_box (operation, "input");
+
+  if (in_rect)
+    result = *in_rect;
+
+  return result;
+}
+
+static GeglAbyssPolicy
+get_abyss_policy (GeglOperation *operation,
+                  const gchar   *input_pad)
+{
+  GeglProperties *o = GEGL_PROPERTIES (operation);
+
+  return o->tileable ? GEGL_ABYSS_LOOP : GEGL_ABYSS_CLAMP;
+}
+
+static gboolean
+process (GeglOperation       *operation,
+         GeglBuffer          *input,
+         GeglBuffer          *output,
+         const GeglRectangle *result,
+         gint                 level)
+{
+  GeglProperties     *o            = GEGL_PROPERTIES (operation);
+  const Babl         *in_format    = gegl_operation_get_format (operation, "input");
+  const Babl         *out_format   = gegl_operation_get_format (operation, "output");
+  GeglAbyssPolicy     abyss_policy = get_abyss_policy (operation, NULL);
+  gfloat              scale        = o->scale / 2.0;
+  gfloat              x_sign       = (o->flip_x ? -0.5 : +0.5);
+  gfloat              y_sign       = (o->flip_y ? -0.5 : +0.5);
+  gint                x_component  = o->x_component;
+  gint                y_component  = o->y_component;
+  gint                z_component  = 2;
+  GeglBufferIterator *iter;
+
+  while (y_component == x_component)
+    y_component = (y_component + 1) % 3;
+
+  while (z_component == x_component || z_component == y_component)
+    z_component = (z_component + 1) % 3;
+
+  iter = gegl_buffer_iterator_new (output, result, 0, out_format,
+                                   GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 2);
+
+  gegl_buffer_iterator_add (iter, input, result, 0, in_format,
+                            GEGL_ACCESS_READ, abyss_policy);
+
+  while (gegl_buffer_iterator_next (iter))
+    {
+      const gfloat        *in     = iter->items[1].data;
+      gfloat              *out    = iter->items[0].data;
+      const GeglRectangle *roi    = &iter->items[0].roi;
+      gint                 stride = 2 * roi->width;
+      gfloat               top[2 * roi->width];
+      gfloat               bottom[2 * roi->width];
+      gfloat               left[2 * roi->height];
+      gfloat               right[2 * roi->height];
+      gint                 x;
+      gint                 y;
+
+      gegl_buffer_get (input,
+                       GEGL_RECTANGLE (roi->x, roi->y - 1,
+                                       roi->width, 1),
+                       1.0, in_format, top,
+                       GEGL_AUTO_ROWSTRIDE, abyss_policy);
+      gegl_buffer_get (input,
+                       GEGL_RECTANGLE (roi->x, roi->y + roi->height,
+                                       roi->width, 1),
+                       1.0, in_format, bottom,
+                       GEGL_AUTO_ROWSTRIDE, abyss_policy);
+      gegl_buffer_get (input,
+                       GEGL_RECTANGLE (roi->x - 1, roi->y,
+                                       1, roi->height),
+                       1.0, in_format, left,
+                       GEGL_AUTO_ROWSTRIDE, abyss_policy);
+      gegl_buffer_get (input,
+                       GEGL_RECTANGLE (roi->x + roi->width, roi->y,
+                                       1, roi->height),
+                       1.0, in_format, right,
+                       GEGL_AUTO_ROWSTRIDE, abyss_policy);
+
+      for (y = 0; y < roi->height; y++)
+        {
+          for (x = 0; x < roi->width; x++)
+            {
+              gfloat l;
+              gfloat r;
+              gfloat t;
+              gfloat b;
+              gfloat nx;
+              gfloat ny;
+              gfloat s;
+
+              if (x > 0)
+                l = in[-2];
+              else
+                l = left[2 * y];
+
+              if (x < roi->width - 1)
+                r = in[2];
+              else
+                r = right[2 * y];
+
+              if (y > 0)
+                t = in[-stride];
+              else
+                t = top[2 * x];
+
+              if (y < roi->height - 1)
+                b = in[stride];
+              else
+                b = bottom[2 * x];
+
+              nx = scale * (l - r);
+              ny = scale * (b - t);
+
+              s = 1.0f / sqrtf (nx * nx + ny * ny + 1.0f);
+
+              nx *= x_sign * s;
+              ny *= y_sign * s;
+
+              out[x_component] = 0.5f + nx;
+              out[y_component] = 0.5f + ny;
+              out[z_component] = 1.0f;
+              out[3]           = in[1];
+
+              in  += 2;
+              out += 4;
+            }
+        }
+    }
+
+  return TRUE;
+}
+
+static void
+gegl_op_class_init (GeglOpClass *klass)
+{
+  GeglOperationClass           *operation_class;
+  GeglOperationFilterClass     *filter_class;
+  GeglOperationAreaFilterClass *area_class;
+
+  operation_class = GEGL_OPERATION_CLASS (klass);
+  filter_class    = GEGL_OPERATION_FILTER_CLASS (klass);
+  area_class      = GEGL_OPERATION_AREA_FILTER_CLASS (klass);
+
+  area_class->get_abyss_policy      = get_abyss_policy;
+  filter_class->process             = process;
+  operation_class->prepare          = prepare;
+  operation_class->get_bounding_box = get_bounding_box;
+
+  gegl_operation_class_set_keys (operation_class,
+    "name",        "gegl:normal-map",
+    "title",       _("Normal Map"),
+    "categories",  "map",
+    "description", _("Generate a normal map from a height map"),
+    NULL);
+}
+#endif


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