[gegl] operations: Extract vector rendering operations from path.c



commit 7028e700c7359492fc1515a3d601963bd61e6a58
Author: Damien de Lemeny <d delemeny gmail com>
Date:   Thu Jun 10 15:26:17 2010 +0200

    operations: Extract vector rendering operations from path.c
    
    * add vector-fill and vector-stroke operations (use cairo)
    * update Makefile

 bin/gegl.c                          |    2 +-
 operations/external/Makefile.am     |    8 +-
 operations/external/vector-fill.c   |  260 ++++++++++++++++++++++++++++++++
 operations/external/vector-stroke.c |  282 +++++++++++++++++++++++++++++++++++
 4 files changed, 550 insertions(+), 2 deletions(-)
---
diff --git a/bin/gegl.c b/bin/gegl.c
index 333a280..74fdfde 100644
--- a/bin/gegl.c
+++ b/bin/gegl.c
@@ -45,7 +45,7 @@
 #endif
 
 #define DEFAULT_COMPOSITION \
-"<?xml version='1.0' encoding='UTF-8'?> <gegl> <node operation='gegl:crop'> <params> <param name='x'>0</param> <param name='y'>0</param> <param name='width'>690</param> <param name='height'>670</param> </params> </node> <node operation='gegl:over'> <node operation='gegl:translate'> <params> <param name='x'>14</param> <param name='y'>613</param> </params> </node> <node operation='gegl:opacity'> <params> <param name='value'>0.59999999999999998</param> </params> </node> <node name='text' operation='gegl:text'> <params> <param name='string'>2000-2010 © Calvin Williamson, Caroline Dahloff, Manish Singh, Jay Cox Daniel Rogers, Sven Neumann, Michael Natterer, �yvind Kolås, Philip Lafleur, Dominik Ernst, Richard Kralovic, Kevin Cozens, Victor Bogado, Martin Nordholts, Geert Jordaens, Michael Schumacher, John Marshall, �tienne Bersac, Mark Probst, Håkon Hitland, Tor Lillqvist, Hans Breuer, Deji Akingunola and Bradley Broom, Hans Petter Jansson, Jan Heller, dmacks netscpace org, 
 Sven Anders, Hubert Figuière, Sam Hocevar, yahvuu at gmail.com, Nicolas Robidoux, Ruben Vermeersch, Gary V. Vaughan, James Legg, Henrik �kesson, Fryderyk Dziarmagowski, Ozan Caglayan, Tobias Mueller, Nils Philippsen, Adam Turcotte, Danny Robson, Javier Jardón and Yakkov Selkowitz, Kaja Liiv, Eric Doust, Garry R. Osgood, �yvind Kolås, Kevin Cozens and Shlomi Fish, Jakub Steiner, and Tonda Tavalec </param> <param name='font'>Sans</param> <param name='size'>8</param> <param name='color'>rgb(0.0000, 0.0000, 0.0000)</param> <param name='wrap'>628</param> <param name='alignment'>0</param> <param name='width'>622</param> <param name='height'>40</param> </params> </node> </node> <node operation='gegl:over'> <node operation='gegl:translate'> <params> <param name='x'>300</param> <param name='y'>500</param> </params> </node> <node operation='gegl:over'> <node operation='gegl:translate'> <params> <param name='x'>0</param> <param name='y'>0</param> </params> </node> <node operation=
 'gegl:dropshadow'> <params> <param name='opacity'>1.2</param> <param name='x'>0</param> <param name='y'>0</param> <param name='radius'>8</param> </params> </node> <gegl:path d='M0,50 C0,78 24,100 50,100 C77,100 100,78 100,50 C100,45 99,40 98,35 C82,35 66,35 50,35 C42,35 35,42 35,50 C35,58 42,65 50,65 C56,65 61,61 64,56 C67,51 75,55 73,60 C69,69 60,75 50,75 C36,75 25,64 25,50 C25,36 36,25 50,25 L93,25 C83,9 67,0 49,0 C25,0 0,20 0,50 z' fill='white'/> </node> <node operation='gegl:over'> <node operation='gegl:translate'> <params> <param name='x'>88</param> <param name='y'>0</param> </params> </node> <node operation='gegl:dropshadow'> <params> <param name='opacity'>1.2</param> <param name='x'>0</param> <param name='y'>0</param> <param name='radius'>8</param> </params> </node> <node operation='gegl:path'> <params> <param name='d'>M50,0 C23,0 0,22 0,50 C0,77 22,100 50,100 C68,100 85,90 93,75 L40,75 C35,75 35,65 40,65 L98,65 C100,55 100,45 98,35 L40,35 C35,35 35,25 40,25 L93,25 C8
 4,10 68,0 50,0 z</param> <param name='fill'>rgb(1.0000, 1.0000, 1.0000)</param> </params> </node> </node> <node operation='gegl:over'> <node operation='gegl:translate'> <params> <param name='x'>176</param> <param name='y'>0</param> </params> </node> <node operation='gegl:dropshadow'> <params> <param name='opacity'>1.2</param> <param name='x'>0</param> <param name='y'>0</param> <param name='radius'>8</param> </params> </node> <node operation='gegl:path'> <params> <param name='d'>M0,50 C0,78 24,100 50,100 C77,100 100,78 100,50 C100,45 99,40 98,35 C82,35 66,35 50,35 C42,35 35,42 35,50 C35,58 42,65 50,65 C56,65 61,61 64,56 C67,51 75,55 73,60 C69,69 60,75 50,75 C36,75 25,64 25,50 C25,36 36,25 50,25 L93,25 C83,9 67,0 49,0 C25,0 0,20 0,50 z</param> <param name='fill'>rgb(1.0000, 1.0000, 1.0000)</param> </params> </node> </node> <node operation='gegl:translate'> <params> <param name='x'>264</param> <param name='y'>0</param> </params> </node> <node operation='gegl:dropshadow'> <param
 s> <param name='opacity'>1.2</param> <param name='x'>0</param> <param name='y'>0</param> <param name='radius'>8</param> </params> </node> <node operation='gegl:path'> <params> <param name='d'>M30,4 C12,13 0,30 0,50 C0,78 23,100 50,100 C71,100 88,88 96,71 L56,71 C42,71 30,59 30,45 L30,4 z</param> <param name='fill'>rgb(1.0000, 1.0000, 1.0000)</param> </params> </node> </node> <node operation='gegl:rotate'> <params> <param name='origin-x'>0</param> <param name='origin-y'>0</param> <param name='filter'>linear</param> <param name='hard-edges'>false</param> <param name='lanczos-width'>3</param> <param name='degrees'>42</param> </params> </node> <node operation='gegl:checkerboard'> <params> <param name='x'>43</param> <param name='y'>44</param> <param name='x-offset'>0</param> <param name='y-offset'>0</param> <param name='color1'>rgb(0.7097, 0.7097, 0.7097)</param> <param name='color2'>rgb(0.7661, 0.7661, 0.7661)</param> </params> </node> </gegl>"
