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 "sp-canvas-util.h"
19 #include "sodipodi-ctrlrect.h"
20 #include "libnr/nr-pixops.h"
22 /*
23 * Currently we do not have point method, as it should always be painted
24 * during some transformation, which takes care of events...
25 *
26 * Corner coords can be in any order - i.e. x1 < x0 is allowed
27 */
29 static void sp_ctrlrect_class_init(SPCtrlRectClass *c);
30 static void sp_ctrlrect_init(CtrlRect *ctrlrect);
31 static void sp_ctrlrect_destroy(GtkObject *object);
33 static void sp_ctrlrect_update(SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags);
34 static void sp_ctrlrect_render(SPCanvasItem *item, SPCanvasBuf *buf);
36 static SPCanvasItemClass *parent_class;
38 static const guint DASH_LENGTH = 4;
40 GType sp_ctrlrect_get_type()
41 {
42 static GType type = 0;
44 if (!type) {
45 GTypeInfo info = {
46 sizeof(SPCtrlRectClass),
47 0, // base_init
48 0, // base_finalize
49 (GClassInitFunc)sp_ctrlrect_class_init,
50 0, // class_finalize
51 0, // class_data
52 sizeof(CtrlRect),
53 0, // n_preallocs
54 (GInstanceInitFunc)sp_ctrlrect_init,
55 0 // value_table
56 };
57 type = g_type_register_static(SP_TYPE_CANVAS_ITEM, "SPCtrlRect", &info, static_cast<GTypeFlags>(0));
58 }
59 return type;
60 }
62 static void sp_ctrlrect_class_init(SPCtrlRectClass *c)
63 {
64 GtkObjectClass *object_class = (GtkObjectClass *) c;
65 SPCanvasItemClass *item_class = (SPCanvasItemClass *) c;
67 parent_class = (SPCanvasItemClass*) gtk_type_class(sp_canvas_item_get_type());
69 object_class->destroy = sp_ctrlrect_destroy;
71 item_class->update = sp_ctrlrect_update;
72 item_class->render = sp_ctrlrect_render;
73 }
75 static void sp_ctrlrect_init(CtrlRect *cr)
76 {
77 cr->init();
78 }
80 static void sp_ctrlrect_destroy(GtkObject *object)
81 {
82 if (GTK_OBJECT_CLASS(parent_class)->destroy) {
83 (* GTK_OBJECT_CLASS(parent_class)->destroy)(object);
84 }
85 }
87 /* FIXME: use definitions from somewhere else */
88 #define RGBA_R(v) ((v) >> 24)
89 #define RGBA_G(v) (((v) >> 16) & 0xff)
90 #define RGBA_B(v) (((v) >> 8) & 0xff)
91 #define RGBA_A(v) ((v) & 0xff)
93 static void sp_ctrlrect_hline(SPCanvasBuf *buf, gint y, gint xs, gint xe, guint32 rgba, guint dashed)
94 {
95 if (y >= buf->rect.y0 && y < buf->rect.y1) {
96 guint const r = RGBA_R(rgba);
97 guint const g = RGBA_G(rgba);
98 guint const b = RGBA_B(rgba);
99 guint const a = RGBA_A(rgba);
100 gint const x0 = MAX(buf->rect.x0, xs);
101 gint const x1 = MIN(buf->rect.x1, xe + 1);
102 guchar *p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x0 - buf->rect.x0) * 4;
103 for (gint x = x0; x < x1; x++) {
104 if (!dashed || ((x / DASH_LENGTH) % 2)) {
105 p[0] = INK_COMPOSE(r, a, p[0]);
106 p[1] = INK_COMPOSE(g, a, p[1]);
107 p[2] = INK_COMPOSE(b, a, p[2]);
108 }
109 p += 4;
110 }
111 }
112 }
114 static void sp_ctrlrect_vline(SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba, guint dashed)
115 {
116 if (x >= buf->rect.x0 && x < buf->rect.x1) {
117 guint const r = RGBA_R(rgba);
118 guint const g = RGBA_G(rgba);
119 guint const b = RGBA_B(rgba);
120 guint const a = RGBA_A(rgba);
121 gint const y0 = MAX(buf->rect.y0, ys);
122 gint const y1 = MIN(buf->rect.y1, ye + 1);
123 guchar *p = buf->buf + (y0 - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 4;
124 for (gint y = y0; y < y1; y++) {
125 if (!dashed || ((y / DASH_LENGTH) % 2)) {
126 p[0] = INK_COMPOSE(r, a, p[0]);
127 p[1] = INK_COMPOSE(g, a, p[1]);
128 p[2] = INK_COMPOSE(b, a, p[2]);
129 }
130 p += buf->buf_rowstride;
131 }
132 }
133 }
135 /** Fills the pixels in [xs, xe)*[ys,ye) clipped to the tile with rgb * a. */
136 static void sp_ctrlrect_area(SPCanvasBuf *buf, gint xs, gint ys, gint xe, gint ye, guint32 rgba)
137 {
138 guint const r = RGBA_R(rgba);
139 guint const g = RGBA_G(rgba);
140 guint const b = RGBA_B(rgba);
141 guint const a = RGBA_A(rgba);
142 gint const x0 = MAX(buf->rect.x0, xs);
143 gint const x1 = MIN(buf->rect.x1, xe + 1);
144 gint const y0 = MAX(buf->rect.y0, ys);
145 gint const y1 = MIN(buf->rect.y1, ye + 1);
146 for (gint y = y0; y < y1; y++) {
147 guchar *p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x0 - buf->rect.x0) * 4;
148 for (gint x = x0; x < x1; x++) {
149 p[0] = INK_COMPOSE(r, a, p[0]);
150 p[1] = INK_COMPOSE(g, a, p[1]);
151 p[2] = INK_COMPOSE(b, a, p[2]);
152 p += 4;
153 }
154 }
155 }
157 static void sp_ctrlrect_render(SPCanvasItem *item, SPCanvasBuf *buf)
158 {
159 SP_CTRLRECT(item)->render(buf);
160 }
163 static void sp_ctrlrect_update(SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
164 {
165 SP_CTRLRECT(item)->update(affine, flags);
166 }
170 void CtrlRect::init()
171 {
172 _has_fill = false;
173 _dashed = false;
174 _shadow = 0;
176 _area.x0 = _area.y0 = 0;
177 _area.x1 = _area.y1 = 0;
179 _rect = Geom::Rect(Geom::Point(0,0),Geom::Point(0,0));
181 _shadow_size = 0;
183 _border_color = 0x000000ff;
184 _fill_color = 0xffffffff;
185 _shadow_color = 0x000000ff;
186 }
189 void CtrlRect::render(SPCanvasBuf *buf)
190 {
191 if ((_area.x0 != 0 || _area.x1 != 0 || _area.y0 != 0 || _area.y1 != 0) &&
192 (_area.x0 < buf->rect.x1) &&
193 (_area.y0 < buf->rect.y1) &&
194 ((_area.x1 + _shadow_size) >= buf->rect.x0) &&
195 ((_area.y1 + _shadow_size) >= buf->rect.y0)) {
196 sp_canvas_prepare_buffer(buf);
198 /* Top */
199 sp_ctrlrect_hline(buf, _area.y0, _area.x0, _area.x1, _border_color, _dashed);
200 /* Bottom */
201 sp_ctrlrect_hline(buf, _area.y1, _area.x0, _area.x1, _border_color, _dashed);
202 /* Left */
203 sp_ctrlrect_vline(buf, _area.x0, _area.y0 + 1, _area.y1 - 1, _border_color, _dashed);
204 /* Right */
205 sp_ctrlrect_vline(buf, _area.x1, _area.y0 + 1, _area.y1 - 1, _border_color, _dashed);
206 if (_shadow_size > 0) {
207 /* Right shadow */
208 sp_ctrlrect_area(buf, _area.x1 + 1, _area.y0 + _shadow_size,
209 _area.x1 + _shadow_size, _area.y1 + _shadow_size, _shadow_color);
210 /* Bottom shadow */
211 sp_ctrlrect_area(buf, _area.x0 + _shadow_size, _area.y1 + 1,
212 _area.x1, _area.y1 + _shadow_size, _shadow_color);
213 }
214 if (_has_fill) {
215 /* Fill */
216 sp_ctrlrect_area(buf, _area.x0 + 1, _area.y0 + 1,
217 _area.x1 - 1, _area.y1 - 1, _fill_color);
218 }
219 }
220 }
223 void CtrlRect::update(Geom::Matrix const &affine, unsigned int flags)
224 {
225 if (((SPCanvasItemClass *) parent_class)->update) {
226 ((SPCanvasItemClass *) parent_class)->update(this, affine, flags);
227 }
229 sp_canvas_item_reset_bounds(this);
231 NRRectL _area_old;
232 _area_old.x0 = _area.x0;
233 _area_old.x1 = _area.x1;
234 _area_old.y0 = _area.y0;
235 _area_old.y1 = _area.y1;
237 Geom::Rect bbox(_rect.min() * affine, _rect.max() * affine);
239 _area.x0 = (int) floor(bbox.min()[Geom::X] + 0.5);
240 _area.y0 = (int) floor(bbox.min()[Geom::Y] + 0.5);
241 _area.x1 = (int) floor(bbox.max()[Geom::X] + 0.5);
242 _area.y1 = (int) floor(bbox.max()[Geom::Y] + 0.5);
244 gint _shadow_size_old = _shadow_size;
245 _shadow_size = _shadow;
247 // FIXME: we don't process a possible change in _has_fill
248 if (_has_fill) {
249 if (_area_old.x0 != 0 || _area_old.x1 != 0 || _area_old.y0 != 0 || _area_old.y1 != 0) {
250 sp_canvas_request_redraw(canvas,
251 _area_old.x0 - 1, _area_old.y0 - 1,
252 _area_old.x1 + _shadow_size + 1, _area_old.y1 + _shadow_size + 1);
253 }
254 if (_area.x0 != 0 || _area.x1 != 0 || _area.y0 != 0 || _area.y1 != 0) {
255 sp_canvas_request_redraw(canvas,
256 _area.x0 - 1, _area.y0 - 1,
257 _area.x1 + _shadow_size + 1, _area.y1 + _shadow_size + 1);
258 }
259 } else { // clear box, be smart about what part of the frame to redraw
261 /* Top */
262 if (_area.y0 != _area_old.y0) { // different level, redraw fully old and new
263 if (_area_old.x0 != _area_old.x1)
264 sp_canvas_request_redraw(canvas,
265 _area_old.x0 - 1, _area_old.y0 - 1,
266 _area_old.x1 + 1, _area_old.y0 + 1);
268 if (_area.x0 != _area.x1)
269 sp_canvas_request_redraw(canvas,
270 _area.x0 - 1, _area.y0 - 1,
271 _area.x1 + 1, _area.y0 + 1);
272 } else { // same level, redraw only the ends
273 if (_area.x0 != _area_old.x0) {
274 sp_canvas_request_redraw(canvas,
275 MIN(_area_old.x0,_area.x0) - 1, _area.y0 - 1,
276 MAX(_area_old.x0,_area.x0) + 1, _area.y0 + 1);
277 }
278 if (_area.x1 != _area_old.x1) {
279 sp_canvas_request_redraw(canvas,
280 MIN(_area_old.x1,_area.x1) - 1, _area.y0 - 1,
281 MAX(_area_old.x1,_area.x1) + 1, _area.y0 + 1);
282 }
283 }
285 /* Left */
286 if (_area.x0 != _area_old.x0) { // different level, redraw fully old and new
287 if (_area_old.y0 != _area_old.y1)
288 sp_canvas_request_redraw(canvas,
289 _area_old.x0 - 1, _area_old.y0 - 1,
290 _area_old.x0 + 1, _area_old.y1 + 1);
292 if (_area.y0 != _area.y1)
293 sp_canvas_request_redraw(canvas,
294 _area.x0 - 1, _area.y0 - 1,
295 _area.x0 + 1, _area.y1 + 1);
296 } else { // same level, redraw only the ends
297 if (_area.y0 != _area_old.y0) {
298 sp_canvas_request_redraw(canvas,
299 _area.x0 - 1, MIN(_area_old.y0,_area.y0) - 1,
300 _area.x0 + 1, MAX(_area_old.y0,_area.y0) + 1);
301 }
302 if (_area.y1 != _area_old.y1) {
303 sp_canvas_request_redraw(canvas,
304 _area.x0 - 1, MIN(_area_old.y1,_area.y1) - 1,
305 _area.x0 + 1, MAX(_area_old.y1,_area.y1) + 1);
306 }
307 }
309 /* Right */
310 if (_area.x1 != _area_old.x1 || _shadow_size_old != _shadow_size) {
311 if (_area_old.y0 != _area_old.y1)
312 sp_canvas_request_redraw(canvas,
313 _area_old.x1 - 1, _area_old.y0 - 1,
314 _area_old.x1 + _shadow_size + 1, _area_old.y1 + _shadow_size + 1);
316 if (_area.y0 != _area.y1)
317 sp_canvas_request_redraw(canvas,
318 _area.x1 - 1, _area.y0 - 1,
319 _area.x1 + _shadow_size + 1, _area.y1 + _shadow_size + 1);
320 } else { // same level, redraw only the ends
321 if (_area.y0 != _area_old.y0) {
322 sp_canvas_request_redraw(canvas,
323 _area.x1 - 1, MIN(_area_old.y0,_area.y0) - 1,
324 _area.x1 + _shadow_size + 1, MAX(_area_old.y0,_area.y0) + _shadow_size + 1);
325 }
326 if (_area.y1 != _area_old.y1) {
327 sp_canvas_request_redraw(canvas,
328 _area.x1 - 1, MIN(_area_old.y1,_area.y1) - 1,
329 _area.x1 + _shadow_size + 1, MAX(_area_old.y1,_area.y1) + _shadow_size + 1);
330 }
331 }
333 /* Bottom */
334 if (_area.y1 != _area_old.y1 || _shadow_size_old != _shadow_size) {
335 if (_area_old.x0 != _area_old.x1)
336 sp_canvas_request_redraw(canvas,
337 _area_old.x0 - 1, _area_old.y1 - 1,
338 _area_old.x1 + _shadow_size + 1, _area_old.y1 + _shadow_size + 1);
340 if (_area.x0 != _area.x1)
341 sp_canvas_request_redraw(canvas,
342 _area.x0 - 1, _area.y1 - 1,
343 _area.x1 + _shadow_size + 1, _area.y1 + _shadow_size + 1);
344 } else { // same level, redraw only the ends
345 if (_area.x0 != _area_old.x0) {
346 sp_canvas_request_redraw(canvas,
347 MIN(_area_old.x0,_area.x0) - 1, _area.y1 - 1,
348 MAX(_area_old.x0,_area.x0) + _shadow_size + 1, _area.y1 + _shadow_size + 1);
349 }
350 if (_area.x1 != _area_old.x1) {
351 sp_canvas_request_redraw(canvas,
352 MIN(_area_old.x1,_area.x1) - 1, _area.y1 - 1,
353 MAX(_area_old.x1,_area.x1) + _shadow_size + 1, _area.y1 + _shadow_size + 1);
354 }
355 }
356 }
358 // update SPCanvasItem box
359 if (_area.x0 != 0 || _area.x1 != 0 || _area.y0 != 0 || _area.y1 != 0) {
360 x1 = _area.x0 - 1;
361 y1 = _area.y0 - 1;
362 x2 = _area.x1 + _shadow_size + 1;
363 y2 = _area.y1 + _shadow_size + 1;
364 }
365 }
368 void CtrlRect::setColor(guint32 b, bool h, guint f)
369 {
370 _border_color = b;
371 _has_fill = h;
372 _fill_color = f;
373 _requestUpdate();
374 }
376 void CtrlRect::setShadow(int s, guint c)
377 {
378 _shadow = s;
379 _shadow_color = c;
380 _requestUpdate();
381 }
383 void CtrlRect::setRectangle(Geom::Rect const &r)
384 {
385 _rect = r;
386 _requestUpdate();
387 }
389 void CtrlRect::setDashed(bool d)
390 {
391 _dashed = d;
392 _requestUpdate();
393 }
395 void CtrlRect::_requestUpdate()
396 {
397 sp_canvas_item_request_update(SP_CANVAS_ITEM(this));
398 }
400 /*
401 Local Variables:
402 mode:c++
403 c-file-style:"stroustrup"
404 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
405 indent-tabs-mode:nil
406 fill-column:99
407 End:
408 */
409 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :