1 /* This file is an image processing operation for GEGL
2  *
3  * GEGL is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 3 of the License, or (at your option) any later version.
7  *
8  * GEGL is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
15  *
16  * Copyright 2006, 2018 Øyvind Kolås <pippin@gimp.org>
17  */
18 
19 
20 #include "config.h"
21 #include <glib/gi18n-lib.h>
22 
23 
24 #ifdef GEGL_PROPERTIES
25 
26 property_color  (color, _("Color"),  "rgba(0.0,0.0,0.0,1.0)")
27   description(_("Color of paint to use for filling."))
28 
29 property_double (opacity,  _("Opacity"), 1.0)
30   description(_("The fill opacity to use."))
31   value_range (-2.0, 2.0)
32 
33   /* XXX: replace with enum? */
34 property_string (fill_rule,_("Fill rule."), "nonzero")
35   description(_("how to determine what to fill (nonzero|evenodd)"))
36 
37 property_string (transform,_("Transform"), "")
38   description (_("svg style description of transform."))
39 
40 property_path (d, _("Vector"), NULL)
41   description (_("A GeglVector representing the path of the stroke"))
42 
43 #else
44 
45 #define GEGL_OP_FILTER
46 #define GEGL_OP_NAME     vector_fill
47 #define GEGL_OP_C_SOURCE vector-fill.c
48 
49 #include "gegl-plugin.h"
50 
51 /* the path api isn't public yet */
52 #include "property-types/gegl-path.h"
53 static void path_changed (GeglPath *path,
54                           const GeglRectangle *roi,
55                           gpointer userdata);
56 
57 #include "gegl-op.h"
58 #include "ctx/ctx.h"
59 
60 static void path_changed (GeglPath *path,
61                           const GeglRectangle *roi,
62                           gpointer userdata)
63 {
64   GeglProperties    *o   = GEGL_PROPERTIES (userdata);
65   GeglRectangle rect;
66   gdouble        x0, x1, y0, y1;
67 
68   gegl_path_get_bounds(o->d, &x0, &x1, &y0, &y1);
69   rect.x = x0 - 1;
70   rect.y = y0 - 1;
71   rect.width = x1 - x0 + 2;
72   rect.height = y1 - y0 + 2;
73 
74   gegl_operation_invalidate (userdata, &rect, TRUE);
75 };
76 
77 static void
78 prepare (GeglOperation *operation)
79 {
80   GeglProperties *o = GEGL_PROPERTIES (operation);
81   const Babl *input_format  = gegl_operation_get_source_format (operation, "input");
82   const Babl *input_space   = input_format?babl_format_get_space (input_format):NULL;
83   const Babl *color_format  = gegl_color_get_format (o->color);
84   BablModelFlag model_flags = input_format?babl_get_model_flags (input_format):0;
85 
86   if (input_space == NULL){
87     input_space = babl_format_get_space (color_format);
88     model_flags = babl_get_model_flags (color_format);
89   }
90 
91   if (model_flags & BABL_MODEL_FLAG_CMYK)
92   {
93     gegl_operation_set_format (operation, "output", babl_format_with_space ("camayakaA float", input_space));
94   }
95   else
96   {
97     gegl_operation_set_format (operation, "output", babl_format_with_space ("RaGaBaA float", input_space));
98   }
99 
100   if (o->transform && o->transform[0] != '\0')
101     {
102       GeglMatrix3 matrix;
103       gegl_matrix3_parse_string (&matrix, o->transform);
104       gegl_path_set_matrix (o->d, &matrix);
105     }
106 }
107 
108 static GeglRectangle
109 get_bounding_box (GeglOperation *operation)
110 {
111   GeglProperties    *o       = GEGL_PROPERTIES (operation);
112   GeglRectangle  defined = { 0, 0, 512, 512 };
113   GeglRectangle *in_rect;
114   gdouble        x0, x1, y0, y1;
115 
116   in_rect =  gegl_operation_source_get_bounding_box (operation, "input");
117 
118   gegl_path_get_bounds (o->d, &x0, &x1, &y0, &y1);
119   defined.x      = x0;
120   defined.y      = y0;
121   defined.width  = x1 - x0;
122   defined.height = y1 - y0;
123 
124   if (in_rect)
125     {
126       gegl_rectangle_bounding_box (&defined, &defined, in_rect);
127     }
128 
129   return defined;
130 }
131 
132 static void gegl_path_ctx_play (GeglPath *path,
133                                 Ctx      *ctx);
134 
135 static gboolean
136 process (GeglOperation       *operation,
137          GeglBuffer          *input,
138          GeglBuffer          *output,
139          const GeglRectangle *result,
140          gint                 level)
141 {
142   GeglProperties *o = GEGL_PROPERTIES (operation);
143   gboolean need_fill = FALSE;
144   const Babl *format =  gegl_operation_get_format (operation, "output");
145   const Babl *device_space  =  babl_format_get_space (format);
146   gdouble color[5] = {0, 0, 0, 0, 0};
147   int is_cmyk = babl_get_model_flags (format) & BABL_MODEL_FLAG_CMYK ? 1 : 0;
148   const Babl *color_format  = gegl_color_get_format (o->color);
149   const Babl *color_space   = babl_format_get_space (color_format);
150 
151   char device_space_ascii[64]="";
152   char color_space_ascii[64]="";
153 
154   if (device_space)
155     sprintf (device_space_ascii, "%p", device_space);
156   if (color_space)
157     sprintf (color_space_ascii, "%p", color_space);
158 
159   if (input)
160     {
161       gegl_buffer_copy (input, result, GEGL_ABYSS_NONE, output, result);
162     }
163   else
164     {
165       gegl_buffer_clear (output, result);
166     }
167 
168   if (o->opacity > 0.0001 && o->color)
169     {
170       if (is_cmyk)
171       {
172         gegl_color_get_pixel (o->color, babl_format_with_space ("CMYKA double", color_space), color);
173         color[4] *= o->opacity;
174         if (color[4] > 0.001)
175           need_fill=TRUE;
176       }
177       else
178       {
179         gegl_color_get_pixel (o->color, babl_format_with_space ("R'G'B'A double", color_space), color);
180         color[3] *= o->opacity;
181         if (color[3] > 0.001)
182           need_fill=TRUE;
183       }
184     }
185 
186   if (need_fill)
187     {
188       static GMutex mutex = { 0, };
189 
190       g_mutex_lock (&mutex);
191 
192       {
193         guchar *data = gegl_buffer_linear_open (output, result, NULL,
194                                                 format);
195         Ctx *ctx;
196         if (is_cmyk)
197           ctx = ctx_new_for_framebuffer (data, result->width, result->height,
198                                          result->width * 5 * 4, CTX_FORMAT_CMYKAF);
199         else
200           ctx = ctx_new_for_framebuffer (data, result->width, result->height,
201                                          result->width * 4 * 4, CTX_FORMAT_RGBAF);
202 
203         if (!is_cmyk)
204         {
205           if (device_space)
206             ctx_colorspace (ctx, CTX_COLOR_SPACE_DEVICE_RGB, (guint8*)device_space_ascii, strlen (device_space_ascii)+1);
207           if (color_space)
208             ctx_colorspace (ctx, CTX_COLOR_SPACE_USER_RGB, (guint8*)color_space_ascii, strlen (color_space_ascii)+1);
209         }
210 
211         ctx_translate (ctx, -result->x, -result->y);
212         if (g_str_equal (o->fill_rule, "evenodd"))
213           ctx_fill_rule (ctx, CTX_FILL_RULE_EVEN_ODD);
214 
215         gegl_path_ctx_play (o->d, ctx);
216 
217         if (is_cmyk)
218             ctx_cmyka (ctx, color[0], color[1], color[2], color[3], color[4]);
219         else
220             ctx_rgba (ctx, color[0], color[1], color[2], color[3]);
221         ctx_fill (ctx);
222         ctx_free (ctx);
223 
224         gegl_buffer_linear_close (output, data);
225       }
226       g_mutex_unlock (&mutex);
227     }
228 
229   return  TRUE;
230 }
231 
232 static void foreach_ctx (const GeglPathItem *knot,
233                           gpointer           ctx)
234 {
235   switch (knot->type)
236     {
237       case 'M':
238         ctx_move_to (ctx, knot->point[0].x, knot->point[0].y);
239         break;
240       case 'L':
241         ctx_line_to (ctx, knot->point[0].x, knot->point[0].y);
242         break;
243       case 'C':
244         ctx_curve_to (ctx, knot->point[0].x, knot->point[0].y,
245                            knot->point[1].x, knot->point[1].y,
246                            knot->point[2].x, knot->point[2].y);
247         break;
248       case 'z':
249         ctx_close_path (ctx);
250         break;
251       default:
252         g_print ("%s uh?:%c\n", G_STRLOC, knot->type);
253     }
254 }
255 
256 static void gegl_path_ctx_play (GeglPath *path,
257                                 Ctx       *ctx)
258 {
259   gegl_path_foreach_flat (path, foreach_ctx, ctx);
260 }
261 
262 static GeglNode *detect (GeglOperation *operation,
263                          gint           x,
264                          gint           y)
265 {
266   GeglProperties *o = GEGL_PROPERTIES (operation);
267   Ctx     *ctx = ctx_new ();
268   gboolean result = FALSE;
269 
270   gegl_path_ctx_play (o->d, ctx);
271 
272   if (!result)
273     {
274       if (o->d)
275         {
276           result = ctx_in_fill (ctx, x, y);
277         }
278     }
279   ctx_free (ctx);
280 
281   if (result)
282     return operation->node;
283 
284   return NULL;
285 }
286 
287 static void
288 gegl_op_class_init (GeglOpClass *klass)
289 {
290   GeglOperationClass       *operation_class;
291   GeglOperationFilterClass *filter_class;
292   gchar                    *composition = "<?xml version='1.0' encoding='UTF-8'?>"
293     "<gegl>"
294     "<node operation='gegl:crop' width='200' height='200'/>"
295     "<node operation='gegl:over'>"
296     "<node operation='gegl:translate' x='40' y='40'/>"
297     "<node operation='gegl:fill-path'>"
298     "  <params>"
299     "    <param name='color'>rgb(0.0, 0.6, 1.0)</param>"
300     "    <param name='d'>"
301     "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"
302     "                    </param>"
303     "  </params>"
304     "</node>"
305     "</node>"
306     "<node operation='gegl:checkerboard' color1='rgb(0.25,0.25,0.25)' color2='rgb(0.75,0.75,0.75)'/>"
307     "</gegl>";
308 
309 
310   operation_class = GEGL_OPERATION_CLASS (klass);
311   filter_class    = GEGL_OPERATION_FILTER_CLASS (klass);
312 
313   filter_class->process = process;
314   operation_class->get_bounding_box = get_bounding_box;
315   operation_class->prepare = prepare;
316   operation_class->detect = detect;
317 
318   gegl_operation_class_set_keys (operation_class,
319     "name",        "gegl:fill-path",
320     "title",       _("Fill Path"),
321     "categories",  "render:vector",
322     "reference-hash", "941dcd6c90739ee2d8a19593c07828b1",
323     "description", _("Renders a filled region"),
324     "reference-composition", composition,
325     NULL);
326 }
327 
328 #endif
329