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