+"<?xml version='1.0' encoding='UTF-8'?> <gegl> <node operation='gegl:crop'> <params> <param name='x'>0</param> <param name='y'>0</param> <param name='width'>690</param> <param name='height'>670</param> </params> </node> <node operation='gegl:over'> <node operation='gegl:translate'> <params> <param name='x'>14</param> <param name='y'>613</param> </params> </node> <node operation='gegl:opacity'> <params> <param name='value'>0.59999999999999998</param> </params> </node> <node name='text' operation='gegl:text'> <params> <param name='string'>2000-2010 © Calvin Williamson, Caroline Dahloff, Manish Singh, Jay Cox Daniel Rogers, Sven Neumann, Michael Natterer, �yvind Kolås, Philip Lafleur, Dominik Ernst, Richard Kralovic, Kevin Cozens, Victor Bogado, Martin Nordholts, Geert Jordaens, Michael Schumacher, John Marshall, �tienne Bersac, Mark Probst, Håkon Hitland, Tor Lillqvist, Hans Breuer, Deji Akingunola and Bradley Broom, Hans Petter Jansson, Jan Heller, dmacks netscpace org, 
 Sven Anders, Hubert Figuière, Sam Hocevar, yahvuu at gmail.com, Nicolas Robidoux, Ruben Vermeersch, Gary V. Vaughan, James Legg, Henrik �kesson, Fryderyk Dziarmagowski, Ozan Caglayan, Tobias Mueller, Nils Philippsen, Adam Turcotte, Danny Robson, Javier Jardón and Yakkov Selkowitz, Kaja Liiv, Eric Doust, Garry R. Osgood, �yvind Kolås, Kevin Cozens and Shlomi Fish, Jakub Steiner, and Tonda Tavalec </param> <param name='font'>Sans</param> <param name='size'>8</param> <param name='color'>rgb(0.0000, 0.0000, 0.0000)</param> <param name='wrap'>628</param> <param name='alignment'>0</param> <param name='width'>622</param> <param name='height'>40</param> </params> </node> </node> <node operation='gegl:over'> <node operation='gegl:translate'> <params> <param name='x'>300</param> <param name='y'>500</param> </params> </node> <node operation='gegl:over'> <node operation='gegl:translate'> <params> <param name='x'>0</param> <param name='y'>0</param> </params> </node> <node operation=
 'gegl:dropshadow'> <params> <param name='opacity'>1.2</param> <param name='x'>0</param> <param name='y'>0</param> <param name='radius'>8</param> </params> </node> <gegl:vector-fill d='M0,50 C0,78 24,100 50,100 C77,100 100,78 100,50 C100,45 99,40 98,35 C82,35 66,35 50,35 C42,35 35,42 35,50 C35,58 42,65 50,65 C56,65 61,61 64,56 C67,51 75,55 73,60 C69,69 60,75 50,75 C36,75 25,64 25,50 C25,36 36,25 50,25 L93,25 C83,9 67,0 49,0 C25,0 0,20 0,50 z' color='white'/> </node> <node operation='gegl:over'> <node operation='gegl:translate'> <params> <param name='x'>88</param> <param name='y'>0</param> </params> </node> <node operation='gegl:dropshadow'> <params> <param name='opacity'>1.2</param> <param name='x'>0</param> <param name='y'>0</param> <param name='radius'>8</param> </params> </node> <node operation='gegl:vector-fill'> <params> <param name='d'>M50,0 C23,0 0,22 0,50 C0,77 22,100 50,100 C68,100 85,90 93,75 L40,75 C35,75 35,65 40,65 L98,65 C100,55 100,45 98,35 L40,35 C35,35 35,25 
 40,25 L93,25 C84,10 68,0 50,0 z</param> <param name='color'>rgb(1.0000, 1.0000, 1.0000)</param> </params> </node> </node> <node operation='gegl:over'> <node operation='gegl:translate'> <params> <param name='x'>176</param> <param name='y'>0</param> </params> </node> <node operation='gegl:dropshadow'> <params> <param name='opacity'>1.2</param> <param name='x'>0</param> <param name='y'>0</param> <param name='radius'>8</param> </params> </node> <node operation='gegl:vector-fill'> <params> <param name='d'>M0,50 C0,78 24,100 50,100 C77,100 100,78 100,50 C100,45 99,40 98,35 C82,35 66,35 50,35 C42,35 35,42 35,50 C35,58 42,65 50,65 C56,65 61,61 64,56 C67,51 75,55 73,60 C69,69 60,75 50,75 C36,75 25,64 25,50 C25,36 36,25 50,25 L93,25 C83,9 67,0 49,0 C25,0 0,20 0,50 z</param> <param name='color'>rgb(1.0000, 1.0000, 1.0000)</param> </params> </node> </node> <node operation='gegl:translate'> <params> <param name='x'>264</param> <param name='y'>0</param> </params> </node> <node operation='
 gegl:dropshadow'> <params> <param name='opacity'>1.2</param> <param name='x'>0</param> <param name='y'>0</param> <param name='radius'>8</param> </params> </node> <node operation='gegl:vector-fill'> <params> <param name='d'>M30,4 C12,13 0,30 0,50 C0,78 23,100 50,100 C71,100 88,88 96,71 L56,71 C42,71 30,59 30,45 L30,4 z</param> <param name='color'>rgb(1.0000, 1.0000, 1.0000)</param> </params> </node> </node> <node operation='gegl:rotate'> <params> <param name='origin-x'>0</param> <param name='origin-y'>0</param> <param name='filter'>linear</param> <param name='hard-edges'>false</param> <param name='lanczos-width'>3</param> <param name='degrees'>42</param> </params> </node> <node operation='gegl:checkerboard'> <params> <param name='x'>43</param> <param name='y'>44</param> <param name='x-offset'>0</param> <param name='y-offset'>0</param> <param name='color1'>rgb(0.7097, 0.7097, 0.7097)</param> <param name='color2'>rgb(0.7661, 0.7661, 0.7661)</param> </params> </node> </gegl>"
 
 #define STDIN_BUF_SIZE 128
 
