1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimpvectors.c
5 * Copyright (C) 2002 Simon Budig <simon@gimp.org>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include <cairo.h>
24 #include <gegl.h>
25 #include <gdk-pixbuf/gdk-pixbuf.h>
26
27 #include "libgimpcolor/gimpcolor.h"
28 #include "libgimpmath/gimpmath.h"
29
30 #include "vectors-types.h"
31
32 #include "core/gimp.h"
33 #include "core/gimp-transform-utils.h"
34 #include "core/gimpbezierdesc.h"
35 #include "core/gimpchannel-select.h"
36 #include "core/gimpcontainer.h"
37 #include "core/gimpcontext.h"
38 #include "core/gimpdrawable-fill.h"
39 #include "core/gimpdrawable-stroke.h"
40 #include "core/gimperror.h"
41 #include "core/gimpimage.h"
42 #include "core/gimpimage-undo-push.h"
43 #include "core/gimpmarshal.h"
44 #include "core/gimppaintinfo.h"
45 #include "core/gimpstrokeoptions.h"
46
47 #include "paint/gimppaintcore-stroke.h"
48 #include "paint/gimppaintoptions.h"
49
50 #include "gimpanchor.h"
51 #include "gimpstroke.h"
52 #include "gimpvectors.h"
53 #include "gimpvectors-preview.h"
54
55 #include "gimp-intl.h"
56
57
58 enum
59 {
60 FREEZE,
61 THAW,
62 LAST_SIGNAL
63 };
64
65
66 static void gimp_vectors_finalize (GObject *object);
67
68 static gint64 gimp_vectors_get_memsize (GimpObject *object,
69 gint64 *gui_size);
70
71 static gboolean gimp_vectors_is_attached (GimpItem *item);
72 static GimpItemTree * gimp_vectors_get_tree (GimpItem *item);
73 static gboolean gimp_vectors_bounds (GimpItem *item,
74 gdouble *x,
75 gdouble *y,
76 gdouble *width,
77 gdouble *height);
78 static GimpItem * gimp_vectors_duplicate (GimpItem *item,
79 GType new_type);
80 static void gimp_vectors_convert (GimpItem *item,
81 GimpImage *dest_image,
82 GType old_type);
83 static void gimp_vectors_translate (GimpItem *item,
84 gdouble offset_x,
85 gdouble offset_y,
86 gboolean push_undo);
87 static void gimp_vectors_scale (GimpItem *item,
88 gint new_width,
89 gint new_height,
90 gint new_offset_x,
91 gint new_offset_y,
92 GimpInterpolationType interp_type,
93 GimpProgress *progress);
94 static void gimp_vectors_resize (GimpItem *item,
95 GimpContext *context,
96 GimpFillType fill_type,
97 gint new_width,
98 gint new_height,
99 gint offset_x,
100 gint offset_y);
101 static void gimp_vectors_flip (GimpItem *item,
102 GimpContext *context,
103 GimpOrientationType flip_type,
104 gdouble axis,
105 gboolean clip_result);
106 static void gimp_vectors_rotate (GimpItem *item,
107 GimpContext *context,
108 GimpRotationType rotate_type,
109 gdouble center_x,
110 gdouble center_y,
111 gboolean clip_result);
112 static void gimp_vectors_transform (GimpItem *item,
113 GimpContext *context,
114 const GimpMatrix3 *matrix,
115 GimpTransformDirection direction,
116 GimpInterpolationType interp_type,
117 GimpTransformResize clip_result,
118 GimpProgress *progress);
119 static GimpTransformResize
120 gimp_vectors_get_clip (GimpItem *item,
121 GimpTransformResize clip_result);
122 static gboolean gimp_vectors_fill (GimpItem *item,
123 GimpDrawable *drawable,
124 GimpFillOptions *fill_options,
125 gboolean push_undo,
126 GimpProgress *progress,
127 GError **error);
128 static gboolean gimp_vectors_stroke (GimpItem *item,
129 GimpDrawable *drawable,
130 GimpStrokeOptions *stroke_options,
131 gboolean push_undo,
132 GimpProgress *progress,
133 GError **error);
134 static void gimp_vectors_to_selection (GimpItem *item,
135 GimpChannelOps op,
136 gboolean antialias,
137 gboolean feather,
138 gdouble feather_radius_x,
139 gdouble feather_radius_y);
140
141 static void gimp_vectors_real_freeze (GimpVectors *vectors);
142 static void gimp_vectors_real_thaw (GimpVectors *vectors);
143 static void gimp_vectors_real_stroke_add (GimpVectors *vectors,
144 GimpStroke *stroke);
145 static void gimp_vectors_real_stroke_remove (GimpVectors *vectors,
146 GimpStroke *stroke);
147 static GimpStroke * gimp_vectors_real_stroke_get (GimpVectors *vectors,
148 const GimpCoords *coord);
149 static GimpStroke *gimp_vectors_real_stroke_get_next(GimpVectors *vectors,
150 GimpStroke *prev);
151 static gdouble gimp_vectors_real_stroke_get_length (GimpVectors *vectors,
152 GimpStroke *prev);
153 static GimpAnchor * gimp_vectors_real_anchor_get (GimpVectors *vectors,
154 const GimpCoords *coord,
155 GimpStroke **ret_stroke);
156 static void gimp_vectors_real_anchor_delete (GimpVectors *vectors,
157 GimpAnchor *anchor);
158 static gdouble gimp_vectors_real_get_length (GimpVectors *vectors,
159 const GimpAnchor *start);
160 static gdouble gimp_vectors_real_get_distance (GimpVectors *vectors,
161 const GimpCoords *coord);
162 static gint gimp_vectors_real_interpolate (GimpVectors *vectors,
163 GimpStroke *stroke,
164 gdouble precision,
165 gint max_points,
166 GimpCoords *ret_coords);
167
168 static GimpBezierDesc * gimp_vectors_make_bezier (GimpVectors *vectors);
169 static GimpBezierDesc * gimp_vectors_real_make_bezier (GimpVectors *vectors);
170
171
172 G_DEFINE_TYPE (GimpVectors, gimp_vectors, GIMP_TYPE_ITEM)
173
174 #define parent_class gimp_vectors_parent_class
175
176 static guint gimp_vectors_signals[LAST_SIGNAL] = { 0 };
177
178
179 static void
gimp_vectors_class_init(GimpVectorsClass * klass)180 gimp_vectors_class_init (GimpVectorsClass *klass)
181 {
182 GObjectClass *object_class = G_OBJECT_CLASS (klass);
183 GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
184 GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass);
185 GimpItemClass *item_class = GIMP_ITEM_CLASS (klass);
186
187 gimp_vectors_signals[FREEZE] =
188 g_signal_new ("freeze",
189 G_TYPE_FROM_CLASS (klass),
190 G_SIGNAL_RUN_LAST,
191 G_STRUCT_OFFSET (GimpVectorsClass, freeze),
192 NULL, NULL,
193 gimp_marshal_VOID__VOID,
194 G_TYPE_NONE, 0);
195
196 gimp_vectors_signals[THAW] =
197 g_signal_new ("thaw",
198 G_TYPE_FROM_CLASS (klass),
199 G_SIGNAL_RUN_FIRST,
200 G_STRUCT_OFFSET (GimpVectorsClass, thaw),
201 NULL, NULL,
202 gimp_marshal_VOID__VOID,
203 G_TYPE_NONE, 0);
204
205 object_class->finalize = gimp_vectors_finalize;
206
207 gimp_object_class->get_memsize = gimp_vectors_get_memsize;
208
209 viewable_class->get_new_preview = gimp_vectors_get_new_preview;
210 viewable_class->default_icon_name = "gimp-path";
211
212 item_class->is_attached = gimp_vectors_is_attached;
213 item_class->get_tree = gimp_vectors_get_tree;
214 item_class->bounds = gimp_vectors_bounds;
215 item_class->duplicate = gimp_vectors_duplicate;
216 item_class->convert = gimp_vectors_convert;
217 item_class->translate = gimp_vectors_translate;
218 item_class->scale = gimp_vectors_scale;
219 item_class->resize = gimp_vectors_resize;
220 item_class->flip = gimp_vectors_flip;
221 item_class->rotate = gimp_vectors_rotate;
222 item_class->transform = gimp_vectors_transform;
223 item_class->get_clip = gimp_vectors_get_clip;
224 item_class->fill = gimp_vectors_fill;
225 item_class->stroke = gimp_vectors_stroke;
226 item_class->to_selection = gimp_vectors_to_selection;
227 item_class->default_name = _("Path");
228 item_class->rename_desc = C_("undo-type", "Rename Path");
229 item_class->translate_desc = C_("undo-type", "Move Path");
230 item_class->scale_desc = C_("undo-type", "Scale Path");
231 item_class->resize_desc = C_("undo-type", "Resize Path");
232 item_class->flip_desc = C_("undo-type", "Flip Path");
233 item_class->rotate_desc = C_("undo-type", "Rotate Path");
234 item_class->transform_desc = C_("undo-type", "Transform Path");
235 item_class->fill_desc = C_("undo-type", "Fill Path");
236 item_class->stroke_desc = C_("undo-type", "Stroke Path");
237 item_class->to_selection_desc = C_("undo-type", "Path to Selection");
238 item_class->reorder_desc = C_("undo-type", "Reorder Path");
239 item_class->raise_desc = C_("undo-type", "Raise Path");
240 item_class->raise_to_top_desc = C_("undo-type", "Raise Path to Top");
241 item_class->lower_desc = C_("undo-type", "Lower Path");
242 item_class->lower_to_bottom_desc = C_("undo-type", "Lower Path to Bottom");
243 item_class->raise_failed = _("Path cannot be raised higher.");
244 item_class->lower_failed = _("Path cannot be lowered more.");
245
246 klass->freeze = gimp_vectors_real_freeze;
247 klass->thaw = gimp_vectors_real_thaw;
248
249 klass->stroke_add = gimp_vectors_real_stroke_add;
250 klass->stroke_remove = gimp_vectors_real_stroke_remove;
251 klass->stroke_get = gimp_vectors_real_stroke_get;
252 klass->stroke_get_next = gimp_vectors_real_stroke_get_next;
253 klass->stroke_get_length = gimp_vectors_real_stroke_get_length;
254
255 klass->anchor_get = gimp_vectors_real_anchor_get;
256 klass->anchor_delete = gimp_vectors_real_anchor_delete;
257
258 klass->get_length = gimp_vectors_real_get_length;
259 klass->get_distance = gimp_vectors_real_get_distance;
260 klass->interpolate = gimp_vectors_real_interpolate;
261
262 klass->make_bezier = gimp_vectors_real_make_bezier;
263 }
264
265 static void
gimp_vectors_init(GimpVectors * vectors)266 gimp_vectors_init (GimpVectors *vectors)
267 {
268 gimp_item_set_visible (GIMP_ITEM (vectors), FALSE, FALSE);
269
270 vectors->strokes = g_queue_new ();
271 vectors->stroke_to_list = g_hash_table_new (g_direct_hash, g_direct_equal);
272 vectors->last_stroke_ID = 0;
273 vectors->freeze_count = 0;
274 vectors->precision = 0.2;
275
276 vectors->bezier_desc = NULL;
277 vectors->bounds_valid = FALSE;
278 }
279
280 static void
gimp_vectors_finalize(GObject * object)281 gimp_vectors_finalize (GObject *object)
282 {
283 GimpVectors *vectors = GIMP_VECTORS (object);
284
285 if (vectors->bezier_desc)
286 {
287 gimp_bezier_desc_free (vectors->bezier_desc);
288 vectors->bezier_desc = NULL;
289 }
290
291 if (vectors->strokes)
292 {
293 g_queue_free_full (vectors->strokes, (GDestroyNotify) g_object_unref);
294 vectors->strokes = NULL;
295 }
296
297 if (vectors->stroke_to_list)
298 {
299 g_hash_table_destroy (vectors->stroke_to_list);
300 vectors->stroke_to_list = NULL;
301 }
302
303 G_OBJECT_CLASS (parent_class)->finalize (object);
304 }
305
306 static gint64
gimp_vectors_get_memsize(GimpObject * object,gint64 * gui_size)307 gimp_vectors_get_memsize (GimpObject *object,
308 gint64 *gui_size)
309 {
310 GimpVectors *vectors;
311 GList *list;
312 gint64 memsize = 0;
313
314 vectors = GIMP_VECTORS (object);
315
316 for (list = vectors->strokes->head; list; list = g_list_next (list))
317 memsize += (gimp_object_get_memsize (GIMP_OBJECT (list->data), gui_size) +
318 sizeof (GList));
319
320 return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
321 gui_size);
322 }
323
324 static gboolean
gimp_vectors_is_attached(GimpItem * item)325 gimp_vectors_is_attached (GimpItem *item)
326 {
327 GimpImage *image = gimp_item_get_image (item);
328
329 return (GIMP_IS_IMAGE (image) &&
330 gimp_container_have (gimp_image_get_vectors (image),
331 GIMP_OBJECT (item)));
332 }
333
334 static GimpItemTree *
gimp_vectors_get_tree(GimpItem * item)335 gimp_vectors_get_tree (GimpItem *item)
336 {
337 if (gimp_item_is_attached (item))
338 {
339 GimpImage *image = gimp_item_get_image (item);
340
341 return gimp_image_get_vectors_tree (image);
342 }
343
344 return NULL;
345 }
346
347 static gboolean
gimp_vectors_bounds(GimpItem * item,gdouble * x,gdouble * y,gdouble * width,gdouble * height)348 gimp_vectors_bounds (GimpItem *item,
349 gdouble *x,
350 gdouble *y,
351 gdouble *width,
352 gdouble *height)
353 {
354 GimpVectors *vectors = GIMP_VECTORS (item);
355
356 if (! vectors->bounds_valid)
357 {
358 GimpStroke *stroke;
359
360 vectors->bounds_empty = TRUE;
361 vectors->bounds_x1 = vectors->bounds_x2 = 0.0;
362 vectors->bounds_y1 = vectors->bounds_y2 = 0.0;
363
364 for (stroke = gimp_vectors_stroke_get_next (vectors, NULL);
365 stroke;
366 stroke = gimp_vectors_stroke_get_next (vectors, stroke))
367 {
368 GArray *stroke_coords;
369 gboolean closed;
370
371 stroke_coords = gimp_stroke_interpolate (stroke, 1.0, &closed);
372
373 if (stroke_coords)
374 {
375 GimpCoords point;
376 gint i;
377
378 if (vectors->bounds_empty && stroke_coords->len > 0)
379 {
380 point = g_array_index (stroke_coords, GimpCoords, 0);
381
382 vectors->bounds_x1 = vectors->bounds_x2 = point.x;
383 vectors->bounds_y1 = vectors->bounds_y2 = point.y;
384
385 vectors->bounds_empty = FALSE;
386 }
387
388 for (i = 0; i < stroke_coords->len; i++)
389 {
390 point = g_array_index (stroke_coords, GimpCoords, i);
391
392 vectors->bounds_x1 = MIN (vectors->bounds_x1, point.x);
393 vectors->bounds_y1 = MIN (vectors->bounds_y1, point.y);
394 vectors->bounds_x2 = MAX (vectors->bounds_x2, point.x);
395 vectors->bounds_y2 = MAX (vectors->bounds_y2, point.y);
396 }
397
398 g_array_free (stroke_coords, TRUE);
399 }
400 }
401
402 vectors->bounds_valid = TRUE;
403 }
404
405 *x = vectors->bounds_x1;
406 *y = vectors->bounds_y1;
407 *width = vectors->bounds_x2 - vectors->bounds_x1;
408 *height = vectors->bounds_y2 - vectors->bounds_y1;
409
410 return ! vectors->bounds_empty;
411 }
412
413 static GimpItem *
gimp_vectors_duplicate(GimpItem * item,GType new_type)414 gimp_vectors_duplicate (GimpItem *item,
415 GType new_type)
416 {
417 GimpItem *new_item;
418
419 g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_VECTORS), NULL);
420
421 new_item = GIMP_ITEM_CLASS (parent_class)->duplicate (item, new_type);
422
423 if (GIMP_IS_VECTORS (new_item))
424 {
425 GimpVectors *vectors = GIMP_VECTORS (item);
426 GimpVectors *new_vectors = GIMP_VECTORS (new_item);
427
428 gimp_vectors_copy_strokes (vectors, new_vectors);
429 }
430
431 return new_item;
432 }
433
434 static void
gimp_vectors_convert(GimpItem * item,GimpImage * dest_image,GType old_type)435 gimp_vectors_convert (GimpItem *item,
436 GimpImage *dest_image,
437 GType old_type)
438 {
439 gimp_item_set_size (item,
440 gimp_image_get_width (dest_image),
441 gimp_image_get_height (dest_image));
442
443 GIMP_ITEM_CLASS (parent_class)->convert (item, dest_image, old_type);
444 }
445
446 static void
gimp_vectors_translate(GimpItem * item,gdouble offset_x,gdouble offset_y,gboolean push_undo)447 gimp_vectors_translate (GimpItem *item,
448 gdouble offset_x,
449 gdouble offset_y,
450 gboolean push_undo)
451 {
452 GimpVectors *vectors = GIMP_VECTORS (item);
453 GList *list;
454
455 gimp_vectors_freeze (vectors);
456
457 if (push_undo)
458 gimp_image_undo_push_vectors_mod (gimp_item_get_image (item),
459 _("Move Path"),
460 vectors);
461
462 for (list = vectors->strokes->head; list; list = g_list_next (list))
463 {
464 GimpStroke *stroke = list->data;
465
466 gimp_stroke_translate (stroke, offset_x, offset_y);
467 }
468
469 gimp_vectors_thaw (vectors);
470 }
471
472 static void
gimp_vectors_scale(GimpItem * item,gint new_width,gint new_height,gint new_offset_x,gint new_offset_y,GimpInterpolationType interpolation_type,GimpProgress * progress)473 gimp_vectors_scale (GimpItem *item,
474 gint new_width,
475 gint new_height,
476 gint new_offset_x,
477 gint new_offset_y,
478 GimpInterpolationType interpolation_type,
479 GimpProgress *progress)
480 {
481 GimpVectors *vectors = GIMP_VECTORS (item);
482 GimpImage *image = gimp_item_get_image (item);
483 GList *list;
484
485 gimp_vectors_freeze (vectors);
486
487 if (gimp_item_is_attached (item))
488 gimp_image_undo_push_vectors_mod (image, NULL, vectors);
489
490 for (list = vectors->strokes->head; list; list = g_list_next (list))
491 {
492 GimpStroke *stroke = list->data;
493
494 gimp_stroke_scale (stroke,
495 (gdouble) new_width / (gdouble) gimp_item_get_width (item),
496 (gdouble) new_height / (gdouble) gimp_item_get_height (item));
497 gimp_stroke_translate (stroke, new_offset_x, new_offset_y);
498 }
499
500 GIMP_ITEM_CLASS (parent_class)->scale (item,
501 gimp_image_get_width (image),
502 gimp_image_get_height (image),
503 0, 0,
504 interpolation_type, progress);
505
506 gimp_vectors_thaw (vectors);
507 }
508
509 static void
gimp_vectors_resize(GimpItem * item,GimpContext * context,GimpFillType fill_type,gint new_width,gint new_height,gint offset_x,gint offset_y)510 gimp_vectors_resize (GimpItem *item,
511 GimpContext *context,
512 GimpFillType fill_type,
513 gint new_width,
514 gint new_height,
515 gint offset_x,
516 gint offset_y)
517 {
518 GimpVectors *vectors = GIMP_VECTORS (item);
519 GimpImage *image = gimp_item_get_image (item);
520 GList *list;
521
522 gimp_vectors_freeze (vectors);
523
524 if (gimp_item_is_attached (item))
525 gimp_image_undo_push_vectors_mod (image, NULL, vectors);
526
527 for (list = vectors->strokes->head; list; list = g_list_next (list))
528 {
529 GimpStroke *stroke = list->data;
530
531 gimp_stroke_translate (stroke, offset_x, offset_y);
532 }
533
534 GIMP_ITEM_CLASS (parent_class)->resize (item, context, fill_type,
535 gimp_image_get_width (image),
536 gimp_image_get_height (image),
537 0, 0);
538
539 gimp_vectors_thaw (vectors);
540 }
541
542 static void
gimp_vectors_flip(GimpItem * item,GimpContext * context,GimpOrientationType flip_type,gdouble axis,gboolean clip_result)543 gimp_vectors_flip (GimpItem *item,
544 GimpContext *context,
545 GimpOrientationType flip_type,
546 gdouble axis,
547 gboolean clip_result)
548 {
549 GimpVectors *vectors = GIMP_VECTORS (item);
550 GList *list;
551 GimpMatrix3 matrix;
552
553 gimp_matrix3_identity (&matrix);
554 gimp_transform_matrix_flip (&matrix, flip_type, axis);
555
556 gimp_vectors_freeze (vectors);
557
558 gimp_image_undo_push_vectors_mod (gimp_item_get_image (item),
559 _("Flip Path"),
560 vectors);
561
562 for (list = vectors->strokes->head; list; list = g_list_next (list))
563 {
564 GimpStroke *stroke = list->data;
565
566 gimp_stroke_transform (stroke, &matrix, NULL);
567 }
568
569 gimp_vectors_thaw (vectors);
570 }
571
572 static void
gimp_vectors_rotate(GimpItem * item,GimpContext * context,GimpRotationType rotate_type,gdouble center_x,gdouble center_y,gboolean clip_result)573 gimp_vectors_rotate (GimpItem *item,
574 GimpContext *context,
575 GimpRotationType rotate_type,
576 gdouble center_x,
577 gdouble center_y,
578 gboolean clip_result)
579 {
580 GimpVectors *vectors = GIMP_VECTORS (item);
581 GList *list;
582 GimpMatrix3 matrix;
583
584 gimp_matrix3_identity (&matrix);
585 gimp_transform_matrix_rotate (&matrix, rotate_type, center_x, center_y);
586
587 gimp_vectors_freeze (vectors);
588
589 gimp_image_undo_push_vectors_mod (gimp_item_get_image (item),
590 _("Rotate Path"),
591 vectors);
592
593 for (list = vectors->strokes->head; list; list = g_list_next (list))
594 {
595 GimpStroke *stroke = list->data;
596
597 gimp_stroke_transform (stroke, &matrix, NULL);
598 }
599
600 gimp_vectors_thaw (vectors);
601 }
602
603 static void
gimp_vectors_transform(GimpItem * item,GimpContext * context,const GimpMatrix3 * matrix,GimpTransformDirection direction,GimpInterpolationType interpolation_type,GimpTransformResize clip_result,GimpProgress * progress)604 gimp_vectors_transform (GimpItem *item,
605 GimpContext *context,
606 const GimpMatrix3 *matrix,
607 GimpTransformDirection direction,
608 GimpInterpolationType interpolation_type,
609 GimpTransformResize clip_result,
610 GimpProgress *progress)
611 {
612 GimpVectors *vectors = GIMP_VECTORS (item);
613 GimpMatrix3 local_matrix;
614 GQueue strokes;
615 GList *list;
616
617 gimp_vectors_freeze (vectors);
618
619 gimp_image_undo_push_vectors_mod (gimp_item_get_image (item),
620 _("Transform Path"),
621 vectors);
622
623 local_matrix = *matrix;
624
625 if (direction == GIMP_TRANSFORM_BACKWARD)
626 gimp_matrix3_invert (&local_matrix);
627
628 g_queue_init (&strokes);
629
630 while (! g_queue_is_empty (vectors->strokes))
631 {
632 GimpStroke *stroke = g_queue_peek_head (vectors->strokes);
633
634 g_object_ref (stroke);
635
636 gimp_vectors_stroke_remove (vectors, stroke);
637
638 gimp_stroke_transform (stroke, &local_matrix, &strokes);
639
640 g_object_unref (stroke);
641 }
642
643 vectors->last_stroke_ID = 0;
644
645 for (list = strokes.head; list; list = g_list_next (list))
646 {
647 GimpStroke *stroke = list->data;
648
649 gimp_vectors_stroke_add (vectors, stroke);
650
651 g_object_unref (stroke);
652 }
653
654 g_queue_clear (&strokes);
655
656 gimp_vectors_thaw (vectors);
657 }
658
659 static GimpTransformResize
gimp_vectors_get_clip(GimpItem * item,GimpTransformResize clip_result)660 gimp_vectors_get_clip (GimpItem *item,
661 GimpTransformResize clip_result)
662 {
663 return GIMP_TRANSFORM_RESIZE_ADJUST;
664 }
665
666 static gboolean
gimp_vectors_fill(GimpItem * item,GimpDrawable * drawable,GimpFillOptions * fill_options,gboolean push_undo,GimpProgress * progress,GError ** error)667 gimp_vectors_fill (GimpItem *item,
668 GimpDrawable *drawable,
669 GimpFillOptions *fill_options,
670 gboolean push_undo,
671 GimpProgress *progress,
672 GError **error)
673 {
674 GimpVectors *vectors = GIMP_VECTORS (item);
675
676 if (g_queue_is_empty (vectors->strokes))
677 {
678 g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
679 _("Not enough points to fill"));
680 return FALSE;
681 }
682
683 return gimp_drawable_fill_vectors (drawable, fill_options,
684 vectors, push_undo, error);
685 }
686
687 static gboolean
gimp_vectors_stroke(GimpItem * item,GimpDrawable * drawable,GimpStrokeOptions * stroke_options,gboolean push_undo,GimpProgress * progress,GError ** error)688 gimp_vectors_stroke (GimpItem *item,
689 GimpDrawable *drawable,
690 GimpStrokeOptions *stroke_options,
691 gboolean push_undo,
692 GimpProgress *progress,
693 GError **error)
694 {
695 GimpVectors *vectors = GIMP_VECTORS (item);
696 gboolean retval = FALSE;
697
698 if (g_queue_is_empty (vectors->strokes))
699 {
700 g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
701 _("Not enough points to stroke"));
702 return FALSE;
703 }
704
705 switch (gimp_stroke_options_get_method (stroke_options))
706 {
707 case GIMP_STROKE_LINE:
708 retval = gimp_drawable_stroke_vectors (drawable, stroke_options,
709 vectors, push_undo, error);
710 break;
711
712 case GIMP_STROKE_PAINT_METHOD:
713 {
714 GimpPaintInfo *paint_info;
715 GimpPaintCore *core;
716 GimpPaintOptions *paint_options;
717 gboolean emulate_dynamics;
718
719 paint_info = gimp_context_get_paint_info (GIMP_CONTEXT (stroke_options));
720
721 core = g_object_new (paint_info->paint_type, NULL);
722
723 paint_options = gimp_stroke_options_get_paint_options (stroke_options);
724 emulate_dynamics = gimp_stroke_options_get_emulate_dynamics (stroke_options);
725
726 retval = gimp_paint_core_stroke_vectors (core, drawable,
727 paint_options,
728 emulate_dynamics,
729 vectors, push_undo, error);
730
731 g_object_unref (core);
732 }
733 break;
734
735 default:
736 g_return_val_if_reached (FALSE);
737 }
738
739 return retval;
740 }
741
742 static void
gimp_vectors_to_selection(GimpItem * item,GimpChannelOps op,gboolean antialias,gboolean feather,gdouble feather_radius_x,gdouble feather_radius_y)743 gimp_vectors_to_selection (GimpItem *item,
744 GimpChannelOps op,
745 gboolean antialias,
746 gboolean feather,
747 gdouble feather_radius_x,
748 gdouble feather_radius_y)
749 {
750 GimpVectors *vectors = GIMP_VECTORS (item);
751 GimpImage *image = gimp_item_get_image (item);
752
753 gimp_channel_select_vectors (gimp_image_get_mask (image),
754 GIMP_ITEM_GET_CLASS (item)->to_selection_desc,
755 vectors,
756 op, antialias,
757 feather, feather_radius_x, feather_radius_x,
758 TRUE);
759 }
760
761 static void
gimp_vectors_real_freeze(GimpVectors * vectors)762 gimp_vectors_real_freeze (GimpVectors *vectors)
763 {
764 /* release cached bezier representation */
765 if (vectors->bezier_desc)
766 {
767 gimp_bezier_desc_free (vectors->bezier_desc);
768 vectors->bezier_desc = NULL;
769 }
770
771 /* invalidate bounds */
772 vectors->bounds_valid = FALSE;
773 }
774
775 static void
gimp_vectors_real_thaw(GimpVectors * vectors)776 gimp_vectors_real_thaw (GimpVectors *vectors)
777 {
778 gimp_viewable_invalidate_preview (GIMP_VIEWABLE (vectors));
779 }
780
781
782 /* public functions */
783
784 GimpVectors *
gimp_vectors_new(GimpImage * image,const gchar * name)785 gimp_vectors_new (GimpImage *image,
786 const gchar *name)
787 {
788 GimpVectors *vectors;
789
790 g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
791
792 vectors = GIMP_VECTORS (gimp_item_new (GIMP_TYPE_VECTORS,
793 image, name,
794 0, 0,
795 gimp_image_get_width (image),
796 gimp_image_get_height (image)));
797
798 return vectors;
799 }
800
801 GimpVectors *
gimp_vectors_get_parent(GimpVectors * vectors)802 gimp_vectors_get_parent (GimpVectors *vectors)
803 {
804 g_return_val_if_fail (GIMP_IS_VECTORS (vectors), NULL);
805
806 return GIMP_VECTORS (gimp_viewable_get_parent (GIMP_VIEWABLE (vectors)));
807 }
808
809 void
gimp_vectors_freeze(GimpVectors * vectors)810 gimp_vectors_freeze (GimpVectors *vectors)
811 {
812 g_return_if_fail (GIMP_IS_VECTORS (vectors));
813
814 vectors->freeze_count++;
815
816 if (vectors->freeze_count == 1)
817 g_signal_emit (vectors, gimp_vectors_signals[FREEZE], 0);
818 }
819
820 void
gimp_vectors_thaw(GimpVectors * vectors)821 gimp_vectors_thaw (GimpVectors *vectors)
822 {
823 g_return_if_fail (GIMP_IS_VECTORS (vectors));
824 g_return_if_fail (vectors->freeze_count > 0);
825
826 vectors->freeze_count--;
827
828 if (vectors->freeze_count == 0)
829 g_signal_emit (vectors, gimp_vectors_signals[THAW], 0);
830 }
831
832 void
gimp_vectors_copy_strokes(GimpVectors * src_vectors,GimpVectors * dest_vectors)833 gimp_vectors_copy_strokes (GimpVectors *src_vectors,
834 GimpVectors *dest_vectors)
835 {
836 g_return_if_fail (GIMP_IS_VECTORS (src_vectors));
837 g_return_if_fail (GIMP_IS_VECTORS (dest_vectors));
838
839 gimp_vectors_freeze (dest_vectors);
840
841 g_queue_free_full (dest_vectors->strokes, (GDestroyNotify) g_object_unref);
842 dest_vectors->strokes = g_queue_new ();
843 g_hash_table_remove_all (dest_vectors->stroke_to_list);
844
845 dest_vectors->last_stroke_ID = 0;
846
847 gimp_vectors_add_strokes (src_vectors, dest_vectors);
848
849 gimp_vectors_thaw (dest_vectors);
850 }
851
852
853 void
gimp_vectors_add_strokes(GimpVectors * src_vectors,GimpVectors * dest_vectors)854 gimp_vectors_add_strokes (GimpVectors *src_vectors,
855 GimpVectors *dest_vectors)
856 {
857 GList *stroke;
858
859 g_return_if_fail (GIMP_IS_VECTORS (src_vectors));
860 g_return_if_fail (GIMP_IS_VECTORS (dest_vectors));
861
862 gimp_vectors_freeze (dest_vectors);
863
864 for (stroke = src_vectors->strokes->head;
865 stroke != NULL;
866 stroke = g_list_next (stroke))
867 {
868 GimpStroke *newstroke = gimp_stroke_duplicate (stroke->data);
869
870 g_queue_push_tail (dest_vectors->strokes, newstroke);
871
872 /* Also add to {stroke: GList node} map */
873 g_hash_table_insert (dest_vectors->stroke_to_list,
874 newstroke,
875 g_queue_peek_tail_link (dest_vectors->strokes));
876
877 dest_vectors->last_stroke_ID++;
878 gimp_stroke_set_ID (newstroke,
879 dest_vectors->last_stroke_ID);
880 }
881
882 gimp_vectors_thaw (dest_vectors);
883 }
884
885
886 void
gimp_vectors_stroke_add(GimpVectors * vectors,GimpStroke * stroke)887 gimp_vectors_stroke_add (GimpVectors *vectors,
888 GimpStroke *stroke)
889 {
890 g_return_if_fail (GIMP_IS_VECTORS (vectors));
891 g_return_if_fail (GIMP_IS_STROKE (stroke));
892
893 gimp_vectors_freeze (vectors);
894
895 GIMP_VECTORS_GET_CLASS (vectors)->stroke_add (vectors, stroke);
896
897 gimp_vectors_thaw (vectors);
898 }
899
900 static void
gimp_vectors_real_stroke_add(GimpVectors * vectors,GimpStroke * stroke)901 gimp_vectors_real_stroke_add (GimpVectors *vectors,
902 GimpStroke *stroke)
903 {
904 /*
905 * Don't prepend into vector->strokes. See ChangeLog 2003-05-21
906 * --Mitch
907 */
908 g_queue_push_tail (vectors->strokes, g_object_ref (stroke));
909
910 /* Also add to {stroke: GList node} map */
911 g_hash_table_insert (vectors->stroke_to_list,
912 stroke,
913 g_queue_peek_tail_link (vectors->strokes));
914
915 vectors->last_stroke_ID++;
916 gimp_stroke_set_ID (stroke, vectors->last_stroke_ID);
917 }
918
919 void
gimp_vectors_stroke_remove(GimpVectors * vectors,GimpStroke * stroke)920 gimp_vectors_stroke_remove (GimpVectors *vectors,
921 GimpStroke *stroke)
922 {
923 g_return_if_fail (GIMP_IS_VECTORS (vectors));
924 g_return_if_fail (GIMP_IS_STROKE (stroke));
925
926 gimp_vectors_freeze (vectors);
927
928 GIMP_VECTORS_GET_CLASS (vectors)->stroke_remove (vectors, stroke);
929
930 gimp_vectors_thaw (vectors);
931 }
932
933 static void
gimp_vectors_real_stroke_remove(GimpVectors * vectors,GimpStroke * stroke)934 gimp_vectors_real_stroke_remove (GimpVectors *vectors,
935 GimpStroke *stroke)
936 {
937 GList *list = g_hash_table_lookup (vectors->stroke_to_list, stroke);
938
939 if (list)
940 {
941 g_queue_delete_link (vectors->strokes, list);
942 g_hash_table_remove (vectors->stroke_to_list, stroke);
943 g_object_unref (stroke);
944 }
945 }
946
947 gint
gimp_vectors_get_n_strokes(GimpVectors * vectors)948 gimp_vectors_get_n_strokes (GimpVectors *vectors)
949 {
950 g_return_val_if_fail (GIMP_IS_VECTORS (vectors), 0);
951
952 return g_queue_get_length (vectors->strokes);
953 }
954
955
956 GimpStroke *
gimp_vectors_stroke_get(GimpVectors * vectors,const GimpCoords * coord)957 gimp_vectors_stroke_get (GimpVectors *vectors,
958 const GimpCoords *coord)
959 {
960 g_return_val_if_fail (GIMP_IS_VECTORS (vectors), NULL);
961
962 return GIMP_VECTORS_GET_CLASS (vectors)->stroke_get (vectors, coord);
963 }
964
965 static GimpStroke *
gimp_vectors_real_stroke_get(GimpVectors * vectors,const GimpCoords * coord)966 gimp_vectors_real_stroke_get (GimpVectors *vectors,
967 const GimpCoords *coord)
968 {
969 GimpStroke *minstroke = NULL;
970 gdouble mindist = G_MAXDOUBLE;
971 GList *list;
972
973 for (list = vectors->strokes->head; list; list = g_list_next (list))
974 {
975 GimpStroke *stroke = list->data;
976 GimpAnchor *anchor = gimp_stroke_anchor_get (stroke, coord);
977
978 if (anchor)
979 {
980 gdouble dx = coord->x - anchor->position.x;
981 gdouble dy = coord->y - anchor->position.y;
982
983 if (mindist > dx * dx + dy * dy)
984 {
985 mindist = dx * dx + dy * dy;
986 minstroke = stroke;
987 }
988 }
989 }
990
991 return minstroke;
992 }
993
994 GimpStroke *
gimp_vectors_stroke_get_by_ID(GimpVectors * vectors,gint id)995 gimp_vectors_stroke_get_by_ID (GimpVectors *vectors,
996 gint id)
997 {
998 GList *list;
999
1000 g_return_val_if_fail (GIMP_IS_VECTORS (vectors), NULL);
1001
1002 for (list = vectors->strokes->head; list; list = g_list_next (list))
1003 {
1004 if (gimp_stroke_get_ID (list->data) == id)
1005 return list->data;
1006 }
1007
1008 return NULL;
1009 }
1010
1011
1012 GimpStroke *
gimp_vectors_stroke_get_next(GimpVectors * vectors,GimpStroke * prev)1013 gimp_vectors_stroke_get_next (GimpVectors *vectors,
1014 GimpStroke *prev)
1015 {
1016 g_return_val_if_fail (GIMP_IS_VECTORS (vectors), NULL);
1017
1018 return GIMP_VECTORS_GET_CLASS (vectors)->stroke_get_next (vectors, prev);
1019 }
1020
1021 static GimpStroke *
gimp_vectors_real_stroke_get_next(GimpVectors * vectors,GimpStroke * prev)1022 gimp_vectors_real_stroke_get_next (GimpVectors *vectors,
1023 GimpStroke *prev)
1024 {
1025 if (! prev)
1026 {
1027 return g_queue_peek_head (vectors->strokes);
1028 }
1029 else
1030 {
1031 GList *stroke = g_hash_table_lookup (vectors->stroke_to_list, prev);
1032
1033 g_return_val_if_fail (stroke != NULL, NULL);
1034
1035 return stroke->next ? stroke->next->data : NULL;
1036 }
1037 }
1038
1039
1040 gdouble
gimp_vectors_stroke_get_length(GimpVectors * vectors,GimpStroke * stroke)1041 gimp_vectors_stroke_get_length (GimpVectors *vectors,
1042 GimpStroke *stroke)
1043 {
1044 g_return_val_if_fail (GIMP_IS_VECTORS (vectors), 0.0);
1045 g_return_val_if_fail (GIMP_IS_STROKE (stroke), 0.0);
1046
1047 return GIMP_VECTORS_GET_CLASS (vectors)->stroke_get_length (vectors, stroke);
1048 }
1049
1050 static gdouble
gimp_vectors_real_stroke_get_length(GimpVectors * vectors,GimpStroke * stroke)1051 gimp_vectors_real_stroke_get_length (GimpVectors *vectors,
1052 GimpStroke *stroke)
1053 {
1054 g_return_val_if_fail (GIMP_IS_VECTORS (vectors), 0.0);
1055 g_return_val_if_fail (GIMP_IS_STROKE (stroke), 0.0);
1056
1057 return gimp_stroke_get_length (stroke, vectors->precision);
1058 }
1059
1060
1061 GimpAnchor *
gimp_vectors_anchor_get(GimpVectors * vectors,const GimpCoords * coord,GimpStroke ** ret_stroke)1062 gimp_vectors_anchor_get (GimpVectors *vectors,
1063 const GimpCoords *coord,
1064 GimpStroke **ret_stroke)
1065 {
1066 g_return_val_if_fail (GIMP_IS_VECTORS (vectors), NULL);
1067
1068 return GIMP_VECTORS_GET_CLASS (vectors)->anchor_get (vectors, coord,
1069 ret_stroke);
1070 }
1071
1072 static GimpAnchor *
gimp_vectors_real_anchor_get(GimpVectors * vectors,const GimpCoords * coord,GimpStroke ** ret_stroke)1073 gimp_vectors_real_anchor_get (GimpVectors *vectors,
1074 const GimpCoords *coord,
1075 GimpStroke **ret_stroke)
1076 {
1077 GimpAnchor *minanchor = NULL;
1078 gdouble mindist = -1;
1079 GList *list;
1080
1081 for (list = vectors->strokes->head; list; list = g_list_next (list))
1082 {
1083 GimpStroke *stroke = list->data;
1084 GimpAnchor *anchor = gimp_stroke_anchor_get (stroke, coord);
1085
1086 if (anchor)
1087 {
1088 gdouble dx = coord->x - anchor->position.x;
1089 gdouble dy = coord->y - anchor->position.y;
1090
1091 if (mindist > dx * dx + dy * dy || mindist < 0)
1092 {
1093 mindist = dx * dx + dy * dy;
1094 minanchor = anchor;
1095
1096 if (ret_stroke)
1097 *ret_stroke = stroke;
1098 }
1099 }
1100 }
1101
1102 return minanchor;
1103 }
1104
1105
1106 void
gimp_vectors_anchor_delete(GimpVectors * vectors,GimpAnchor * anchor)1107 gimp_vectors_anchor_delete (GimpVectors *vectors,
1108 GimpAnchor *anchor)
1109 {
1110 g_return_if_fail (GIMP_IS_VECTORS (vectors));
1111 g_return_if_fail (anchor != NULL);
1112
1113 GIMP_VECTORS_GET_CLASS (vectors)->anchor_delete (vectors, anchor);
1114 }
1115
1116 static void
gimp_vectors_real_anchor_delete(GimpVectors * vectors,GimpAnchor * anchor)1117 gimp_vectors_real_anchor_delete (GimpVectors *vectors,
1118 GimpAnchor *anchor)
1119 {
1120 }
1121
1122
1123 void
gimp_vectors_anchor_select(GimpVectors * vectors,GimpStroke * target_stroke,GimpAnchor * anchor,gboolean selected,gboolean exclusive)1124 gimp_vectors_anchor_select (GimpVectors *vectors,
1125 GimpStroke *target_stroke,
1126 GimpAnchor *anchor,
1127 gboolean selected,
1128 gboolean exclusive)
1129 {
1130 GList *list;
1131
1132 for (list = vectors->strokes->head; list; list = g_list_next (list))
1133 {
1134 GimpStroke *stroke = list->data;
1135
1136 gimp_stroke_anchor_select (stroke,
1137 stroke == target_stroke ? anchor : NULL,
1138 selected, exclusive);
1139 }
1140 }
1141
1142
1143 gdouble
gimp_vectors_get_length(GimpVectors * vectors,const GimpAnchor * start)1144 gimp_vectors_get_length (GimpVectors *vectors,
1145 const GimpAnchor *start)
1146 {
1147 g_return_val_if_fail (GIMP_IS_VECTORS (vectors), 0.0);
1148
1149 return GIMP_VECTORS_GET_CLASS (vectors)->get_length (vectors, start);
1150 }
1151
1152 static gdouble
gimp_vectors_real_get_length(GimpVectors * vectors,const GimpAnchor * start)1153 gimp_vectors_real_get_length (GimpVectors *vectors,
1154 const GimpAnchor *start)
1155 {
1156 g_printerr ("gimp_vectors_get_length: default implementation\n");
1157
1158 return 0;
1159 }
1160
1161
1162 gdouble
gimp_vectors_get_distance(GimpVectors * vectors,const GimpCoords * coord)1163 gimp_vectors_get_distance (GimpVectors *vectors,
1164 const GimpCoords *coord)
1165 {
1166 g_return_val_if_fail (GIMP_IS_VECTORS (vectors), 0.0);
1167
1168 return GIMP_VECTORS_GET_CLASS (vectors)->get_distance (vectors, coord);
1169 }
1170
1171 static gdouble
gimp_vectors_real_get_distance(GimpVectors * vectors,const GimpCoords * coord)1172 gimp_vectors_real_get_distance (GimpVectors *vectors,
1173 const GimpCoords *coord)
1174 {
1175 g_printerr ("gimp_vectors_get_distance: default implementation\n");
1176
1177 return 0;
1178 }
1179
1180 gint
gimp_vectors_interpolate(GimpVectors * vectors,GimpStroke * stroke,gdouble precision,gint max_points,GimpCoords * ret_coords)1181 gimp_vectors_interpolate (GimpVectors *vectors,
1182 GimpStroke *stroke,
1183 gdouble precision,
1184 gint max_points,
1185 GimpCoords *ret_coords)
1186 {
1187 g_return_val_if_fail (GIMP_IS_VECTORS (vectors), 0);
1188
1189 return GIMP_VECTORS_GET_CLASS (vectors)->interpolate (vectors, stroke,
1190 precision, max_points,
1191 ret_coords);
1192 }
1193
1194 static gint
gimp_vectors_real_interpolate(GimpVectors * vectors,GimpStroke * stroke,gdouble precision,gint max_points,GimpCoords * ret_coords)1195 gimp_vectors_real_interpolate (GimpVectors *vectors,
1196 GimpStroke *stroke,
1197 gdouble precision,
1198 gint max_points,
1199 GimpCoords *ret_coords)
1200 {
1201 g_printerr ("gimp_vectors_interpolate: default implementation\n");
1202
1203 return 0;
1204 }
1205
1206 const GimpBezierDesc *
gimp_vectors_get_bezier(GimpVectors * vectors)1207 gimp_vectors_get_bezier (GimpVectors *vectors)
1208 {
1209 g_return_val_if_fail (GIMP_IS_VECTORS (vectors), NULL);
1210
1211 if (! vectors->bezier_desc)
1212 {
1213 vectors->bezier_desc = gimp_vectors_make_bezier (vectors);
1214 }
1215
1216 return vectors->bezier_desc;
1217 }
1218
1219 static GimpBezierDesc *
gimp_vectors_make_bezier(GimpVectors * vectors)1220 gimp_vectors_make_bezier (GimpVectors *vectors)
1221 {
1222 return GIMP_VECTORS_GET_CLASS (vectors)->make_bezier (vectors);
1223 }
1224
1225 static GimpBezierDesc *
gimp_vectors_real_make_bezier(GimpVectors * vectors)1226 gimp_vectors_real_make_bezier (GimpVectors *vectors)
1227 {
1228 GimpStroke *stroke;
1229 GArray *cmd_array;
1230 GimpBezierDesc *ret_bezdesc = NULL;
1231
1232 cmd_array = g_array_new (FALSE, FALSE, sizeof (cairo_path_data_t));
1233
1234 for (stroke = gimp_vectors_stroke_get_next (vectors, NULL);
1235 stroke;
1236 stroke = gimp_vectors_stroke_get_next (vectors, stroke))
1237 {
1238 GimpBezierDesc *bezdesc = gimp_stroke_make_bezier (stroke);
1239
1240 if (bezdesc)
1241 {
1242 cmd_array = g_array_append_vals (cmd_array, bezdesc->data,
1243 bezdesc->num_data);
1244 gimp_bezier_desc_free (bezdesc);
1245 }
1246 }
1247
1248 if (cmd_array->len > 0)
1249 ret_bezdesc = gimp_bezier_desc_new ((cairo_path_data_t *) cmd_array->data,
1250 cmd_array->len);
1251
1252 g_array_free (cmd_array, FALSE);
1253
1254 return ret_bezdesc;
1255 }
1256