1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimpcanvasrectangle.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 "gimpcanvasrectangle.h"
32 #include "gimpdisplayshell.h"
33
34
35 enum
36 {
37 PROP_0,
38 PROP_X,
39 PROP_Y,
40 PROP_WIDTH,
41 PROP_HEIGHT,
42 PROP_FILLED
43 };
44
45
46 typedef struct _GimpCanvasRectanglePrivate GimpCanvasRectanglePrivate;
47
48 struct _GimpCanvasRectanglePrivate
49 {
50 gdouble x;
51 gdouble y;
52 gdouble width;
53 gdouble height;
54 gboolean filled;
55 };
56
57 #define GET_PRIVATE(rectangle) \
58 ((GimpCanvasRectanglePrivate *) gimp_canvas_rectangle_get_instance_private ((GimpCanvasRectangle *) (rectangle)))
59
60
61 /* local function prototypes */
62
63 static void gimp_canvas_rectangle_set_property (GObject *object,
64 guint property_id,
65 const GValue *value,
66 GParamSpec *pspec);
67 static void gimp_canvas_rectangle_get_property (GObject *object,
68 guint property_id,
69 GValue *value,
70 GParamSpec *pspec);
71 static void gimp_canvas_rectangle_draw (GimpCanvasItem *item,
72 cairo_t *cr);
73 static cairo_region_t * gimp_canvas_rectangle_get_extents (GimpCanvasItem *item);
74
75
G_DEFINE_TYPE_WITH_PRIVATE(GimpCanvasRectangle,gimp_canvas_rectangle,GIMP_TYPE_CANVAS_ITEM)76 G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasRectangle, gimp_canvas_rectangle,
77 GIMP_TYPE_CANVAS_ITEM)
78
79 #define parent_class gimp_canvas_rectangle_parent_class
80
81
82 static void
83 gimp_canvas_rectangle_class_init (GimpCanvasRectangleClass *klass)
84 {
85 GObjectClass *object_class = G_OBJECT_CLASS (klass);
86 GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass);
87
88 object_class->set_property = gimp_canvas_rectangle_set_property;
89 object_class->get_property = gimp_canvas_rectangle_get_property;
90
91 item_class->draw = gimp_canvas_rectangle_draw;
92 item_class->get_extents = gimp_canvas_rectangle_get_extents;
93
94 g_object_class_install_property (object_class, PROP_X,
95 g_param_spec_double ("x", NULL, NULL,
96 -GIMP_MAX_IMAGE_SIZE,
97 GIMP_MAX_IMAGE_SIZE, 0,
98 GIMP_PARAM_READWRITE));
99
100 g_object_class_install_property (object_class, PROP_Y,
101 g_param_spec_double ("y", NULL, NULL,
102 -GIMP_MAX_IMAGE_SIZE,
103 GIMP_MAX_IMAGE_SIZE, 0,
104 GIMP_PARAM_READWRITE));
105
106 g_object_class_install_property (object_class, PROP_WIDTH,
107 g_param_spec_double ("width", NULL, NULL,
108 -GIMP_MAX_IMAGE_SIZE,
109 GIMP_MAX_IMAGE_SIZE, 0,
110 GIMP_PARAM_READWRITE));
111
112 g_object_class_install_property (object_class, PROP_HEIGHT,
113 g_param_spec_double ("height", NULL, NULL,
114 -GIMP_MAX_IMAGE_SIZE,
115 GIMP_MAX_IMAGE_SIZE, 0,
116 GIMP_PARAM_READWRITE));
117
118 g_object_class_install_property (object_class, PROP_FILLED,
119 g_param_spec_boolean ("filled", NULL, NULL,
120 FALSE,
121 GIMP_PARAM_READWRITE));
122 }
123
124 static void
gimp_canvas_rectangle_init(GimpCanvasRectangle * rectangle)125 gimp_canvas_rectangle_init (GimpCanvasRectangle *rectangle)
126 {
127 }
128
129 static void
gimp_canvas_rectangle_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)130 gimp_canvas_rectangle_set_property (GObject *object,
131 guint property_id,
132 const GValue *value,
133 GParamSpec *pspec)
134 {
135 GimpCanvasRectanglePrivate *private = GET_PRIVATE (object);
136
137 switch (property_id)
138 {
139 case PROP_X:
140 private->x = g_value_get_double (value);
141 break;
142 case PROP_Y:
143 private->y = g_value_get_double (value);
144 break;
145 case PROP_WIDTH:
146 private->width = g_value_get_double (value);
147 break;
148 case PROP_HEIGHT:
149 private->height = g_value_get_double (value);
150 break;
151 case PROP_FILLED:
152 private->filled = g_value_get_boolean (value);
153 break;
154
155 default:
156 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
157 break;
158 }
159 }
160
161 static void
gimp_canvas_rectangle_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)162 gimp_canvas_rectangle_get_property (GObject *object,
163 guint property_id,
164 GValue *value,
165 GParamSpec *pspec)
166 {
167 GimpCanvasRectanglePrivate *private = GET_PRIVATE (object);
168
169 switch (property_id)
170 {
171 case PROP_X:
172 g_value_set_double (value, private->x);
173 break;
174 case PROP_Y:
175 g_value_set_double (value, private->y);
176 break;
177 case PROP_WIDTH:
178 g_value_set_double (value, private->width);
179 break;
180 case PROP_HEIGHT:
181 g_value_set_double (value, private->height);
182 break;
183 case PROP_FILLED:
184 g_value_set_boolean (value, private->filled);
185 break;
186
187 default:
188 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
189 break;
190 }
191 }
192
193 static void
gimp_canvas_rectangle_transform(GimpCanvasItem * item,gdouble * x,gdouble * y,gdouble * w,gdouble * h)194 gimp_canvas_rectangle_transform (GimpCanvasItem *item,
195 gdouble *x,
196 gdouble *y,
197 gdouble *w,
198 gdouble *h)
199 {
200 GimpCanvasRectanglePrivate *private = GET_PRIVATE (item);
201 gdouble x1, y1;
202 gdouble x2, y2;
203
204 gimp_canvas_item_transform_xy_f (item,
205 MIN (private->x,
206 private->x + private->width),
207 MIN (private->y,
208 private->y + private->height),
209 &x1, &y1);
210 gimp_canvas_item_transform_xy_f (item,
211 MAX (private->x,
212 private->x + private->width),
213 MAX (private->y,
214 private->y + private->height),
215 &x2, &y2);
216
217 x1 = floor (x1);
218 y1 = floor (y1);
219 x2 = ceil (x2);
220 y2 = ceil (y2);
221
222 if (private->filled)
223 {
224 *x = x1;
225 *y = y1;
226 *w = x2 - x1;
227 *h = y2 - y1;
228 }
229 else
230 {
231 *x = x1 + 0.5;
232 *y = y1 + 0.5;
233 *w = x2 - 0.5 - *x;
234 *h = y2 - 0.5 - *y;
235
236 *w = MAX (0.0, *w);
237 *h = MAX (0.0, *h);
238 }
239 }
240
241 static void
gimp_canvas_rectangle_draw(GimpCanvasItem * item,cairo_t * cr)242 gimp_canvas_rectangle_draw (GimpCanvasItem *item,
243 cairo_t *cr)
244 {
245 GimpCanvasRectanglePrivate *private = GET_PRIVATE (item);
246 gdouble x, y;
247 gdouble w, h;
248
249 gimp_canvas_rectangle_transform (item, &x, &y, &w, &h);
250
251 cairo_rectangle (cr, x, y, w, h);
252
253 if (private->filled)
254 _gimp_canvas_item_fill (item, cr);
255 else
256 _gimp_canvas_item_stroke (item, cr);
257 }
258
259 static cairo_region_t *
gimp_canvas_rectangle_get_extents(GimpCanvasItem * item)260 gimp_canvas_rectangle_get_extents (GimpCanvasItem *item)
261 {
262 GimpCanvasRectanglePrivate *private = GET_PRIVATE (item);
263 cairo_rectangle_int_t rectangle;
264 gdouble x, y;
265 gdouble w, h;
266
267 gimp_canvas_rectangle_transform (item, &x, &y, &w, &h);
268
269 if (private->filled)
270 {
271 rectangle.x = floor (x - 1.0);
272 rectangle.y = floor (y - 1.0);
273 rectangle.width = ceil (w + 2.0);
274 rectangle.height = ceil (h + 2.0);
275
276 return cairo_region_create_rectangle (&rectangle);
277 }
278 else if (w > 64 && h > 64)
279 {
280 cairo_region_t *region;
281
282 /* left */
283 rectangle.x = floor (x - 1.5);
284 rectangle.y = floor (y - 1.5);
285 rectangle.width = 3.0;
286 rectangle.height = ceil (h + 3.0);
287
288 region = cairo_region_create_rectangle (&rectangle);
289
290 /* right */
291 rectangle.x = floor (x + w - 1.5);
292
293 cairo_region_union_rectangle (region, &rectangle);
294
295 /* top */
296 rectangle.x = floor (x - 1.5);
297 rectangle.y = floor (y - 1.5);
298 rectangle.width = ceil (w + 3.0);
299 rectangle.height = 3.0;
300
301 cairo_region_union_rectangle (region, &rectangle);
302
303 /* bottom */
304 rectangle.y = floor (y + h - 1.5);
305
306 cairo_region_union_rectangle (region, &rectangle);
307
308 return region;
309 }
310 else
311 {
312 rectangle.x = floor (x - 1.5);
313 rectangle.y = floor (y - 1.5);
314 rectangle.width = ceil (w + 3.0);
315 rectangle.height = ceil (h + 3.0);
316
317 return cairo_region_create_rectangle (&rectangle);
318 }
319 }
320
321 GimpCanvasItem *
gimp_canvas_rectangle_new(GimpDisplayShell * shell,gdouble x,gdouble y,gdouble width,gdouble height,gboolean filled)322 gimp_canvas_rectangle_new (GimpDisplayShell *shell,
323 gdouble x,
324 gdouble y,
325 gdouble width,
326 gdouble height,
327 gboolean filled)
328 {
329 g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL);
330
331 return g_object_new (GIMP_TYPE_CANVAS_RECTANGLE,
332 "shell", shell,
333 "x", x,
334 "y", y,
335 "width", width,
336 "height", height,
337 "filled", filled,
338 NULL);
339 }
340
341 void
gimp_canvas_rectangle_set(GimpCanvasItem * rectangle,gdouble x,gdouble y,gdouble width,gdouble height)342 gimp_canvas_rectangle_set (GimpCanvasItem *rectangle,
343 gdouble x,
344 gdouble y,
345 gdouble width,
346 gdouble height)
347 {
348 g_return_if_fail (GIMP_IS_CANVAS_RECTANGLE (rectangle));
349
350 gimp_canvas_item_begin_change (rectangle);
351
352 g_object_set (rectangle,
353 "x", x,
354 "y", y,
355 "width", width,
356 "height", height,
357 NULL);
358
359 gimp_canvas_item_end_change (rectangle);
360 }
361