[gegl/paint-system: 6/7] Extract brush-stroke code from gegl-path.c
- From: Damien de Lemeny <ddelemeny src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl/paint-system: 6/7] Extract brush-stroke code from gegl-path.c
- Date: Tue, 20 Jul 2010 18:09:21 +0000 (UTC)
commit c27187c9244eff4fd427bc4dbedff3f30510b5da
Author: Damien de Lemeny <d delemeny gmail com>
Date: Thu Jun 10 15:29:22 2010 +0200
Extract brush-stroke code from gegl-path.c
* remove stroke code from gegl-path.c
* rename gegl:path to gegl:brush-stroke(workshop)
* modify the stroke operation (heavy refactoring)
* prepare the use of brush workers
* modify gegl-paint example to use gegl:brush-stroke
examples/gegl-paint.c | 47 ++---
gegl/property-types/gegl-path.c | 235 ----------------------
operations/external/Makefile.am | 5 +-
operations/external/path.c | 342 --------------------------------
operations/workshop/brush-stroke.c | 382 ++++++++++++++++++++++++++++++++++++
5 files changed, 403 insertions(+), 608 deletions(-)
---
diff --git a/examples/gegl-paint.c b/examples/gegl-paint.c
index 6dfabbf..81a32f4 100644
--- a/examples/gegl-paint.c
+++ b/examples/gegl-paint.c
@@ -23,22 +23,24 @@
#include "util/gegl-view.c"
#include "property-types/gegl-path.h"
-#define HARDNESS 0.2
-#define LINEWIDTH 60.0
-#define COLOR "rgba(0.0,0.0,0.0,0.4)"
+#define SCALE 3.0
+#define SPACING 0.5
+#define HARDNESS 0.2
+#define OPACITY 0.5
+#define COLOR "rgb(0.5,0.3,0.0)"
GtkWidget *window;
GtkWidget *view;
static GeglBuffer *buffer = NULL;
+
static GeglNode *gegl = NULL;
static GeglNode *out = NULL;
static GeglNode *top = NULL;
+static GeglNode *stroke = NULL;
+
static gboolean pen_down = FALSE;
static GeglPath *vector = NULL;
-static GeglNode *over = NULL;
-static GeglNode *stroke = NULL;
-
static gboolean paint_press (GtkWidget *widget,
GdkEventButton *event)
@@ -47,16 +49,16 @@ static gboolean paint_press (GtkWidget *widget,
{
vector = gegl_path_new ();
- over = gegl_node_new_child (gegl, "operation", "gegl:over", NULL);
- stroke = gegl_node_new_child (gegl, "operation", "gegl:path",
+ stroke = gegl_node_new_child (gegl, "operation", "gegl:brush-stroke",
"d", vector,
- "fill-opacity", 0.0,
- "stroke", gegl_color_new (COLOR),
- "stroke-width", LINEWIDTH,
- "stroke-hardness", HARDNESS,
+ "color", gegl_color_new (COLOR),
+ "scale", SCALE,
+ "hardness", HARDNESS,
+ "spacing", SPACING,
+ "opacity", OPACITY,
NULL);
- gegl_node_link_many (top, over, out, NULL);
- gegl_node_connect_to (stroke, "output", over, "aux");
+ gegl_node_link_many (top, stroke, out, NULL);
+
gegl_path_append (vector, 'M', event->x, event->y);
pen_down = TRUE;
@@ -89,35 +91,26 @@ static gboolean paint_release (GtkWidget *widget,
{
if (event->button == 1)
{
- gdouble x0, x1, y0, y1;
GeglProcessor *processor;
GeglNode *writebuf;
- GeglRectangle roi;
-
- gegl_path_get_bounds (vector, &x0, &x1, &y0, &y1);
-
- roi.x = x0 - LINEWIDTH;
- roi.y = y0 - LINEWIDTH;
- roi.width = x1 - x0 + LINEWIDTH * 2;
- roi.height = y1 - y0 + LINEWIDTH * 2;
+ GeglRectangle roi = gegl_node_get_bounding_box(stroke);
writebuf = gegl_node_new_child (gegl,
"operation", "gegl:write-buffer",
"buffer", buffer,
NULL);
- gegl_node_link_many (over, writebuf, NULL);
+ gegl_node_link_many (stroke, writebuf, NULL);
processor = gegl_node_new_processor (writebuf, &roi);
while (gegl_processor_work (processor, NULL)) ;
gegl_processor_destroy (processor);
- g_object_unref (writebuf);
gegl_node_link_many (top, out, NULL);
- g_object_unref (over);
+
g_object_unref (stroke);
+ g_object_unref (writebuf);
- over = NULL;
stroke = NULL;
pen_down = FALSE;
diff --git a/gegl/property-types/gegl-path.c b/gegl/property-types/gegl-path.c
index 50af23a..ea06760 100644
--- a/gegl/property-types/gegl-path.c
+++ b/gegl/property-types/gegl-path.c
@@ -2006,241 +2006,6 @@ fill_close: /* label used for goto to close last segment */
}
}
-typedef struct StampStatic {
- gboolean valid;
- Babl *format;
- gfloat *buf;
- gdouble radius;
-}StampStatic;
-
-#if 0
-void gegl_path_stamp (GeglBuffer *buffer,
- gdouble x,
- gdouble y,
- gdouble radius,
- gdouble hardness,
- GeglColor *color,
- gdouble opacity);
-#endif
-
-static void gegl_path_stamp (GeglBuffer *buffer,
- const GeglRectangle *clip_rect,
- gdouble x,
- gdouble y,
- gdouble radius,
- gdouble hardness,
- GeglColor *color,
- gdouble opacity)
-{
- gfloat col[4];
- static StampStatic s = {FALSE,}; /* XXX:
- we will ultimately leak the last valid
- cached brush. */
-
- GeglRectangle temp;
- GeglRectangle roi;
-
- roi.x = floor(x-radius);
- roi.y = floor(y-radius);
- roi.width = ceil (x+radius) - floor (x-radius);
- roi.height = ceil (y+radius) - floor (y-radius);
-
- gegl_color_get_rgba4f (color, col);
-
- /* bail out if we wouldn't leave a mark on the buffer */
- if (!gegl_rectangle_intersect (&temp, &roi, clip_rect))
- {
- return;
- }
-
- if (s.format == NULL)
- s.format = babl_format ("RaGaBaA float");
-
- if (s.buf == NULL ||
- s.radius != radius)
- {
- if (s.buf != NULL)
- g_free (s.buf);
- /* allocate a little bit more, just in case due to rounding errors and
- * such */
- s.buf = g_malloc (4*4* (roi.width + 2 ) * (roi.height + 2));
- s.radius = radius;
- s.valid = TRUE;
- }
- g_assert (s.buf);
-
- gegl_buffer_get_unlocked (buffer, 1.0, &roi, s.format, s.buf, 0);
-
- {
- gint u, v;
- gint i=0;
-
- gfloat radius_squared = radius * radius;
- gfloat inner_radius_squared = (radius * hardness)*(radius * hardness);
- gfloat soft_range = radius_squared - inner_radius_squared;
-
- for (v= roi.y; v < roi.y + roi.height ; v++)
- {
- gfloat vy2 = (v-y)*(v-y);
- for (u= roi.x; u < roi.x + roi.width; u++)
- {
- gfloat o = (u-x) * (u-x) + vy2;
-
- if (o < inner_radius_squared)
- o = col[3];
- else if (o < radius_squared)
- {
- o = (1.0 - (o-inner_radius_squared) / (soft_range)) * col[3];
- }
- else
- {
- o=0.0;
- }
- if (o!=0.0)
- {
- gint c;
- o = o*opacity;
- for (c=0;c<4;c++)
- s.buf[i*4+c] = (s.buf[i*4+c] * (1.0-o) + col[c] * o);
- }
- i++;
- }
- }
- }
- gegl_buffer_set_unlocked (buffer, &roi, s.format, s.buf, 0);
-}
-
-
-void gegl_path_stroke (GeglBuffer *buffer,
- const GeglRectangle *clip_rect,
- GeglPath *vector,
- GeglColor *color,
- gdouble linewidth,
- gdouble hardness,
- gdouble opacity);
-
-
-void gegl_path_stroke (GeglBuffer *buffer,
- const GeglRectangle *clip_rect,
- GeglPath *vector,
- GeglColor *color,
- gdouble linewidth,
- gdouble hardness,
- gdouble opacity)
-{
- GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (vector);
- gfloat traveled_length = 0;
- gfloat need_to_travel = 0;
- gfloat x = 0,y = 0;
- GeglPathList *iter;
- gdouble xmin, xmax, ymin, ymax;
- GeglRectangle extent;
-
- if (!vector)
- return;
-
- if (!clip_rect)
- {
- g_print ("using buffer extent\n");
- clip_rect = gegl_buffer_get_extent (buffer);
- }
-
- ensure_flattened (vector);
-
- iter = priv->flat_path;
- gegl_path_get_bounds (vector, &xmin, &xmax, &ymin, &ymax);
- extent.x = floor (xmin);
- extent.y = floor (ymin);
- extent.width = ceil (xmax) - extent.x;
- extent.height = ceil (ymax) - extent.y;
-
- if (!gegl_rectangle_intersect (&extent, &extent, clip_rect))
- {
- return;
- }
- if (gegl_buffer_is_shared (buffer))
- while (!gegl_buffer_try_lock (buffer));
-
- /*gegl_buffer_clear (buffer, &extent);*/
-
- while (iter)
- {
- /*fprintf (stderr, "%c, %i %i\n", iter->d.type, iter->d.point[0].x, iter->d.point[0].y);*/
- switch (iter->d.type)
- {
- case 'M':
- x = iter->d.point[0].x;
- y = iter->d.point[0].y;
- need_to_travel = 0;
- traveled_length = 0;
- break;
- case 'L':
- {
- Point a,b;
-
- gfloat spacing;
- gfloat local_pos;
- gfloat distance;
- gfloat offset;
- gfloat leftover;
- gfloat radius = linewidth / 2.0;
-
-
- a.x = x;
- a.y = y;
-
- b.x = iter->d.point[0].x;
- b.y = iter->d.point[0].y;
-
- spacing = 0.2 * radius;
-
- distance = point_dist (&a, &b);
-
- leftover = need_to_travel - traveled_length;
- offset = spacing - leftover;
-
- local_pos = offset;
-
- if (distance > 0)
- for (;
- local_pos <= distance;
- local_pos += spacing)
- {
- Point spot;
- gfloat ratio = local_pos / distance;
- gfloat radius = linewidth/2;
-
- lerp (&spot, &a, &b, ratio);
-
- gegl_path_stamp (buffer, clip_rect,
- spot.x, spot.y, radius, hardness, color, opacity);
-
- traveled_length += spacing;
- }
-
- need_to_travel += distance;
-
- x = b.x;
- y = b.y;
- }
-
- break;
- case 'u':
- g_error ("stroking uninitialized path\n");
- break;
- case 's':
- break;
- default:
- g_error ("can't stroke for instruction: %i\n", iter->d.type);
- break;
- }
- iter=iter->next;
- }
-
- if (gegl_buffer_is_shared (buffer))
- gegl_buffer_unlock (buffer);
-}
-
gint gegl_path_type_get_n_items (gchar type)
{
InstructionInfo *info = lookup_instruction_info (type);
diff --git a/operations/external/Makefile.am b/operations/external/Makefile.am
index 81d8956..cf2d80b 100644
--- a/operations/external/Makefile.am
+++ b/operations/external/Makefile.am
@@ -12,10 +12,7 @@ text_la_CFLAGS = $(AM_CFLAGS) $(PANGOCAIRO_CFLAGS)
endif
if HAVE_CAIRO
-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)
+ops += vector-fill.la vector-stroke.la
vector_fill_la_SOURCES = vector-fill.c
vector_fill_la_LIBADD = $(op_libs) $(CAIRO_LIBS)
vector_fill_la_CFLAGS = $(AM_CFLAGS) $(CAIRO_CFLAGS)
diff --git a/operations/workshop/brush-stroke.c b/operations/workshop/brush-stroke.c
new file mode 100644
index 0000000..c62e88a
--- /dev/null
+++ b/operations/workshop/brush-stroke.c
@@ -0,0 +1,382 @@
+/* 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_path (d, _("Vector"),
+ _("A GeglVector representing the path of the stroke"))
+gegl_chant_string (transform,_("Transform"), "",
+ _("svg style description of transform."))
+
+/* Brush shape parameters - FIXME : those should not be in the stroke operation
+ * stroking is about where to put dabs of paint, not modifying the brush
+ * use a brush dab op instead
+ */
+gegl_chant_double (scale, _("Scale Factor"), 0.0, 10.0, 2.0,
+ _("Brush Scale Factor"))
+gegl_chant_double (hardness, _("Hardness"), 0.0, 1.0, 0.6,
+ _("Brush Hardness, 0.0 for soft 1.0 for hard."))
+gegl_chant_double (angle, _("Angle"), 0.0, 360.0, 0.0,
+ _("Brush Angle."))
+gegl_chant_double (aspect, _("Aspect Ratio"), 0.1, 10.0, 1.0,
+ _("Brush Aspect, 0.1 for pancake 10.0 for spike."))
+gegl_chant_double (force, _("Force"), 0.0, 1.0, 0.6,
+ _("Brush Force."))
+
+/* Stroke shape parameters */
+gegl_chant_double (spacing, _("Spacing"), 0.01, 200, 0.6,
+ _("Stroke Spacing."))
+gegl_chant_double (jitter, _("Jitter"), 0.0, 1.0, 0.6,
+ _("Stroke Jitter"))
+
+gegl_chant_color (color, _("Color"), "rgba(0.0,0.0,0.0,0.0)",
+ _("Color of paint to use for stroking."))
+gegl_chant_double (opacity, _("Opacity"), -2.0, 2.0, 1.0,
+ _("Stroke Opacity."))
+
+#else
+
+#define GEGL_CHANT_TYPE_FILTER
+#define GEGL_CHANT_C_FILE "brush-stroke.c"
+
+#include "gegl-plugin.h"
+#include "gegl-buffer-private.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 <stdio.h>
+
+
+typedef struct StampStatic {
+ gboolean valid;
+ Babl *format;
+ gfloat *buf;
+ gdouble radius;
+}StampStatic;
+
+static void gegl_path_stamp (GeglBuffer *buffer,
+ const GeglRectangle *clip_rect,
+ gdouble x,
+ gdouble y,
+ gdouble radius,
+ gdouble hardness,
+ GeglColor *color,
+ gdouble opacity)
+{
+ gfloat col[4];
+ static StampStatic s = {FALSE,}; /* XXX:
+ we will ultimately leak the last valid
+ cached brush. */
+
+ GeglRectangle temp;
+ GeglRectangle roi;
+
+ roi.x = floor(x-radius);
+ roi.y = floor(y-radius);
+ roi.width = ceil (x+radius) - floor (x-radius);
+ roi.height = ceil (y+radius) - floor (y-radius);
+
+ gegl_color_get_rgba4f (color, col);
+
+ /* bail out if we wouldn't leave a mark on the buffer */
+ if (!gegl_rectangle_intersect (&temp, &roi, clip_rect))
+ {
+ return;
+ }
+
+ if (s.format == NULL)
+ s.format = babl_format ("RaGaBaA float");
+
+ if (s.buf == NULL ||
+ s.radius != radius)
+ {
+ if (s.buf != NULL)
+ g_free (s.buf);
+ /* allocate a little bit more, just in case due to rounding errors and
+ * such */
+ s.buf = g_malloc (4*4* (roi.width + 2 ) * (roi.height + 2));
+ s.radius = radius;
+ s.valid = TRUE;
+ }
+ g_assert (s.buf);
+
+ gegl_buffer_get_unlocked (buffer, 1.0, &roi, s.format, s.buf, 0);
+
+ {
+ gint u, v;
+ gint i=0;
+
+ gfloat radius_squared = radius * radius;
+ gfloat inner_radius_squared = (radius * hardness)*(radius * hardness);
+ gfloat soft_range = radius_squared - inner_radius_squared;
+
+ for (v= roi.y; v < roi.y + roi.height ; v++)
+ {
+ gfloat vy2 = (v-y)*(v-y);
+ for (u= roi.x; u < roi.x + roi.width; u++)
+ {
+ gfloat o = (u-x) * (u-x) + vy2;
+
+ if (o < inner_radius_squared)
+ o = col[3];
+ else if (o < radius_squared)
+ {
+ o = (1.0 - (o-inner_radius_squared) / (soft_range)) * col[3];
+ }
+ else
+ {
+ o=0.0;
+ }
+ if (o!=0.0)
+ {
+ gint c;
+ o = o*opacity;
+ for (c=0;c<4;c++)
+ s.buf[i*4+c] = (s.buf[i*4+c] * (1.0-o) + col[c] * o);
+ }
+ i++;
+ }
+ }
+ }
+ gegl_buffer_set_unlocked (buffer, &roi, s.format, s.buf, 0);
+}
+
+static void path_changed (GeglPath *path,
+ const GeglRectangle *roi,
+ gpointer userdata)
+{
+ GeglRectangle rect = *roi;
+ GeglChantO *o = GEGL_CHANT_PROPERTIES (userdata);
+ gfloat stroke_width = o->scale*5.0*2.0;
+ /* invalidate the incoming rectangle */
+
+ rect.x -= stroke_width/2.;
+ rect.y -= stroke_width/2;
+ rect.width += stroke_width;
+ rect.height += stroke_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"));
+ gegl_operation_set_format (operation, "input", babl_format ("RaGaBaA float"));
+ /*
+ gegl_operation_set_format (operation, "aux", babl_format ("Y 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;
+ gfloat stroke_width = o->scale*5.0;
+
+ in_rect = gegl_operation_source_get_bounding_box (operation, "input");
+
+ gegl_path_get_bounds (o->d, &x0, &x1, &y0, &y1);
+ defined.x = x0 - stroke_width/2;
+ defined.y = y0 - stroke_width/2;
+ defined.width = x1 - x0 + stroke_width;
+ defined.height = y1 - y0 + stroke_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 gboolean
+process (GeglOperation *operation,
+ GeglBuffer *input,
+ GeglBuffer *output,
+ const GeglRectangle *result)
+{
+ GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
+ GeglPath *path = o->d;
+
+ gfloat spacing, length, radius, hardness, opacity;
+ gdouble pos = 0;
+ gboolean incremental = TRUE;
+
+ if (!o->d)
+ return TRUE;
+
+ if (input)
+ {
+ gegl_buffer_copy (input, result, output, result);
+ }
+ else
+ {
+ gegl_buffer_clear (output, result);
+ }
+
+ radius = 5 * o->scale;
+ hardness = o->hardness;
+
+ length = gegl_path_get_length(path);
+
+
+ if (gegl_buffer_is_shared (output))
+ while (!gegl_buffer_try_lock (output));
+
+ do
+ {
+ gdouble x, y;
+
+ /* Set the brush tip : set brush worker's pos */
+ if (! gegl_path_calc (path,pos,&x,&y)) break;
+ /* Compute coordinates : jitter ? */
+
+ if (incremental)
+ {
+ /* Blend into output : set blend worker pos and process */
+ gegl_path_stamp (output, result,
+ x, y, radius, hardness, o->color, o->opacity);
+ }
+ else
+ {
+ /* Blend into a mask */
+ }
+
+ /* Calculate next position */
+ spacing = o->spacing * radius;
+ pos = pos + spacing;
+ }
+ while (pos < length);
+
+ if (! incremental)
+ {
+ /* Blend the mask into output */
+ }
+
+ if (gegl_buffer_is_shared (output))
+ gegl_buffer_unlock (output);
+
+ return TRUE;
+}
+
+static GeglNode *detect (GeglOperation *operation,
+ gint x,
+ gint y)
+{
+#if 0
+ 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->stroke_width);
+
+
+ if (o->stroke_width > 0.1 && o->stroke_opacity > 0.0001)
+ result = cairo_in_stroke (cr, x, y);
+
+
+ cairo_destroy (cr);
+
+ if (result)
+ return operation->node;
+
+#endif
+ 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:brush-stroke";
+ operation_class->categories = "render";
+ operation_class->description = _("Renders a brush stroke");
+#if 0
+ operation_class->get_cached_region = (void*)get_cached_region;
+#endif
+}
+
+
+#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]