Code

46d83c208a4ca082b93353d86177a401a3705f4d
[inkscape.git] / src / display / sodipodi-ctrlrect.cpp
1 #define __INKSCAPE_CTRLRECT_C__
3 /*
4  * Simple non-transformed rectangle, usable for rubberband
5  *
6  * Author:
7  *   Lauris Kaplinski <lauris@ximian.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *   Carl Hetherington <inkscape@carlh.net>
10  *
11  * Copyright (C) 1999-2001 Lauris Kaplinski
12  * Copyright (C) 2000-2001 Ximian, Inc.
13  *
14  * Released under GNU GPL
15  *
16  */
18 #include "display-forward.h"
19 #include "sp-canvas-util.h"
20 #include "sodipodi-ctrlrect.h"
21 #include "libnr/nr-pixops.h"
23 /*
24  * Currently we do not have point method, as it should always be painted
25  * during some transformation, which takes care of events...
26  *
27  * Corner coords can be in any order - i.e. x1 < x0 is allowed
28  */
30 static void sp_ctrlrect_class_init(SPCtrlRectClass *c);
31 static void sp_ctrlrect_init(CtrlRect *ctrlrect);
32 static void sp_ctrlrect_destroy(GtkObject *object);
34 static void sp_ctrlrect_update(SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags);
35 static void sp_ctrlrect_render(SPCanvasItem *item, SPCanvasBuf *buf);
37 static SPCanvasItemClass *parent_class;
39 static const guint DASH_LENGTH = 4;
41 GType sp_ctrlrect_get_type()
42 {
43     static GType type = 0;
45     if (!type) {
46         GTypeInfo info = {
47             sizeof(SPCtrlRectClass),
48             0, // base_init
49             0, // base_finalize
50             (GClassInitFunc)sp_ctrlrect_class_init,
51             0, // class_finalize
52             0, // class_data
53             sizeof(CtrlRect),
54             0, // n_preallocs
55             (GInstanceInitFunc)sp_ctrlrect_init,
56             0 // value_table
57         };
58         type = g_type_register_static(SP_TYPE_CANVAS_ITEM, "SPCtrlRect", &info, static_cast<GTypeFlags>(0));
59     }
60     return type;
61 }
63 static void sp_ctrlrect_class_init(SPCtrlRectClass *c)
64 {
65     GtkObjectClass *object_class = (GtkObjectClass *) c;
66     SPCanvasItemClass *item_class = (SPCanvasItemClass *) c;
68     parent_class = (SPCanvasItemClass*) gtk_type_class(sp_canvas_item_get_type());
70     object_class->destroy = sp_ctrlrect_destroy;
72     item_class->update = sp_ctrlrect_update;
73     item_class->render = sp_ctrlrect_render;
74 }
76 static void sp_ctrlrect_init(CtrlRect *cr)
77 {
78     cr->init();
79 }
81 static void sp_ctrlrect_destroy(GtkObject *object)
82 {
83     if (GTK_OBJECT_CLASS(parent_class)->destroy) {
84         (* GTK_OBJECT_CLASS(parent_class)->destroy)(object);
85     }
86 }
88 /* FIXME: use definitions from somewhere else */
89 #define RGBA_R(v) ((v) >> 24)
90 #define RGBA_G(v) (((v) >> 16) & 0xff)
91 #define RGBA_B(v) (((v) >> 8) & 0xff)
92 #define RGBA_A(v) ((v) & 0xff)
94 static void sp_ctrlrect_hline(SPCanvasBuf *buf, gint y, gint xs, gint xe, guint32 rgba, guint dashed)
95 {
96     if (y >= buf->rect.y0 && y < buf->rect.y1) {
97         guint const r = RGBA_R(rgba);
98         guint const g = RGBA_G(rgba);
99         guint const b = RGBA_B(rgba);
100         guint const a = RGBA_A(rgba);
101         gint const x0 = MAX(buf->rect.x0, xs);
102         gint const x1 = MIN(buf->rect.x1, xe + 1);
103         guchar *p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x0 - buf->rect.x0) * 4;
104         for (gint x = x0; x < x1; x++) {
105             if (!dashed || ((x / DASH_LENGTH) % 2)) {
106                 p[0] = INK_COMPOSE(r, a, p[0]);
107                 p[1] = INK_COMPOSE(g, a, p[1]);
108                 p[2] = INK_COMPOSE(b, a, p[2]);
109             }
110             p += 4;
111         }
112     }
115 static void sp_ctrlrect_vline(SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba, guint dashed)
117     if (x >= buf->rect.x0 && x < buf->rect.x1) {
118         guint const r = RGBA_R(rgba);
119         guint const g = RGBA_G(rgba);
120         guint const b = RGBA_B(rgba);
121         guint const a = RGBA_A(rgba);
122         gint const y0 = MAX(buf->rect.y0, ys);
123         gint const y1 = MIN(buf->rect.y1, ye + 1);
124         guchar *p = buf->buf + (y0 - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 4;
125         for (gint y = y0; y < y1; y++) {
126             if (!dashed || ((y / DASH_LENGTH) % 2)) {
127                 p[0] = INK_COMPOSE(r, a, p[0]);
128                 p[1] = INK_COMPOSE(g, a, p[1]);
129                 p[2] = INK_COMPOSE(b, a, p[2]);
130             }
131             p += buf->buf_rowstride;
132         }
133     }
136 /** Fills the pixels in [xs, xe)*[ys,ye) clipped to the tile with rgb * a. */
137 static void sp_ctrlrect_area(SPCanvasBuf *buf, gint xs, gint ys, gint xe, gint ye, guint32 rgba)
139     guint const r = RGBA_R(rgba);
140     guint const g = RGBA_G(rgba);
141     guint const b = RGBA_B(rgba);
142     guint const a = RGBA_A(rgba);
143     gint const x0 = MAX(buf->rect.x0, xs);
144     gint const x1 = MIN(buf->rect.x1, xe + 1);
145     gint const y0 = MAX(buf->rect.y0, ys);
146     gint const y1 = MIN(buf->rect.y1, ye + 1);
147     for (gint y = y0; y < y1; y++) {
148         guchar *p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x0 - buf->rect.x0) * 4;
149         for (gint x = x0; x < x1; x++) {
150             p[0] = INK_COMPOSE(r, a, p[0]);
151             p[1] = INK_COMPOSE(g, a, p[1]);
152             p[2] = INK_COMPOSE(b, a, p[2]);
153             p += 4;
154         }
155     }
158 static void sp_ctrlrect_render(SPCanvasItem *item, SPCanvasBuf *buf)
160     SP_CTRLRECT(item)->render(buf);
164 static void sp_ctrlrect_update(SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
166     SP_CTRLRECT(item)->update(affine, flags);
171 void CtrlRect::init()
173     _has_fill = false;
174     _dashed = false;
175     _shadow = 0;
177     _area.x0 = _area.y0 = 0;
178     _area.x1 = _area.y1 = 0;
180     _rect = Geom::Rect(Geom::Point(0,0),Geom::Point(0,0));
182     _shadow_size = 0;
184     _border_color = 0x000000ff;
185     _fill_color = 0xffffffff;
186     _shadow_color = 0x000000ff;
190 void CtrlRect::render(SPCanvasBuf *buf)
192     if ((_area.x0 != 0 || _area.x1 != 0 || _area.y0 != 0 || _area.y1 != 0) &&
193         (_area.x0 < buf->rect.x1) &&
194         (_area.y0 < buf->rect.y1) &&
195         ((_area.x1 + _shadow_size) >= buf->rect.x0) &&
196         ((_area.y1 + _shadow_size) >= buf->rect.y0)) {
197         sp_canvas_prepare_buffer(buf);
199         /* Top */
200         sp_ctrlrect_hline(buf, _area.y0, _area.x0, _area.x1, _border_color, _dashed);
201         /* Bottom */
202         sp_ctrlrect_hline(buf, _area.y1, _area.x0, _area.x1, _border_color, _dashed);
203         /* Left */
204         sp_ctrlrect_vline(buf, _area.x0, _area.y0 + 1, _area.y1 - 1, _border_color, _dashed);
205         /* Right */
206         sp_ctrlrect_vline(buf, _area.x1, _area.y0 + 1, _area.y1 - 1, _border_color, _dashed);
207         if (_shadow_size > 0) {
208             /* Right shadow */
209             sp_ctrlrect_area(buf, _area.x1 + 1, _area.y0 + _shadow_size,
210                              _area.x1 + _shadow_size, _area.y1 + _shadow_size, _shadow_color);
211             /* Bottom shadow */
212             sp_ctrlrect_area(buf, _area.x0 + _shadow_size, _area.y1 + 1,
213                              _area.x1, _area.y1 + _shadow_size, _shadow_color);
214         }
215         if (_has_fill) {
216             /* Fill */
217             sp_ctrlrect_area(buf, _area.x0 + 1, _area.y0 + 1,
218                              _area.x1 - 1, _area.y1 - 1, _fill_color);
219         }
220     }
224 void CtrlRect::update(Geom::Matrix const &affine, unsigned int flags)
226     if (((SPCanvasItemClass *) parent_class)->update) {
227         ((SPCanvasItemClass *) parent_class)->update(this, affine, flags);
228     }
230     sp_canvas_item_reset_bounds(this);
232     NRRectL _area_old;
233     _area_old.x0 = _area.x0;
234     _area_old.x1 = _area.x1;
235     _area_old.y0 = _area.y0;
236     _area_old.y1 = _area.y1;
238     Geom::Rect bbox(_rect.min() * affine, _rect.max() * affine);
240     _area.x0 = (int) floor(bbox.min()[Geom::X] + 0.5);
241     _area.y0 = (int) floor(bbox.min()[Geom::Y] + 0.5);
242     _area.x1 = (int) floor(bbox.max()[Geom::X] + 0.5);
243     _area.y1 = (int) floor(bbox.max()[Geom::Y] + 0.5);
245     gint _shadow_size_old = _shadow_size;
246     _shadow_size = _shadow;
248     // FIXME: we don't process a possible change in _has_fill
249     if (_has_fill) {
250         if (_area_old.x0 != 0 || _area_old.x1 != 0 || _area_old.y0 != 0 || _area_old.y1 != 0) {
251             sp_canvas_request_redraw(canvas,
252                                  _area_old.x0 - 1, _area_old.y0 - 1,
253                                  _area_old.x1 + _shadow_size + 1, _area_old.y1 + _shadow_size + 1);
254         }
255         if (_area.x0 != 0 || _area.x1 != 0 || _area.y0 != 0 || _area.y1 != 0) {
256             sp_canvas_request_redraw(canvas,
257                                  _area.x0 - 1, _area.y0 - 1,
258                                  _area.x1 + _shadow_size + 1, _area.y1 + _shadow_size + 1);
259         }
260     } else { // clear box, be smart about what part of the frame to redraw
262         /* Top */
263         if (_area.y0 != _area_old.y0) { // different level, redraw fully old and new
264             if (_area_old.x0 != _area_old.x1)
265                 sp_canvas_request_redraw(canvas,
266                                          _area_old.x0 - 1, _area_old.y0 - 1,
267                                          _area_old.x1 + 1, _area_old.y0 + 1);
269             if (_area.x0 != _area.x1)
270                 sp_canvas_request_redraw(canvas,
271                                          _area.x0 - 1, _area.y0 - 1,
272                                          _area.x1 + 1, _area.y0 + 1);
273         } else { // same level, redraw only the ends
274             if (_area.x0 != _area_old.x0) {
275                 sp_canvas_request_redraw(canvas,
276                                          MIN(_area_old.x0,_area.x0) - 1, _area.y0 - 1,
277                                          MAX(_area_old.x0,_area.x0) + 1, _area.y0 + 1);
278             }
279             if (_area.x1 != _area_old.x1) {
280                 sp_canvas_request_redraw(canvas,
281                                          MIN(_area_old.x1,_area.x1) - 1, _area.y0 - 1,
282                                          MAX(_area_old.x1,_area.x1) + 1, _area.y0 + 1);
283             }
284         }
286         /* Left */
287         if (_area.x0 != _area_old.x0) { // different level, redraw fully old and new
288             if (_area_old.y0 != _area_old.y1)
289                 sp_canvas_request_redraw(canvas,
290                                          _area_old.x0 - 1, _area_old.y0 - 1,
291                                          _area_old.x0 + 1, _area_old.y1 + 1);
293             if (_area.y0 != _area.y1)
294                 sp_canvas_request_redraw(canvas,
295                                          _area.x0 - 1, _area.y0 - 1,
296                                          _area.x0 + 1, _area.y1 + 1);
297         } else { // same level, redraw only the ends
298             if (_area.y0 != _area_old.y0) {
299                 sp_canvas_request_redraw(canvas,
300                                          _area.x0 - 1, MIN(_area_old.y0,_area.y0) - 1, 
301                                          _area.x0 + 1, MAX(_area_old.y0,_area.y0) + 1);
302             }
303             if (_area.y1 != _area_old.y1) {
304                 sp_canvas_request_redraw(canvas,
305                                          _area.x0 - 1, MIN(_area_old.y1,_area.y1) - 1, 
306                                          _area.x0 + 1, MAX(_area_old.y1,_area.y1) + 1);
307             }
308         }
310         /* Right */
311         if (_area.x1 != _area_old.x1 || _shadow_size_old != _shadow_size) { 
312             if (_area_old.y0 != _area_old.y1)
313                 sp_canvas_request_redraw(canvas,
314                                          _area_old.x1 - 1, _area_old.y0 - 1,
315                                          _area_old.x1 + _shadow_size + 1, _area_old.y1 + _shadow_size + 1);
317             if (_area.y0 != _area.y1)
318                 sp_canvas_request_redraw(canvas,
319                                          _area.x1 - 1, _area.y0 - 1,
320                                          _area.x1 + _shadow_size + 1, _area.y1 + _shadow_size + 1);
321         } else { // same level, redraw only the ends
322             if (_area.y0 != _area_old.y0) {
323                 sp_canvas_request_redraw(canvas,
324                                          _area.x1 - 1, MIN(_area_old.y0,_area.y0) - 1, 
325                                          _area.x1 + _shadow_size + 1, MAX(_area_old.y0,_area.y0) + _shadow_size + 1);
326             }
327             if (_area.y1 != _area_old.y1) {
328                 sp_canvas_request_redraw(canvas,
329                                          _area.x1 - 1, MIN(_area_old.y1,_area.y1) - 1, 
330                                          _area.x1 + _shadow_size + 1, MAX(_area_old.y1,_area.y1) + _shadow_size + 1);
331             }
332         }
334         /* Bottom */
335         if (_area.y1 != _area_old.y1 || _shadow_size_old != _shadow_size) { 
336             if (_area_old.x0 != _area_old.x1)
337                 sp_canvas_request_redraw(canvas,
338                                          _area_old.x0 - 1, _area_old.y1 - 1,
339                                          _area_old.x1 + _shadow_size + 1, _area_old.y1 + _shadow_size + 1);
341             if (_area.x0 != _area.x1)
342                 sp_canvas_request_redraw(canvas,
343                                          _area.x0 - 1, _area.y1 - 1,
344                                          _area.x1 + _shadow_size + 1, _area.y1 + _shadow_size + 1);
345         } else { // same level, redraw only the ends
346             if (_area.x0 != _area_old.x0) {
347                 sp_canvas_request_redraw(canvas,
348                                          MIN(_area_old.x0,_area.x0) - 1, _area.y1 - 1,
349                                          MAX(_area_old.x0,_area.x0) + _shadow_size + 1, _area.y1 + _shadow_size + 1);
350             }
351             if (_area.x1 != _area_old.x1) {
352                 sp_canvas_request_redraw(canvas,
353                                          MIN(_area_old.x1,_area.x1) - 1, _area.y1 - 1,
354                                          MAX(_area_old.x1,_area.x1) + _shadow_size + 1, _area.y1 + _shadow_size + 1);
355             }
356         }
357     }
359     // update SPCanvasItem box
360     if (_area.x0 != 0 || _area.x1 != 0 || _area.y0 != 0 || _area.y1 != 0) {
361         x1 = _area.x0 - 1;
362         y1 = _area.y0 - 1;
363         x2 = _area.x1 + _shadow_size + 1;
364         y2 = _area.y1 + _shadow_size + 1;
365     }
369 void CtrlRect::setColor(guint32 b, bool h, guint f)
371     _border_color = b;
372     _has_fill = h;
373     _fill_color = f;
374     _requestUpdate();
377 void CtrlRect::setShadow(int s, guint c)
379     _shadow = s;
380     _shadow_color = c;
381     _requestUpdate();
384 void CtrlRect::setRectangle(Geom::Rect const &r)
386     _rect = r;
387     _requestUpdate();
390 void CtrlRect::setDashed(bool d)
392     _dashed = d;
393     _requestUpdate();
396 void CtrlRect::_requestUpdate()
398     sp_canvas_item_request_update(SP_CANVAS_ITEM(this));
401 /*
402   Local Variables:
403   mode:c++
404   c-file-style:"stroustrup"
405   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
406   indent-tabs-mode:nil
407   fill-column:99
408   End:
409 */
410 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :