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