1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimpcanvasboundary.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/gimp-cairo.h"
32 #include "core/gimp-transform-utils.h"
33 #include "core/gimpboundary.h"
34 #include "core/gimpparamspecs.h"
35
36 #include "gimpcanvasboundary.h"
37 #include "gimpdisplayshell.h"
38
39
40 enum
41 {
42 PROP_0,
43 PROP_SEGS,
44 PROP_TRANSFORM,
45 PROP_OFFSET_X,
46 PROP_OFFSET_Y
47 };
48
49
50 typedef struct _GimpCanvasBoundaryPrivate GimpCanvasBoundaryPrivate;
51
52 struct _GimpCanvasBoundaryPrivate
53 {
54 GimpBoundSeg *segs;
55 gint n_segs;
56 GimpMatrix3 *transform;
57 gdouble offset_x;
58 gdouble offset_y;
59 };
60
61 #define GET_PRIVATE(boundary) \
62 ((GimpCanvasBoundaryPrivate *) gimp_canvas_boundary_get_instance_private ((GimpCanvasBoundary *) (boundary)))
63
64
65 /* local function prototypes */
66
67 static void gimp_canvas_boundary_finalize (GObject *object);
68 static void gimp_canvas_boundary_set_property (GObject *object,
69 guint property_id,
70 const GValue *value,
71 GParamSpec *pspec);
72 static void gimp_canvas_boundary_get_property (GObject *object,
73 guint property_id,
74 GValue *value,
75 GParamSpec *pspec);
76 static void gimp_canvas_boundary_draw (GimpCanvasItem *item,
77 cairo_t *cr);
78 static cairo_region_t * gimp_canvas_boundary_get_extents (GimpCanvasItem *item);
79
80
G_DEFINE_TYPE_WITH_PRIVATE(GimpCanvasBoundary,gimp_canvas_boundary,GIMP_TYPE_CANVAS_ITEM)81 G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasBoundary, gimp_canvas_boundary,
82 GIMP_TYPE_CANVAS_ITEM)
83
84 #define parent_class gimp_canvas_boundary_parent_class
85
86
87 static void
88 gimp_canvas_boundary_class_init (GimpCanvasBoundaryClass *klass)
89 {
90 GObjectClass *object_class = G_OBJECT_CLASS (klass);
91 GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass);
92
93 object_class->finalize = gimp_canvas_boundary_finalize;
94 object_class->set_property = gimp_canvas_boundary_set_property;
95 object_class->get_property = gimp_canvas_boundary_get_property;
96
97 item_class->draw = gimp_canvas_boundary_draw;
98 item_class->get_extents = gimp_canvas_boundary_get_extents;
99
100 g_object_class_install_property (object_class, PROP_SEGS,
101 gimp_param_spec_array ("segs", NULL, NULL,
102 GIMP_PARAM_READWRITE));
103
104 g_object_class_install_property (object_class, PROP_TRANSFORM,
105 g_param_spec_pointer ("transform", NULL, NULL,
106 GIMP_PARAM_READWRITE));
107
108 g_object_class_install_property (object_class, PROP_OFFSET_X,
109 g_param_spec_double ("offset-x", NULL, NULL,
110 -GIMP_MAX_IMAGE_SIZE,
111 GIMP_MAX_IMAGE_SIZE, 0,
112 GIMP_PARAM_READWRITE));
113
114 g_object_class_install_property (object_class, PROP_OFFSET_Y,
115 g_param_spec_double ("offset-y", NULL, NULL,
116 -GIMP_MAX_IMAGE_SIZE,
117 GIMP_MAX_IMAGE_SIZE, 0,
118 GIMP_PARAM_READWRITE));
119 }
120
121 static void
gimp_canvas_boundary_init(GimpCanvasBoundary * boundary)122 gimp_canvas_boundary_init (GimpCanvasBoundary *boundary)
123 {
124 gimp_canvas_item_set_line_cap (GIMP_CANVAS_ITEM (boundary),
125 CAIRO_LINE_CAP_SQUARE);
126 }
127
128 static void
gimp_canvas_boundary_finalize(GObject * object)129 gimp_canvas_boundary_finalize (GObject *object)
130 {
131 GimpCanvasBoundaryPrivate *private = GET_PRIVATE (object);
132
133 g_clear_pointer (&private->segs, g_free);
134 private->n_segs = 0;
135
136 g_clear_pointer (&private->transform, g_free);
137
138 G_OBJECT_CLASS (parent_class)->finalize (object);
139 }
140
141 static void
gimp_canvas_boundary_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)142 gimp_canvas_boundary_set_property (GObject *object,
143 guint property_id,
144 const GValue *value,
145 GParamSpec *pspec)
146 {
147 GimpCanvasBoundaryPrivate *private = GET_PRIVATE (object);
148
149 switch (property_id)
150 {
151 case PROP_SEGS:
152 break;
153
154 case PROP_TRANSFORM:
155 {
156 GimpMatrix3 *transform = g_value_get_pointer (value);
157 if (private->transform)
158 g_free (private->transform);
159 if (transform)
160 private->transform = g_memdup (transform, sizeof (GimpMatrix3));
161 else
162 private->transform = NULL;
163 }
164 break;
165
166 case PROP_OFFSET_X:
167 private->offset_x = g_value_get_double (value);
168 break;
169 case PROP_OFFSET_Y:
170 private->offset_y = g_value_get_double (value);
171 break;
172
173 default:
174 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
175 break;
176 }
177 }
178
179 static void
gimp_canvas_boundary_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)180 gimp_canvas_boundary_get_property (GObject *object,
181 guint property_id,
182 GValue *value,
183 GParamSpec *pspec)
184 {
185 GimpCanvasBoundaryPrivate *private = GET_PRIVATE (object);
186
187 switch (property_id)
188 {
189 case PROP_SEGS:
190 break;
191
192 case PROP_TRANSFORM:
193 g_value_set_pointer (value, private->transform);
194 break;
195
196 case PROP_OFFSET_X:
197 g_value_set_double (value, private->offset_x);
198 break;
199 case PROP_OFFSET_Y:
200 g_value_set_double (value, private->offset_y);
201 break;
202
203 default:
204 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
205 break;
206 }
207 }
208
209 static void
gimp_canvas_boundary_transform(GimpCanvasItem * item,GimpSegment * segs,gint * n_segs)210 gimp_canvas_boundary_transform (GimpCanvasItem *item,
211 GimpSegment *segs,
212 gint *n_segs)
213 {
214 GimpCanvasBoundaryPrivate *private = GET_PRIVATE (item);
215 gint i;
216
217 if (private->transform)
218 {
219 gint n = 0;
220
221 for (i = 0; i < private->n_segs; i++)
222 {
223 GimpVector2 vertices[2];
224 GimpVector2 t_vertices[2];
225 gint n_t_vertices;
226
227 vertices[0] = (GimpVector2) { private->segs[i].x1, private->segs[i].y1 };
228 vertices[1] = (GimpVector2) { private->segs[i].x2, private->segs[i].y2 };
229
230 gimp_transform_polygon (private->transform, vertices, 2, FALSE,
231 t_vertices, &n_t_vertices);
232
233 if (n_t_vertices == 2)
234 {
235 gimp_canvas_item_transform_xy (item,
236 t_vertices[0].x + private->offset_x,
237 t_vertices[0].y + private->offset_y,
238 &segs[n].x1, &segs[n].y1);
239 gimp_canvas_item_transform_xy (item,
240 t_vertices[1].x + private->offset_x,
241 t_vertices[1].y + private->offset_y,
242 &segs[n].x2, &segs[n].y2);
243
244 n++;
245 }
246 }
247
248 *n_segs = n;
249 }
250 else
251 {
252 for (i = 0; i < private->n_segs; i++)
253 {
254 gimp_canvas_item_transform_xy (item,
255 private->segs[i].x1 + private->offset_x,
256 private->segs[i].y1 + private->offset_y,
257 &segs[i].x1,
258 &segs[i].y1);
259 gimp_canvas_item_transform_xy (item,
260 private->segs[i].x2 + private->offset_x,
261 private->segs[i].y2 + private->offset_y,
262 &segs[i].x2,
263 &segs[i].y2);
264
265 /* If this segment is a closing segment && the segments lie inside
266 * the region, OR if this is an opening segment and the segments
267 * lie outside the region...
268 * we need to transform it by one display pixel
269 */
270 if (! private->segs[i].open)
271 {
272 /* If it is vertical */
273 if (segs[i].x1 == segs[i].x2)
274 {
275 segs[i].x1 -= 1;
276 segs[i].x2 -= 1;
277 }
278 else
279 {
280 segs[i].y1 -= 1;
281 segs[i].y2 -= 1;
282 }
283 }
284 }
285
286 *n_segs = private->n_segs;
287 }
288 }
289
290 static void
gimp_canvas_boundary_draw(GimpCanvasItem * item,cairo_t * cr)291 gimp_canvas_boundary_draw (GimpCanvasItem *item,
292 cairo_t *cr)
293 {
294 GimpCanvasBoundaryPrivate *private = GET_PRIVATE (item);
295 GimpSegment *segs;
296 gint n_segs;
297
298 segs = g_new0 (GimpSegment, private->n_segs);
299
300 gimp_canvas_boundary_transform (item, segs, &n_segs);
301
302 gimp_cairo_segments (cr, segs, n_segs);
303
304 _gimp_canvas_item_stroke (item, cr);
305
306 g_free (segs);
307 }
308
309 static cairo_region_t *
gimp_canvas_boundary_get_extents(GimpCanvasItem * item)310 gimp_canvas_boundary_get_extents (GimpCanvasItem *item)
311 {
312 GimpCanvasBoundaryPrivate *private = GET_PRIVATE (item);
313 cairo_rectangle_int_t rectangle;
314 GimpSegment *segs;
315 gint n_segs;
316 gint x1, y1, x2, y2;
317 gint i;
318
319 segs = g_new0 (GimpSegment, private->n_segs);
320
321 gimp_canvas_boundary_transform (item, segs, &n_segs);
322
323 if (n_segs == 0)
324 {
325 g_free (segs);
326
327 return cairo_region_create ();
328 }
329
330 x1 = MIN (segs[0].x1, segs[0].x2);
331 y1 = MIN (segs[0].y1, segs[0].y2);
332 x2 = MAX (segs[0].x1, segs[0].x2);
333 y2 = MAX (segs[0].y1, segs[0].y2);
334
335 for (i = 1; i < n_segs; i++)
336 {
337 gint x3 = MIN (segs[i].x1, segs[i].x2);
338 gint y3 = MIN (segs[i].y1, segs[i].y2);
339 gint x4 = MAX (segs[i].x1, segs[i].x2);
340 gint y4 = MAX (segs[i].y1, segs[i].y2);
341
342 x1 = MIN (x1, x3);
343 y1 = MIN (y1, y3);
344 x2 = MAX (x2, x4);
345 y2 = MAX (y2, y4);
346 }
347
348 g_free (segs);
349
350 rectangle.x = x1 - 2;
351 rectangle.y = y1 - 2;
352 rectangle.width = x2 - x1 + 4;
353 rectangle.height = y2 - y1 + 4;
354
355 return cairo_region_create_rectangle (&rectangle);
356 }
357
358 GimpCanvasItem *
gimp_canvas_boundary_new(GimpDisplayShell * shell,const GimpBoundSeg * segs,gint n_segs,GimpMatrix3 * transform,gdouble offset_x,gdouble offset_y)359 gimp_canvas_boundary_new (GimpDisplayShell *shell,
360 const GimpBoundSeg *segs,
361 gint n_segs,
362 GimpMatrix3 *transform,
363 gdouble offset_x,
364 gdouble offset_y)
365 {
366 GimpCanvasItem *item;
367 GimpCanvasBoundaryPrivate *private;
368
369 g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL);
370
371 item = g_object_new (GIMP_TYPE_CANVAS_BOUNDARY,
372 "shell", shell,
373 "transform", transform,
374 "offset-x", offset_x,
375 "offset-y", offset_y,
376 NULL);
377 private = GET_PRIVATE (item);
378
379 /* puke */
380 private->segs = g_memdup (segs, n_segs * sizeof (GimpBoundSeg));
381 private->n_segs = n_segs;
382
383 return item;
384 }
385