1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimpcanvaspath.c
5  * Copyright (C) 2010 Michael Natterer <mitch@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 <gegl.h>
24 #include <gtk/gtk.h>
25 
26 #include "libgimpbase/gimpbase.h"
27 #include "libgimpmath/gimpmath.h"
28 
29 #include "display-types.h"
30 
31 #include "core/gimpbezierdesc.h"
32 #include "core/gimpparamspecs.h"
33 
34 #include "gimpcanvas-style.h"
35 #include "gimpcanvaspath.h"
36 #include "gimpdisplayshell.h"
37 
38 
39 enum
40 {
41   PROP_0,
42   PROP_PATH,
43   PROP_X,
44   PROP_Y,
45   PROP_FILLED,
46   PROP_PATH_STYLE
47 };
48 
49 
50 typedef struct _GimpCanvasPathPrivate GimpCanvasPathPrivate;
51 
52 struct _GimpCanvasPathPrivate
53 {
54   cairo_path_t *path;
55   gdouble       x;
56   gdouble       y;
57   gboolean      filled;
58   GimpPathStyle path_style;
59 };
60 
61 #define GET_PRIVATE(path) \
62         ((GimpCanvasPathPrivate *) gimp_canvas_path_get_instance_private ((GimpCanvasPath *) (path)))
63 
64 /*  local function prototypes  */
65 
66 static void             gimp_canvas_path_finalize     (GObject        *object);
67 static void             gimp_canvas_path_set_property (GObject        *object,
68                                                        guint           property_id,
69                                                        const GValue   *value,
70                                                        GParamSpec     *pspec);
71 static void             gimp_canvas_path_get_property (GObject        *object,
72                                                        guint           property_id,
73                                                        GValue         *value,
74                                                        GParamSpec     *pspec);
75 static void             gimp_canvas_path_draw         (GimpCanvasItem *item,
76                                                        cairo_t        *cr);
77 static cairo_region_t * gimp_canvas_path_get_extents  (GimpCanvasItem *item);
78 static void             gimp_canvas_path_stroke       (GimpCanvasItem *item,
79                                                        cairo_t        *cr);
80 
81 
G_DEFINE_TYPE_WITH_PRIVATE(GimpCanvasPath,gimp_canvas_path,GIMP_TYPE_CANVAS_ITEM)82 G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasPath, gimp_canvas_path,
83                             GIMP_TYPE_CANVAS_ITEM)
84 
85 #define parent_class gimp_canvas_path_parent_class
86 
87 
88 static void
89 gimp_canvas_path_class_init (GimpCanvasPathClass *klass)
90 {
91   GObjectClass        *object_class = G_OBJECT_CLASS (klass);
92   GimpCanvasItemClass *item_class   = GIMP_CANVAS_ITEM_CLASS (klass);
93 
94   object_class->finalize     = gimp_canvas_path_finalize;
95   object_class->set_property = gimp_canvas_path_set_property;
96   object_class->get_property = gimp_canvas_path_get_property;
97 
98   item_class->draw           = gimp_canvas_path_draw;
99   item_class->get_extents    = gimp_canvas_path_get_extents;
100   item_class->stroke         = gimp_canvas_path_stroke;
101 
102   g_object_class_install_property (object_class, PROP_PATH,
103                                    g_param_spec_boxed ("path", NULL, NULL,
104                                                        GIMP_TYPE_BEZIER_DESC,
105                                                        GIMP_PARAM_READWRITE));
106 
107   g_object_class_install_property (object_class, PROP_X,
108                                    g_param_spec_double ("x", NULL, NULL,
109                                                         -GIMP_MAX_IMAGE_SIZE,
110                                                         GIMP_MAX_IMAGE_SIZE, 0,
111                                                         GIMP_PARAM_READWRITE));
112 
113   g_object_class_install_property (object_class, PROP_Y,
114                                    g_param_spec_double ("y", NULL, NULL,
115                                                         -GIMP_MAX_IMAGE_SIZE,
116                                                         GIMP_MAX_IMAGE_SIZE, 0,
117                                                         GIMP_PARAM_READWRITE));
118 
119   g_object_class_install_property (object_class, PROP_FILLED,
120                                    g_param_spec_boolean ("filled", NULL, NULL,
121                                                          FALSE,
122                                                          GIMP_PARAM_READWRITE));
123 
124 
125   g_object_class_install_property (object_class, PROP_PATH_STYLE,
126                                    g_param_spec_enum ("path-style", NULL, NULL,
127                                                       GIMP_TYPE_PATH_STYLE,
128                                                       GIMP_PATH_STYLE_DEFAULT,
129                                                       GIMP_PARAM_READWRITE));
130 }
131 
132 static void
gimp_canvas_path_init(GimpCanvasPath * path)133 gimp_canvas_path_init (GimpCanvasPath *path)
134 {
135 }
136 
137 static void
gimp_canvas_path_finalize(GObject * object)138 gimp_canvas_path_finalize (GObject *object)
139 {
140   GimpCanvasPathPrivate *private = GET_PRIVATE (object);
141 
142   if (private->path)
143     {
144       gimp_bezier_desc_free (private->path);
145       private->path = NULL;
146     }
147 
148   G_OBJECT_CLASS (parent_class)->finalize (object);
149 }
150 
151 static void
gimp_canvas_path_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)152 gimp_canvas_path_set_property (GObject      *object,
153                                guint         property_id,
154                                const GValue *value,
155                                GParamSpec   *pspec)
156 {
157   GimpCanvasPathPrivate *private = GET_PRIVATE (object);
158 
159   switch (property_id)
160     {
161     case PROP_PATH:
162       if (private->path)
163         gimp_bezier_desc_free (private->path);
164       private->path = g_value_dup_boxed (value);
165       break;
166     case PROP_X:
167       private->x = g_value_get_double (value);
168       break;
169     case PROP_Y:
170       private->y = g_value_get_double (value);
171       break;
172     case PROP_FILLED:
173       private->filled = g_value_get_boolean (value);
174       break;
175     case PROP_PATH_STYLE:
176       private->path_style = g_value_get_enum (value);
177       break;
178 
179     default:
180       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
181       break;
182     }
183 }
184 
185 static void
gimp_canvas_path_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)186 gimp_canvas_path_get_property (GObject    *object,
187                                guint       property_id,
188                                GValue     *value,
189                                GParamSpec *pspec)
190 {
191   GimpCanvasPathPrivate *private = GET_PRIVATE (object);
192 
193   switch (property_id)
194     {
195     case PROP_PATH:
196       g_value_set_boxed (value, private->path);
197       break;
198     case PROP_X:
199       g_value_set_double (value, private->x);
200       break;
201     case PROP_Y:
202       g_value_set_double (value, private->y);
203       break;
204     case PROP_FILLED:
205       g_value_set_boolean (value, private->filled);
206       break;
207     case PROP_PATH_STYLE:
208       g_value_set_enum (value, private->path_style);
209       break;
210 
211     default:
212       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
213       break;
214     }
215 }
216 
217 static void
gimp_canvas_path_draw(GimpCanvasItem * item,cairo_t * cr)218 gimp_canvas_path_draw (GimpCanvasItem *item,
219                        cairo_t        *cr)
220 {
221   GimpCanvasPathPrivate *private = GET_PRIVATE (item);
222 
223   if (private->path)
224     {
225       cairo_save (cr);
226       gimp_canvas_item_transform (item, cr);
227       cairo_translate (cr, private->x, private->y);
228 
229       cairo_append_path (cr, private->path);
230       cairo_restore (cr);
231 
232       if (private->filled)
233         _gimp_canvas_item_fill (item, cr);
234       else
235         _gimp_canvas_item_stroke (item, cr);
236     }
237 }
238 
239 static cairo_region_t *
gimp_canvas_path_get_extents(GimpCanvasItem * item)240 gimp_canvas_path_get_extents (GimpCanvasItem *item)
241 {
242   GimpCanvasPathPrivate *private = GET_PRIVATE (item);
243   GtkWidget             *canvas  = gimp_canvas_item_get_canvas (item);
244 
245   if (private->path && gtk_widget_get_realized (canvas))
246     {
247       cairo_t               *cr;
248       cairo_rectangle_int_t  rectangle;
249       gdouble                x1, y1, x2, y2;
250 
251       cr = gdk_cairo_create (gtk_widget_get_window (canvas));
252 
253       cairo_save (cr);
254       gimp_canvas_item_transform (item, cr);
255       cairo_translate (cr, private->x, private->y);
256 
257       cairo_append_path (cr, private->path);
258       cairo_restore (cr);
259 
260       cairo_path_extents (cr, &x1, &y1, &x2, &y2);
261 
262       cairo_destroy (cr);
263 
264       if (private->filled)
265         {
266           rectangle.x      = floor (x1 - 1.0);
267           rectangle.y      = floor (y1 - 1.0);
268           rectangle.width  = ceil (x2 + 1.0) - rectangle.x;
269           rectangle.height = ceil (y2 + 1.0) - rectangle.y;
270         }
271       else
272         {
273           rectangle.x      = floor (x1 - 1.5);
274           rectangle.y      = floor (y1 - 1.5);
275           rectangle.width  = ceil (x2 + 1.5) - rectangle.x;
276           rectangle.height = ceil (y2 + 1.5) - rectangle.y;
277         }
278 
279       return cairo_region_create_rectangle (&rectangle);
280     }
281 
282   return NULL;
283 }
284 
285 static void
gimp_canvas_path_stroke(GimpCanvasItem * item,cairo_t * cr)286 gimp_canvas_path_stroke (GimpCanvasItem *item,
287                          cairo_t        *cr)
288 {
289   GimpCanvasPathPrivate *private = GET_PRIVATE (item);
290   GtkWidget             *canvas  = gimp_canvas_item_get_canvas (item);
291   gboolean               active;
292 
293   switch (private->path_style)
294     {
295     case GIMP_PATH_STYLE_VECTORS:
296       active = gimp_canvas_item_get_highlight (item);
297 
298       gimp_canvas_set_vectors_bg_style (canvas, cr, active);
299       cairo_stroke_preserve (cr);
300 
301       gimp_canvas_set_vectors_fg_style (canvas, cr, active);
302       cairo_stroke (cr);
303       break;
304 
305     case GIMP_PATH_STYLE_OUTLINE:
306       gimp_canvas_set_outline_bg_style (canvas, cr);
307       cairo_stroke_preserve (cr);
308 
309       gimp_canvas_set_outline_fg_style (canvas, cr);
310       cairo_stroke (cr);
311       break;
312 
313     case GIMP_PATH_STYLE_DEFAULT:
314       GIMP_CANVAS_ITEM_CLASS (parent_class)->stroke (item, cr);
315       break;
316     }
317 }
318 
319 GimpCanvasItem *
gimp_canvas_path_new(GimpDisplayShell * shell,const GimpBezierDesc * bezier,gdouble x,gdouble y,gboolean filled,GimpPathStyle style)320 gimp_canvas_path_new (GimpDisplayShell     *shell,
321                       const GimpBezierDesc *bezier,
322                       gdouble               x,
323                       gdouble               y,
324                       gboolean              filled,
325                       GimpPathStyle         style)
326 {
327   g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL);
328 
329   return g_object_new (GIMP_TYPE_CANVAS_PATH,
330                        "shell",      shell,
331                        "path",       bezier,
332                        "x",          x,
333                        "y",          y,
334                        "filled",     filled,
335                        "path-style", style,
336                        NULL);
337 }
338 
339 void
gimp_canvas_path_set(GimpCanvasItem * path,const GimpBezierDesc * bezier)340 gimp_canvas_path_set (GimpCanvasItem       *path,
341                       const GimpBezierDesc *bezier)
342 {
343   g_return_if_fail (GIMP_IS_CANVAS_PATH (path));
344 
345   gimp_canvas_item_begin_change (path);
346 
347   g_object_set (path,
348                 "path", bezier,
349                 NULL);
350 
351   gimp_canvas_item_end_change (path);
352 }
353