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