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