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;
115 ctrl->_point = Geom::Point(0,0);
116 }
118 static void
119 sp_ctrl_destroy (GtkObject *object)
120 {
121 SPCtrl *ctrl;
123 g_return_if_fail (object != NULL);
124 g_return_if_fail (SP_IS_CTRL (object));
126 ctrl = SP_CTRL (object);
128 if (ctrl->cache) {
129 g_free(ctrl->cache);
130 ctrl->cache = NULL;
131 }
133 if (GTK_OBJECT_CLASS (parent_class)->destroy)
134 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
135 }
137 static void
138 sp_ctrl_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
139 {
140 SPCanvasItem *item;
141 SPCtrl *ctrl;
142 GdkPixbuf * pixbuf = NULL;
144 item = SP_CANVAS_ITEM (object);
145 ctrl = SP_CTRL (object);
147 switch (arg_id) {
148 case ARG_SHAPE:
149 ctrl->shape = (SPCtrlShapeType)(GTK_VALUE_INT (*arg));
150 ctrl->build = FALSE;
151 sp_canvas_item_request_update (item);
152 break;
154 case ARG_MODE:
155 ctrl->mode = (SPCtrlModeType)(GTK_VALUE_INT (*arg));
156 ctrl->build = FALSE;
157 sp_canvas_item_request_update (item);
158 break;
160 case ARG_ANCHOR:
161 ctrl->anchor = (GtkAnchorType)(GTK_VALUE_INT (*arg));
162 ctrl->build = FALSE;
163 sp_canvas_item_request_update (item);
164 break;
166 case ARG_SIZE:
167 ctrl->span = (gint) ((GTK_VALUE_DOUBLE (*arg) - 1.0) / 2.0 + 0.5);
168 ctrl->defined = (ctrl->span > 0);
169 ctrl->build = FALSE;
170 sp_canvas_item_request_update (item);
171 break;
173 case ARG_FILLED:
174 ctrl->filled = GTK_VALUE_BOOL (*arg);
175 ctrl->build = FALSE;
176 sp_canvas_item_request_update (item);
177 break;
179 case ARG_FILL_COLOR:
180 ctrl->fill_color = GTK_VALUE_INT (*arg);
181 ctrl->build = FALSE;
182 sp_canvas_item_request_update (item);
183 break;
185 case ARG_STROKED:
186 ctrl->stroked = GTK_VALUE_BOOL (*arg);
187 ctrl->build = FALSE;
188 sp_canvas_item_request_update (item);
189 break;
191 case ARG_STROKE_COLOR:
192 ctrl->stroke_color = GTK_VALUE_INT (*arg);
193 ctrl->build = FALSE;
194 sp_canvas_item_request_update (item);
195 break;
197 case ARG_PIXBUF:
198 pixbuf = (GdkPixbuf*)(GTK_VALUE_POINTER (*arg));
199 if (gdk_pixbuf_get_has_alpha (pixbuf)) {
200 ctrl->pixbuf = pixbuf;
201 } else {
202 ctrl->pixbuf = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
203 gdk_pixbuf_unref (pixbuf);
204 }
205 ctrl->build = FALSE;
206 break;
208 default:
209 break;
210 }
211 }
213 static void
214 sp_ctrl_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
215 {
216 SPCtrl *ctrl;
217 gint x, y;
219 ctrl = SP_CTRL (item);
221 if (((SPCanvasItemClass *) parent_class)->update)
222 (* ((SPCanvasItemClass *) parent_class)->update) (item, affine, flags);
224 sp_canvas_item_reset_bounds (item);
226 if (!ctrl->_moved) return;
228 if (ctrl->shown) {
229 sp_canvas_request_redraw (item->canvas, ctrl->box.x0, ctrl->box.y0, ctrl->box.x1 + 1, ctrl->box.y1 + 1);
230 }
232 if (!ctrl->defined) return;
234 x = (gint) ((affine[4] > 0) ? (affine[4] + 0.5) : (affine[4] - 0.5)) - ctrl->span;
235 y = (gint) ((affine[5] > 0) ? (affine[5] + 0.5) : (affine[5] - 0.5)) - ctrl->span;
237 switch (ctrl->anchor) {
238 case GTK_ANCHOR_N:
239 case GTK_ANCHOR_CENTER:
240 case GTK_ANCHOR_S:
241 break;
243 case GTK_ANCHOR_NW:
244 case GTK_ANCHOR_W:
245 case GTK_ANCHOR_SW:
246 x += ctrl->span;
247 break;
249 case GTK_ANCHOR_NE:
250 case GTK_ANCHOR_E:
251 case GTK_ANCHOR_SE:
252 x -= (ctrl->span + 1);
253 break;
254 }
256 switch (ctrl->anchor) {
257 case GTK_ANCHOR_W:
258 case GTK_ANCHOR_CENTER:
259 case GTK_ANCHOR_E:
260 break;
262 case GTK_ANCHOR_NW:
263 case GTK_ANCHOR_N:
264 case GTK_ANCHOR_NE:
265 y += ctrl->span;
266 break;
268 case GTK_ANCHOR_SW:
269 case GTK_ANCHOR_S:
270 case GTK_ANCHOR_SE:
271 y -= (ctrl->span + 1);
272 break;
273 }
275 ctrl->box.x0 = x;
276 ctrl->box.y0 = y;
277 ctrl->box.x1 = ctrl->box.x0 + 2 * ctrl->span;
278 ctrl->box.y1 = ctrl->box.y0 + 2 * ctrl->span;
280 sp_canvas_update_bbox (item, ctrl->box.x0, ctrl->box.y0, ctrl->box.x1 + 1, ctrl->box.y1 + 1);
281 }
283 static double
284 sp_ctrl_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
285 {
286 SPCtrl *ctrl = SP_CTRL (item);
288 *actual_item = item;
290 double const x = p[Geom::X];
291 double const y = p[Geom::Y];
293 if ((x >= ctrl->box.x0) && (x <= ctrl->box.x1) && (y >= ctrl->box.y0) && (y <= ctrl->box.y1)) return 0.0;
295 return 1e18;
296 }
298 static void
299 sp_ctrl_build_cache (SPCtrl *ctrl)
300 {
301 guchar * p, *q;
302 gint size, x, y, z, s, a, side, c;
303 guint8 fr, fg, fb, fa, sr, sg, sb, sa;
305 if (ctrl->filled) {
306 fr = (ctrl->fill_color >> 24) & 0xff;
307 fg = (ctrl->fill_color >> 16) & 0xff;
308 fb = (ctrl->fill_color >> 8) & 0xff;
309 fa = (ctrl->fill_color) & 0xff;
310 } else {
311 fr = 0x00; fg = 0x00; fb = 0x00; fa = 0x00;
312 }
313 if (ctrl->stroked) {
314 sr = (ctrl->stroke_color >> 24) & 0xff;
315 sg = (ctrl->stroke_color >> 16) & 0xff;
316 sb = (ctrl->stroke_color >> 8) & 0xff;
317 sa = (ctrl->stroke_color) & 0xff;
318 } else {
319 sr = fr; sg = fg; sb = fb; sa = fa;
320 }
323 side = (ctrl->span * 2 +1);
324 c = ctrl->span ;
325 size = (side) * (side) * 4;
326 if (side < 2) return;
328 if (ctrl->cache)
329 g_free (ctrl->cache);
330 ctrl->cache = (guchar*)g_malloc (size);
332 switch (ctrl->shape) {
333 case SP_CTRL_SHAPE_SQUARE:
334 p = ctrl->cache;
335 for (x=0; x < side; x++) {
336 *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa;
337 }
338 for (y = 2; y < side; y++) {
339 *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa;
340 for (x=2; x < side; x++) {
341 *p++ = fr; *p++ = fg; *p++ = fb; *p++ = fa;
342 }
343 *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa;
344 }
345 for (x=0; x < side; x++) {
346 *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa;
347 }
348 ctrl->build = TRUE;
349 break;
351 case SP_CTRL_SHAPE_DIAMOND:
352 p = ctrl->cache;
353 for (y = 0; y < side; y++) {
354 z = abs (c - y);
355 for (x = 0; x < z; x++) {
356 *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
357 }
358 *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++;
359 for (; x < side - z -1; x++) {
360 *p++ = fr; *p++ = fg; *p++ = fb; *p++ = fa;
361 }
362 if (z != c) {
363 *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++;
364 }
365 for (; x < side; x++) {
366 *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
367 }
368 }
369 break;
371 case SP_CTRL_SHAPE_CIRCLE:
372 p = ctrl->cache;
373 q = p + size -1;
374 s = -1;
375 for (y = 0; y <= c ; y++) {
376 a = abs (c - y);
377 z = (gint)(0.0 + sqrt ((c+.4)*(c+.4) - a*a));
378 x = 0;
379 while (x < c-z) {
380 *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
381 *q-- = 0x00; *q-- = 0x00; *q-- = 0x00; *q-- = 0x00;
382 x++;
383 }
384 do {
385 *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa;
386 *q-- = sa; *q-- = sb; *q-- = sg; *q-- = sr;
387 x++;
388 } while (x < c-s);
389 while (x < MIN(c+s+1, c+z)) {
390 *p++ = fr; *p++ = fg; *p++ = fb; *p++ = fa;
391 *q-- = fa; *q-- = fb; *q-- = fg; *q-- = fr;
392 x++;
393 }
394 do {
395 *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa;
396 *q-- = sa; *q-- = sb; *q-- = sg; *q-- = sr;
397 x++;
398 } while (x <= c+z);
399 while (x < side) {
400 *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
401 *q-- = 0x00; *q-- = 0x00; *q-- = 0x00; *q-- = 0x00;
402 x++;
403 }
404 s = z;
405 }
406 ctrl->build = TRUE;
407 break;
409 case SP_CTRL_SHAPE_CROSS:
410 p = ctrl->cache;
411 for (y = 0; y < side; y++) {
412 z = abs (c - y);
413 for (x = 0; x < c-z; x++) {
414 *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
415 }
416 *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++;
417 for (; x < c + z; x++) {
418 *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
419 }
420 if (z != 0) {
421 *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++;
422 }
423 for (; x < side; x++) {
424 *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
425 }
426 }
427 ctrl->build = TRUE;
428 break;
430 case SP_CTRL_SHAPE_BITMAP:
431 if (ctrl->pixbuf) {
432 unsigned char *px;
433 unsigned int rs;
434 px = gdk_pixbuf_get_pixels (ctrl->pixbuf);
435 rs = gdk_pixbuf_get_rowstride (ctrl->pixbuf);
436 for (y = 0; y < side; y++){
437 unsigned char *s, *d;
438 s = px + y * rs;
439 d = ctrl->cache + 4 * side * y;
440 for (x = 0; x < side; x++) {
441 if (s[3] < 0x80) {
442 d[0] = 0x00;
443 d[1] = 0x00;
444 d[2] = 0x00;
445 d[3] = 0x00;
446 } else if (s[0] < 0x80) {
447 d[0] = sr;
448 d[1] = sg;
449 d[2] = sb;
450 d[3] = sa;
451 } else {
452 d[0] = fr;
453 d[1] = fg;
454 d[2] = fb;
455 d[3] = fa;
456 }
457 s += 4;
458 d += 4;
459 }
460 }
461 } else {
462 g_print ("control has no pixmap\n");
463 }
464 ctrl->build = TRUE;
465 break;
467 case SP_CTRL_SHAPE_IMAGE:
468 if (ctrl->pixbuf) {
469 guint r = gdk_pixbuf_get_rowstride (ctrl->pixbuf);
470 guchar * pix;
471 q = gdk_pixbuf_get_pixels (ctrl->pixbuf);
472 p = ctrl->cache;
473 for (y = 0; y < side; y++){
474 pix = q + (y * r);
475 for (x = 0; x < side; x++) {
476 *p++ = *pix++;
477 *p++ = *pix++;
478 *p++ = *pix++;
479 *p++ = *pix++;
480 }
481 }
482 } else {
483 g_print ("control has no pixmap\n");
484 }
485 ctrl->build = TRUE;
486 break;
488 default:
489 break;
490 }
492 }
494 // composite background, foreground, alpha for xor mode
495 #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) ) )
496 // composite background, foreground, alpha for color mode
497 #define COMPOSE_N(b,f,a) ( FAST_DIVIDE<255>( ((guchar) b) * ((guchar) (0xff - a)) + ((guchar) f) * ((guchar) a) ) )
499 static void
500 sp_ctrl_render (SPCanvasItem *item, SPCanvasBuf *buf)
501 {
502 gint y0, y1, y, x0,x1,x;
503 guchar *p, *q, a;
505 SPCtrl *ctrl = SP_CTRL (item);
507 if (!ctrl->defined) return;
508 if ((!ctrl->filled) && (!ctrl->stroked)) return;
510 sp_canvas_prepare_buffer (buf);
512 // the control-image is rendered into ctrl->cache
513 if (!ctrl->build) {
514 sp_ctrl_build_cache (ctrl);
515 }
517 // then we render from ctrl->cache
518 y0 = MAX (ctrl->box.y0, buf->rect.y0);
519 y1 = MIN (ctrl->box.y1, buf->rect.y1 - 1);
520 x0 = MAX (ctrl->box.x0, buf->rect.x0);
521 x1 = MIN (ctrl->box.x1, buf->rect.x1 - 1);
523 bool colormode;
525 for (y = y0; y <= y1; y++) {
526 p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x0 - buf->rect.x0) * 4;
527 q = ctrl->cache + ((y - ctrl->box.y0) * (ctrl->span*2+1) + (x0 - ctrl->box.x0)) * 4;
528 for (x = x0; x <= x1; x++) {
529 a = *(q + 3);
530 // 00000000 is the only way to get invisible; all other colors with alpha 00 are treated as mode_color with alpha ff
531 colormode = false;
532 if (a == 0x00 && !(q[0] == 0x00 && q[1] == 0x00 && q[2] == 0x00)) {
533 a = 0xff;
534 colormode = true;
535 }
536 if (ctrl->mode == SP_CTRL_MODE_COLOR || colormode) {
537 p[0] = COMPOSE_N (p[0], q[0], a);
538 p[1] = COMPOSE_N (p[1], q[1], a);
539 p[2] = COMPOSE_N (p[2], q[2], a);
540 q += 4;
541 p += 4;
542 } else if (ctrl->mode == SP_CTRL_MODE_XOR) {
543 p[0] = COMPOSE_X (p[0], q[0], a);
544 p[1] = COMPOSE_X (p[1], q[1], a);
545 p[2] = COMPOSE_X (p[2], q[2], a);
546 q += 4;
547 p += 4;
548 }
549 }
550 }
551 ctrl->shown = TRUE;
552 }
554 void SPCtrl::moveto (Geom::Point const p) {
555 if (p != _point) {
556 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (this), Geom::Matrix(Geom::Translate (p)));
557 _moved = true;
558 }
559 _point = p;
560 }
563 /*
564 Local Variables:
565 mode:c++
566 c-file-style:"stroustrup"
567 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
568 indent-tabs-mode:nil
569 fill-column:99
570 End:
571 */
572 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :