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