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