diff --git a/operations/external/Makefile.am b/operations/external/Makefile.am
index 5d4e014..fd5eddf 100644
--- a/operations/external/Makefile.am
+++ b/operations/external/Makefile.am
@@ -12,10 +12,16 @@ text_la_CFLAGS = $(AM_CFLAGS) $(PANGOCAIRO_CFLAGS)
 endif
 
 if HAVE_CAIRO
-ops += path.la
+ops += path.la vector-fill.la vector-stroke.la
 path_la_SOURCES = path.c
 path_la_LIBADD = $(op_libs) $(CAIRO_LIBS)
 path_la_CFLAGS = $(AM_CFLAGS) $(CAIRO_CFLAGS)
+vector_fill_la_SOURCES = vector-fill.c
+vector_fill_la_LIBADD = $(op_libs) $(CAIRO_LIBS)
+vector_fill_la_CFLAGS = $(AM_CFLAGS) $(CAIRO_CFLAGS)
+vector_stroke_la_SOURCES = vector-stroke.c
+vector_stroke_la_LIBADD = $(op_libs) $(CAIRO_LIBS)
+vector_stroke_la_CFLAGS = $(AM_CFLAGS) $(CAIRO_CFLAGS)
 endif
 
 if HAVE_PNG
diff --git a/operations/external/vector-fill.c b/operations/external/vector-fill.c
new file mode 100644
index 0000000..6f4629d
--- /dev/null
+++ b/operations/external/vector-fill.c
@@ -0,0 +1,260 @@
+/* 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 �yvind Kolås <pippin gimp org>
+ */
+
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+
+#ifdef GEGL_CHANT_PROPERTIES
+
+
+gegl_chant_color  (color,    _("Color"),  "rgba(0.0,0.0,0.0,0.6)",
+                             _("Color of paint to use for filling."))
+
+gegl_chant_double (opacity,  _("Opacity"),  -2.0, 2.0, 1.0,
+                             _("The fill opacity to use."))
+
+gegl_chant_string (fill_rule,_("Fill rule."), "nonzero",
+                             _("how to determine what to fill (nonzero|evenodd"))
+
+gegl_chant_string (transform,_("Transform"), "",
+                             _("svg style description of transform."))
+
+gegl_chant_path   (d,        _("Vector"),
+                             _("A GeglVector representing the path of the stroke"))
+
+#else
+
+#define GEGL_CHANT_TYPE_FILTER
+#define GEGL_CHANT_C_FILE "vector-fill.c"
+
+#include "gegl-plugin.h"
+
+/* the path api isn't public yet */
+#include "property-types/gegl-path.h"
+static void path_changed (GeglPath *path,
+                          const GeglRectangle *roi,
+                          gpointer userdata);
+
+#include "gegl-chant.h"
+#include <cairo.h>
+
+static void path_changed (GeglPath *path,
+                          const GeglRectangle *roi,
+                          gpointer userdata)
+{
+  GeglChantO    *o   = GEGL_CHANT_PROPERTIES (userdata);
+  GeglRectangle rect;
+  gdouble        x0, x1, y0, y1;
+  
+  gegl_path_get_bounds(o->d, &x0, &x1, &y0, &y1);
+  rect.x = x0;
+  rect.y = y0;
+  rect.width = x1 - x0;
+  rect.height = y1 - y0;  
+  
+  gegl_operation_invalidate (userdata, roi, TRUE);
+};
+
+static void
+prepare (GeglOperation *operation)
+{
+  GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
+  gegl_operation_set_format (operation, "output", babl_format ("RaGaBaA float"));
+  if (o->transform && o->transform[0] != '\0')
+    {
+      GeglMatrix3 matrix;
+      gegl_matrix3_parse_string (matrix, o->transform);
+      gegl_path_set_matrix (o->d, matrix);
+    }
+}
+
+static GeglRectangle
+get_bounding_box (GeglOperation *operation)
+{
+  GeglChantO    *o       = GEGL_CHANT_PROPERTIES (operation);
+  GeglRectangle  defined = { 0, 0, 512, 512 };
+  GeglRectangle *in_rect;
+  gdouble        x0, x1, y0, y1;
+
+  in_rect =  gegl_operation_source_get_bounding_box (operation, "input");
+
+  gegl_path_get_bounds (o->d, &x0, &x1, &y0, &y1);
+  defined.x      = x0;
+  defined.y      = y0;
+  defined.width  = x1 - x0;
+  defined.height = y1 - y0;
+
+  if (in_rect)
+    {
+      gegl_rectangle_bounding_box (&defined, &defined, in_rect);
+    }
+
+  return defined;
+}
+
+static void gegl_path_cairo_play (GeglPath *path,
+                                    cairo_t *cr);
+
+static gboolean
+process (GeglOperation       *operation,
+         GeglBuffer          *input,
+         GeglBuffer          *output,
+         const GeglRectangle *result)
+{
+  GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
+  gboolean need_fill = FALSE;
+  gdouble r,g,b,a;
+
+  if (input)
+    {
+      gegl_buffer_copy (input, result, output, result);
+    }
+  else
+    {
+      gegl_buffer_clear (output, result);
+    }
+
+
+  if (o->opacity > 0.0001 && o->color)
+    {
+      gegl_color_get_rgba (o->color, &r,&g,&b,&a);
+      a *= o->opacity;
+      if (a>0.001)
+          need_fill=TRUE;
+    }
+
+  if (need_fill)   
+    {
+      GStaticMutex mutex = G_STATIC_MUTEX_INIT;
+      cairo_t *cr;
+      cairo_surface_t *surface;
+      guchar *data;
+
+      
+      g_static_mutex_lock (&mutex);      
+      data = (void*)gegl_buffer_linear_open (output, result, NULL, babl_format ("B'aG'aR'aA u8"));
+      surface = cairo_image_surface_create_for_data (data,
+                                                     CAIRO_FORMAT_ARGB32,
+                                                     result->width,
+                                                     result->height,
+                                                     result->width * 4);
+      
+      cr = cairo_create (surface);
+      cairo_translate (cr, -result->x, -result->y);
+      if (g_str_equal (o->fill_rule, "evenodd"))
+          cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+
+      gegl_path_cairo_play (o->d, cr);
+      cairo_set_source_rgba (cr, r,g,b,a);
+      cairo_fill (cr);
+      cairo_destroy (cr);
+      
+      gegl_buffer_linear_close (output, data);
+      g_static_mutex_unlock (&mutex);
+    }
+  return  TRUE;
+}
+
+static void foreach_cairo (const GeglPathItem *knot,
+                           gpointer              cr)
+{
+  switch (knot->type)
+    {
+      case 'M':
+        cairo_move_to (cr, knot->point[0].x, knot->point[0].y);
+        break;
+      case 'L':
+        cairo_line_to (cr, knot->point[0].x, knot->point[0].y);
+        break;
+      case 'C':
+        cairo_curve_to (cr, knot->point[0].x, knot->point[0].y,
+                            knot->point[1].x, knot->point[1].y,
+                            knot->point[2].x, knot->point[2].y);
+        break;
+      case 'z':
+        cairo_close_path (cr);
+        break;
+      default:
+        g_print ("%s uh?:%c\n", G_STRLOC, knot->type);
+    }
+}
+
+static void gegl_path_cairo_play (GeglPath *path,
+                                    cairo_t *cr)
+{
+  gegl_path_foreach_flat (path, foreach_cairo, cr);
+}
+
+static GeglNode *detect (GeglOperation *operation,
+                         gint           x,
+                         gint           y)
+{
+  GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
+  cairo_t *cr;
+  cairo_surface_t *surface;
+  gchar *data = "     ";
+  gboolean result = FALSE;
+
+  surface = cairo_image_surface_create_for_data ((guchar*)data,
+                                                 CAIRO_FORMAT_ARGB32,
+                                                 1,1,4);
+  cr = cairo_create (surface);
+  gegl_path_cairo_play (o->d, cr);
+
+  if (!result)
+    {
+      if (o->d)
+        {
+          gdouble r,g,b,a;
+          gegl_color_get_rgba (o->color, &r,&g,&b,&a);
+          if (a * o->opacity>0.8)
+            result = cairo_in_fill (cr, x, y);
+        }
+    }
+
+  cairo_destroy (cr);
+
+  if (result)
+    return operation->node;
+
+  return NULL;
+}
+
+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->get_bounding_box = get_bounding_box;
+  operation_class->prepare = prepare;
+  operation_class->detect = detect;
+
+  operation_class->name        = "gegl:fill-path";
+  operation_class->categories  = "render";
+  operation_class->description = _("Renders a filled region");
+}
+
+
+#endif
diff --git a/operations/external/vector-stroke.c b/operations/external/vector-stroke.c
new file mode 100644
index 0000000..8d1318c
--- /dev/null
+++ b/operations/external/vector-stroke.c
@@ -0,0 +1,282 @@
+/* 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 �yvind Kolås <pippin gimp org>
+ */
+
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+
+#ifdef GEGL_CHANT_PROPERTIES
+
+
+gegl_chant_color  (color,    _("Color"),      "rgba(0.0,0.0,0.0,0.0)",
+                             _("Color of paint to use for stroking."))
+
+gegl_chant_double (width,    _("Width"),  0.0, 200.0, 2.0,
+                             _("The width of the brush used to stroke the path."))
+
+gegl_chant_double (opacity,  _("Opacity"),  -2.0, 2.0, 1.0,
+                             _("Opacity of stroke, note, does not behave like SVG since at the moment stroking is done using an airbrush tool."))
+
+gegl_chant_string (transform,_("Transform"), "",
+                             _("svg style description of transform."))
+
+gegl_chant_path   (d,        _("Vector"),
+                             _("A GeglVector representing the path of the stroke"))
+
+#else
+
+#define GEGL_CHANT_TYPE_FILTER
+#define GEGL_CHANT_C_FILE "vector-stroke.c"
+
+#include "gegl-plugin.h"
+
+/* the path api isn't public yet */
+#include "property-types/gegl-path.h"
+static void path_changed (GeglPath *path,
+                          const GeglRectangle *roi,
+                          gpointer userdata);
+
+#include "gegl-chant.h"
+#include <cairo.h>
+
+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->width/2;
+  rect.y -= o->width/2;
+  rect.width += o->width;
+  rect.height += o->width;
+
+  gegl_operation_invalidate (userdata, &rect, FALSE);
+};
+
+static void
+prepare (GeglOperation *operation)
+{
+  GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
+  gegl_operation_set_format (operation, "output", babl_format ("RaGaBaA float"));
+  if (o->transform && o->transform[0] != '\0')
+    {
+      GeglMatrix3 matrix;
+      gegl_matrix3_parse_string (matrix, o->transform);
+      gegl_path_set_matrix (o->d, matrix);
+    }
+}
+
+static GeglRectangle
+get_bounding_box (GeglOperation *operation)
+{
+  GeglChantO    *o       = GEGL_CHANT_PROPERTIES (operation);
+  GeglRectangle  defined = { 0, 0, 512, 512 };
+  GeglRectangle *in_rect;
+  gdouble        x0, x1, y0, y1;
+
+  in_rect =  gegl_operation_source_get_bounding_box (operation, "input");
+
+  gegl_path_get_bounds (o->d, &x0, &x1, &y0, &y1);
+  defined.x      = x0 - o->width/2;
+  defined.y      = y0 - o->width/2;
+  defined.width  = x1 - x0 + o->width;
+  defined.height = y1 - y0 + o->width;
+
+  if (in_rect)
+    {
+      gegl_rectangle_bounding_box (&defined, &defined, in_rect);
+    }
+
+  return defined;
+}
+
+
+
+#if 0
+static gboolean gegl_path_is_closed (GeglPath *path)
+{
+  const GeglPathItem *knot;
+
+  if (!path)
+    return FALSE;
+  knot = gegl_path_get_node (path, -1);
+  if (!knot)
+    return FALSE;
+  if (knot->type == 'z')
+    {
+      return TRUE;
+    }
+  return FALSE;
+}
+#endif
+
+
+#if 0
+static GeglRectangle
+get_cached_region (GeglOperation *operation)
+{
+  return get_bounding_box (operation);
+}
+#endif
+static void gegl_path_cairo_play (GeglPath *path,
+                                    cairo_t *cr);
+
+static gboolean
+process (GeglOperation       *operation,
+         GeglBuffer          *input,
+         GeglBuffer          *output,
+         const GeglRectangle *result)
+{
+  GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
+  gboolean need_stroke = FALSE;
+  gdouble r,g,b,a;
+
+  if (input)
+    {
+      gegl_buffer_copy (input, result, output, result);
+    }
+  else
+    {
+      gegl_buffer_clear (output, result);
+    }
+
+  if (o->width > 0.1 && o->opacity > 0.0001)
+    {
+      gegl_color_get_rgba (o->color, &r,&g,&b,&a);
+      a *= o->opacity;
+      if (a>0.001)
+          need_stroke=TRUE;
+    }
+
+  if (need_stroke)   
+    {
+      GStaticMutex mutex = G_STATIC_MUTEX_INIT;
+      cairo_t *cr;
+      cairo_surface_t *surface;
+      guchar *data;
+      
+      g_static_mutex_lock (&mutex);
+      data = (void*)gegl_buffer_linear_open (output, result, NULL, babl_format ("B'aG'aR'aA u8"));
+      surface = cairo_image_surface_create_for_data (data,
+                                                     CAIRO_FORMAT_ARGB32,
+                                                     result->width,
+                                                     result->height,
+                                                     result->width * 4);
+      
+      cr = cairo_create (surface);
+      
+      cairo_translate (cr, -result->x, -result->y);
+      
+      cairo_set_line_width  (cr, o->width);
+      cairo_set_line_cap    (cr, CAIRO_LINE_CAP_ROUND);
+      cairo_set_line_join   (cr, CAIRO_LINE_JOIN_ROUND);
+      
+      gegl_path_cairo_play (o->d, cr);
+      cairo_set_source_rgba (cr, r,g,b,a);
+      cairo_stroke (cr);      
+      cairo_destroy (cr);
+      
+      gegl_buffer_linear_close (output, data);
+      g_static_mutex_unlock (&mutex);
+    }
+  return  TRUE;
+} 
+static void foreach_cairo (const GeglPathItem *knot,
+                           gpointer              cr)
+{
+  switch (knot->type)
+    {
+      case 'M':
+        cairo_move_to (cr, knot->point[0].x, knot->point[0].y);
+        break;
+      case 'L':
+        cairo_line_to (cr, knot->point[0].x, knot->point[0].y);
+        break;
+      case 'C':
+        cairo_curve_to (cr, knot->point[0].x, knot->point[0].y,
+                            knot->point[1].x, knot->point[1].y,
+                            knot->point[2].x, knot->point[2].y);
+        break;
+      case 'z':
+        cairo_close_path (cr);
+        break;
+      default:
+        g_print ("%s uh?:%c\n", G_STRLOC, knot->type);
+    }
+}
+
+static void gegl_path_cairo_play (GeglPath *path,
+                                    cairo_t *cr)
+{
+  gegl_path_foreach_flat (path, foreach_cairo, cr);
+}
+
+static GeglNode *detect (GeglOperation *operation,
+                         gint           x,
+                         gint           y)
+{
+  GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
+  cairo_t *cr;
+  cairo_surface_t *surface;
+  gchar *data = "     ";
+  gboolean result = FALSE;
+
+  surface = cairo_image_surface_create_for_data ((guchar*)data,
+                                                 CAIRO_FORMAT_ARGB32,
+                                                 1,1,4);
+  cr = cairo_create (surface);
+  gegl_path_cairo_play (o->d, cr);
+  cairo_set_line_width (cr, o->width);
+
+
+  if (o->width > 0.1 && o->opacity > 0.0001)
+    result = cairo_in_stroke (cr, x, y);
+
+  cairo_destroy (cr);
+
+  if (result)
+    return operation->node;
+
+  return NULL;
+}
+
+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->get_bounding_box = get_bounding_box;
+  operation_class->prepare = prepare;
+  operation_class->detect = detect;
+  /*operation_class->no_cache = TRUE;*/
+
+  operation_class->name        = "gegl:vector-stroke";
+  operation_class->categories  = "render";
+  operation_class->description = _("Renders a vector stroke");
+}
+
+
+#endif



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