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 }
113 }
115 static void sp_ctrlrect_vline(SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba, guint dashed)
116 {
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 }
134 }
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)
138 {
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 }
156 }
158 static void sp_ctrlrect_render(SPCanvasItem *item, SPCanvasBuf *buf)
159 {
160 SP_CTRLRECT(item)->render(buf);
161 }
164 static void sp_ctrlrect_update(SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
165 {
166 SP_CTRLRECT(item)->update(affine, flags);
167 }
171 void CtrlRect::init()
172 {
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;
187 }
190 void CtrlRect::render(SPCanvasBuf *buf)
191 {
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 }
221 }
224 void CtrlRect::update(Geom::Matrix const &affine, unsigned int flags)
225 {
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 }
366 }
369 void CtrlRect::setColor(guint32 b, bool h, guint f)
370 {
371 _border_color = b;
372 _has_fill = h;
373 _fill_color = f;
374 _requestUpdate();
375 }
377 void CtrlRect::setShadow(int s, guint c)
378 {
379 _shadow = s;
380 _shadow_color = c;
381 _requestUpdate();
382 }
384 void CtrlRect::setRectangle(Geom::Rect const &r)
385 {
386 _rect = r;
387 _requestUpdate();
388 }
390 void CtrlRect::setDashed(bool d)
391 {
392 _dashed = d;
393 _requestUpdate();
394 }
396 void CtrlRect::_requestUpdate()
397 {
398 sp_canvas_item_request_update(SP_CANVAS_ITEM(this));
399 }
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:encoding=utf-8:textwidth=99 :