1 #define INKSCAPE_CTRL_C
3 /*
4 * SPCtrl
5 *
6 * We render it by hand to reduce allocing/freeing svps & to get clean
7 * (non-aa) images
8 *
9 */
11 #include <2geom/transforms.h>
12 #include "sp-canvas-util.h"
13 #include "display-forward.h"
14 #include "sodipodi-ctrl.h"
15 #include "libnr/nr-pixops.h"
17 enum {
18 ARG_0,
19 ARG_SHAPE,
20 ARG_MODE,
21 ARG_ANCHOR,
22 ARG_SIZE,
23 ARG_FILLED,
24 ARG_FILL_COLOR,
25 ARG_STROKED,
26 ARG_STROKE_COLOR,
27 ARG_PIXBUF
28 };
31 static void sp_ctrl_class_init (SPCtrlClass *klass);
32 static void sp_ctrl_init (SPCtrl *ctrl);
33 static void sp_ctrl_destroy (GtkObject *object);
34 static void sp_ctrl_set_arg (GtkObject *object, GtkArg *arg, guint arg_id);
36 static void sp_ctrl_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags);
37 static void sp_ctrl_render (SPCanvasItem *item, SPCanvasBuf *buf);
39 static double sp_ctrl_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item);
42 static SPCanvasItemClass *parent_class;
44 GtkType
45 sp_ctrl_get_type (void)
46 {
47 static GtkType ctrl_type = 0;
48 if (!ctrl_type) {
49 static GTypeInfo const ctrl_info = {
50 sizeof (SPCtrlClass),
51 NULL, /* base_init */
52 NULL, /* base_finalize */
53 (GClassInitFunc) sp_ctrl_class_init,
54 NULL, /* class_finalize */
55 NULL, /* class_data */
56 sizeof (SPCtrl),
57 0, /* n_preallocs */
58 (GInstanceInitFunc) sp_ctrl_init,
59 NULL
60 };
61 ctrl_type = g_type_register_static (SP_TYPE_CANVAS_ITEM, "SPCtrl", &ctrl_info, (GTypeFlags)0);
62 }
63 return ctrl_type;
64 }
66 static void
67 sp_ctrl_class_init (SPCtrlClass *klass)
68 {
69 GtkObjectClass *object_class;
70 SPCanvasItemClass *item_class;
72 object_class = (GtkObjectClass *) klass;
73 item_class = (SPCanvasItemClass *) klass;
75 parent_class = (SPCanvasItemClass *)gtk_type_class (sp_canvas_item_get_type ());
77 gtk_object_add_arg_type ("SPCtrl::shape", GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_SHAPE);
78 gtk_object_add_arg_type ("SPCtrl::mode", GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_MODE);
79 gtk_object_add_arg_type ("SPCtrl::anchor", GTK_TYPE_ANCHOR_TYPE, GTK_ARG_READWRITE, ARG_ANCHOR);
80 gtk_object_add_arg_type ("SPCtrl::size", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_SIZE);
81 gtk_object_add_arg_type ("SPCtrl::pixbuf", GTK_TYPE_POINTER, GTK_ARG_READWRITE, ARG_PIXBUF);
82 gtk_object_add_arg_type ("SPCtrl::filled", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_FILLED);
83 gtk_object_add_arg_type ("SPCtrl::fill_color", GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_FILL_COLOR);
84 gtk_object_add_arg_type ("SPCtrl::stroked", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_STROKED);
85 gtk_object_add_arg_type ("SPCtrl::stroke_color", GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_STROKE_COLOR);
87 object_class->destroy = sp_ctrl_destroy;
88 object_class->set_arg = sp_ctrl_set_arg;
90 item_class->update = sp_ctrl_update;
91 item_class->render = sp_ctrl_render;
92 item_class->point = sp_ctrl_point;
93 }
95 static void
96 sp_ctrl_init (SPCtrl *ctrl)
97 {
98 ctrl->shape = SP_CTRL_SHAPE_SQUARE;
99 ctrl->mode = SP_CTRL_MODE_COLOR;
100 ctrl->anchor = GTK_ANCHOR_CENTER;
101 ctrl->span = 3;
102 ctrl->defined = TRUE;
103 ctrl->shown = FALSE;
104 ctrl->build = FALSE;
105 ctrl->filled = 1;
106 ctrl->stroked = 0;
107 ctrl->fill_color = 0x000000ff;
108 ctrl->stroke_color = 0x000000ff;
109 ctrl->_moved = false;
111 ctrl->box.x0 = ctrl->box.y0 = ctrl->box.x1 = ctrl->box.y1 = 0;
112 ctrl->cache = NULL;
113 ctrl->pixbuf = NULL;
114 }
116 static void
117 sp_ctrl_destroy (GtkObject *object)
118 {
119 SPCtrl *ctrl;
121 g_return_if_fail (object != NULL);
122 g_return_if_fail (SP_IS_CTRL (object));
124 ctrl = SP_CTRL (object);
126 if (ctrl->cache) {
127 g_free(ctrl->cache);
128 ctrl->cache = NULL;
129 }
131 if (GTK_OBJECT_CLASS (parent_class)->destroy)
132 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
133 }
135 static void
136 sp_ctrl_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
137 {
138 SPCanvasItem *item;
139 SPCtrl *ctrl;
140 GdkPixbuf * pixbuf = NULL;
142 item = SP_CANVAS_ITEM (object);
143 ctrl = SP_CTRL (object);
145 switch (arg_id) {
146 case ARG_SHAPE:
147 ctrl->shape = (SPCtrlShapeType)(GTK_VALUE_INT (*arg));
148 ctrl->build = FALSE;
149 sp_canvas_item_request_update (item);
150 break;
152 case ARG_MODE:
153 ctrl->mode = (SPCtrlModeType)(GTK_VALUE_INT (*arg));
154 ctrl->build = FALSE;
155 sp_canvas_item_request_update (item);
156 break;
158 case ARG_ANCHOR:
159 ctrl->anchor = (GtkAnchorType)(GTK_VALUE_INT (*arg));
160 ctrl->build = FALSE;
161 sp_canvas_item_request_update (item);
162 break;
164 case ARG_SIZE:
165 ctrl->span = (gint) ((GTK_VALUE_DOUBLE (*arg) - 1.0) / 2.0 + 0.5);
166 ctrl->defined = (ctrl->span > 0);
167 ctrl->build = FALSE;
168 sp_canvas_item_request_update (item);
169 break;
171 case ARG_FILLED:
172 ctrl->filled = GTK_VALUE_BOOL (*arg);
173 ctrl->build = FALSE;
174 sp_canvas_item_request_update (item);
175 break;
177 case ARG_FILL_COLOR:
178 ctrl->fill_color = GTK_VALUE_INT (*arg);
179 ctrl->build = FALSE;
180 sp_canvas_item_request_update (item);
181 break;
183 case ARG_STROKED:
184 ctrl->stroked = GTK_VALUE_BOOL (*arg);
185 ctrl->build = FALSE;
186 sp_canvas_item_request_update (item);
187 break;
189 case ARG_STROKE_COLOR:
190 ctrl->stroke_color = GTK_VALUE_INT (*arg);
191 ctrl->build = FALSE;
192 sp_canvas_item_request_update (item);
193 break;
195 case ARG_PIXBUF:
196 pixbuf = (GdkPixbuf*)(GTK_VALUE_POINTER (*arg));
197 if (gdk_pixbuf_get_has_alpha (pixbuf)) {
198 ctrl->pixbuf = pixbuf;
199 } else {
200 ctrl->pixbuf = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
201 gdk_pixbuf_unref (pixbuf);
202 }
203 ctrl->build = FALSE;
204 break;
206 default:
207 break;
208 }
209 }
211 static void
212 sp_ctrl_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
213 {
214 SPCtrl *ctrl;
215 gint x, y;
217 ctrl = SP_CTRL (item);
219 if (((SPCanvasItemClass *) parent_class)->update)
220 (* ((SPCanvasItemClass *) parent_class)->update) (item, affine, flags);
222 sp_canvas_item_reset_bounds (item);
224 if (!ctrl->_moved) return;
226 if (ctrl->shown) {
227 sp_canvas_request_redraw (item->canvas, ctrl->box.x0, ctrl->box.y0, ctrl->box.x1 + 1, ctrl->box.y1 + 1);
228 }
230 if (!ctrl->defined) return;
232 x = (gint) ((affine[4] > 0) ? (affine[4] + 0.5) : (affine[4] - 0.5)) - ctrl->span;
233 y = (gint) ((affine[5] > 0) ? (affine[5] + 0.5) : (affine[5] - 0.5)) - ctrl->span;
235 switch (ctrl->anchor) {
236 case GTK_ANCHOR_N:
237 case GTK_ANCHOR_CENTER:
238 case GTK_ANCHOR_S:
239 break;
241 case GTK_ANCHOR_NW:
242 case GTK_ANCHOR_W:
243 case GTK_ANCHOR_SW:
244 x += ctrl->span;
245 break;
247 case GTK_ANCHOR_NE:
248 case GTK_ANCHOR_E:
249 case GTK_ANCHOR_SE:
250 x -= (ctrl->span + 1);
251 break;
252 }
254 switch (ctrl->anchor) {
255 case GTK_ANCHOR_W:
256 case GTK_ANCHOR_CENTER:
257 case GTK_ANCHOR_E:
258 break;
260 case GTK_ANCHOR_NW:
261 case GTK_ANCHOR_N:
262 case GTK_ANCHOR_NE:
263 y += ctrl->span;
264 break;
266 case GTK_ANCHOR_SW:
267 case GTK_ANCHOR_S:
268 case GTK_ANCHOR_SE:
269 y -= (ctrl->span + 1);
270 break;
271 }
273 ctrl->box.x0 = x;
274 ctrl->box.y0 = y;
275 ctrl->box.x1 = ctrl->box.x0 + 2 * ctrl->span;
276 ctrl->box.y1 = ctrl->box.y0 + 2 * ctrl->span;
278 sp_canvas_update_bbox (item, ctrl->box.x0, ctrl->box.y0, ctrl->box.x1 + 1, ctrl->box.y1 + 1);
279 }
281 static double
282 sp_ctrl_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
283 {
284 SPCtrl *ctrl = SP_CTRL (item);
286 *actual_item = item;
288 double const x = p[Geom::X];
289 double const y = p[Geom::Y];
291 if ((x >= ctrl->box.x0) && (x <= ctrl->box.x1) && (y >= ctrl->box.y0) && (y <= ctrl->box.y1)) return 0.0;
293 return 1e18;
294 }
296 static void
297 sp_ctrl_build_cache (SPCtrl *ctrl)
298 {
299 guchar * p, *q;
300 gint size, x, y, z, s, a, side, c;
301 guint8 fr, fg, fb, fa, sr, sg, sb, sa;
303 if (ctrl->filled) {
304 fr = (ctrl->fill_color >> 24) & 0xff;
305 fg = (ctrl->fill_color >> 16) & 0xff;
306 fb = (ctrl->fill_color >> 8) & 0xff;
307 fa = (ctrl->fill_color) & 0xff;
308 } else {
309 fr = 0x00; fg = 0x00; fb = 0x00; fa = 0x00;
310 }
311 if (ctrl->stroked) {
312 sr = (ctrl->stroke_color >> 24) & 0xff;
313 sg = (ctrl->stroke_color >> 16) & 0xff;
314 sb = (ctrl->stroke_color >> 8) & 0xff;
315 sa = (ctrl->stroke_color) & 0xff;
316 } else {
317 sr = fr; sg = fg; sb = fb; sa = fa;
318 }
321 side = (ctrl->span * 2 +1);
322 c = ctrl->span ;
323 size = (side) * (side) * 4;
324 if (side < 2) return;
326 if (ctrl->cache)
327 g_free (ctrl->cache);
328 ctrl->cache = (guchar*)g_malloc (size);
330 switch (ctrl->shape) {
331 case SP_CTRL_SHAPE_SQUARE:
332 p = ctrl->cache;
333 for (x=0; x < side; x++) {
334 *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa;
335 }
336 for (y = 2; y < side; y++) {
337 *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa;
338 for (x=2; x < side; x++) {
339 *p++ = fr; *p++ = fg; *p++ = fb; *p++ = fa;
340 }
341 *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa;
342 }
343 for (x=0; x < side; x++) {
344 *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa;
345 }
346 ctrl->build = TRUE;
347 break;
349 case SP_CTRL_SHAPE_DIAMOND:
350 p = ctrl->cache;
351 for (y = 0; y < side; y++) {
352 z = abs (c - y);
353 for (x = 0; x < z; x++) {
354 *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
355 }
356 *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++;
357 for (; x < side - z -1; x++) {
358 *p++ = fr; *p++ = fg; *p++ = fb; *p++ = fa;
359 }
360 if (z != c) {
361 *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++;
362 }
363 for (; x < side; x++) {
364 *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
365 }
366 }
367 break;
369 case SP_CTRL_SHAPE_CIRCLE:
370 p = ctrl->cache;
371 q = p + size -1;
372 s = -1;
373 for (y = 0; y <= c ; y++) {
374 a = abs (c - y);
375 z = (gint)(0.0 + sqrt ((c+.4)*(c+.4) - a*a));
376 x = 0;
377 while (x < c-z) {
378 *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
379 *q-- = 0x00; *q-- = 0x00; *q-- = 0x00; *q-- = 0x00;
380 x++;
381 }
382 do {
383 *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa;
384 *q-- = sa; *q-- = sb; *q-- = sg; *q-- = sr;
385 x++;
386 } while (x < c-s);
387 while (x < MIN(c+s+1, c+z)) {
388 *p++ = fr; *p++ = fg; *p++ = fb; *p++ = fa;
389 *q-- = fa; *q-- = fb; *q-- = fg; *q-- = fr;
390 x++;
391 }
392 do {
393 *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa;
394 *q-- = sa; *q-- = sb; *q-- = sg; *q-- = sr;
395 x++;
396 } while (x <= c+z);
397 while (x < side) {
398 *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
399 *q-- = 0x00; *q-- = 0x00; *q-- = 0x00; *q-- = 0x00;
400 x++;
401 }
402 s = z;
403 }
404 ctrl->build = TRUE;
405 break;
407 case SP_CTRL_SHAPE_CROSS:
408 p = ctrl->cache;
409 for (y = 0; y < side; y++) {
410 z = abs (c - y);
411 for (x = 0; x < c-z; x++) {
412 *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
413 }
414 *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++;
415 for (; x < c + z; x++) {
416 *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
417 }
418 if (z != 0) {
419 *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++;
420 }
421 for (; x < side; x++) {
422 *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
423 }
424 }
425 ctrl->build = TRUE;
426 break;
428 case SP_CTRL_SHAPE_BITMAP:
429 if (ctrl->pixbuf) {
430 unsigned char *px;
431 unsigned int rs;
432 px = gdk_pixbuf_get_pixels (ctrl->pixbuf);
433 rs = gdk_pixbuf_get_rowstride (ctrl->pixbuf);
434 for (y = 0; y < side; y++){
435 unsigned char *s, *d;
436 s = px + y * rs;
437 d = ctrl->cache + 4 * side * y;
438 for (x = 0; x < side; x++) {
439 if (s[3] < 0x80) {
440 d[0] = 0x00;
441 d[1] = 0x00;
442 d[2] = 0x00;
443 d[3] = 0x00;
444 } else if (s[0] < 0x80) {
445 d[0] = sr;
446 d[1] = sg;
447 d[2] = sb;
448 d[3] = sa;
449 } else {
450 d[0] = fr;
451 d[1] = fg;
452 d[2] = fb;
453 d[3] = fa;
454 }
455 s += 4;
456 d += 4;
457 }
458 }
459 } else {
460 g_print ("control has no pixmap\n");
461 }
462 ctrl->build = TRUE;
463 break;
465 case SP_CTRL_SHAPE_IMAGE:
466 if (ctrl->pixbuf) {
467 guint r = gdk_pixbuf_get_rowstride (ctrl->pixbuf);
468 guchar * pix;
469 q = gdk_pixbuf_get_pixels (ctrl->pixbuf);
470 p = ctrl->cache;
471 for (y = 0; y < side; y++){
472 pix = q + (y * r);
473 for (x = 0; x < side; x++) {
474 *p++ = *pix++;
475 *p++ = *pix++;
476 *p++ = *pix++;
477 *p++ = *pix++;
478 }
479 }
480 } else {
481 g_print ("control has no pixmap\n");
482 }
483 ctrl->build = TRUE;
484 break;
486 default:
487 break;
488 }
490 }
492 // composite background, foreground, alpha for xor mode
493 #define COMPOSE_X(b,f,a) ( FAST_DIVIDE<255>( ((guchar) b) * ((guchar) (0xff - a)) + ((guchar) ((b ^ ~f) + b/4 - (b>127? 63 : 0))) * ((guchar) a) ) )
494 // composite background, foreground, alpha for color mode
495 #define COMPOSE_N(b,f,a) ( FAST_DIVIDE<255>( ((guchar) b) * ((guchar) (0xff - a)) + ((guchar) f) * ((guchar) a) ) )
497 static void
498 sp_ctrl_render (SPCanvasItem *item, SPCanvasBuf *buf)
499 {
500 gint y0, y1, y, x0,x1,x;
501 guchar *p, *q, a;
503 SPCtrl *ctrl = SP_CTRL (item);
505 if (!ctrl->defined) return;
506 if ((!ctrl->filled) && (!ctrl->stroked)) return;
508 sp_canvas_prepare_buffer (buf);
510 // the control-image is rendered into ctrl->cache
511 if (!ctrl->build) {
512 sp_ctrl_build_cache (ctrl);
513 }
515 // then we render from ctrl->cache
516 y0 = MAX (ctrl->box.y0, buf->rect.y0);
517 y1 = MIN (ctrl->box.y1, buf->rect.y1 - 1);
518 x0 = MAX (ctrl->box.x0, buf->rect.x0);
519 x1 = MIN (ctrl->box.x1, buf->rect.x1 - 1);
521 bool colormode;
523 for (y = y0; y <= y1; y++) {
524 p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x0 - buf->rect.x0) * 4;
525 q = ctrl->cache + ((y - ctrl->box.y0) * (ctrl->span*2+1) + (x0 - ctrl->box.x0)) * 4;
526 for (x = x0; x <= x1; x++) {
527 a = *(q + 3);
528 // 00000000 is the only way to get invisible; all other colors with alpha 00 are treated as mode_color with alpha ff
529 colormode = false;
530 if (a == 0x00 && !(q[0] == 0x00 && q[1] == 0x00 && q[2] == 0x00)) {
531 a = 0xff;
532 colormode = true;
533 }
534 if (ctrl->mode == SP_CTRL_MODE_COLOR || colormode) {
535 p[0] = COMPOSE_N (p[0], q[0], a);
536 p[1] = COMPOSE_N (p[1], q[1], a);
537 p[2] = COMPOSE_N (p[2], q[2], a);
538 q += 4;
539 p += 4;
540 } else if (ctrl->mode == SP_CTRL_MODE_XOR) {
541 p[0] = COMPOSE_X (p[0], q[0], a);
542 p[1] = COMPOSE_X (p[1], q[1], a);
543 p[2] = COMPOSE_X (p[2], q[2], a);
544 q += 4;
545 p += 4;
546 }
547 }
548 }
549 ctrl->shown = TRUE;
550 }
552 void SPCtrl::moveto (Geom::Point const p) {
553 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (this), Geom::Matrix(Geom::Translate (p)));
554 _moved = true;
555 }
558 /*
559 Local Variables:
560 mode:c++
561 c-file-style:"stroustrup"
562 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
563 indent-tabs-mode:nil
564 fill-column:99
565 End:
566 */
567 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :