Code

do not redraw if moved to the same point as before
[inkscape.git] / src / display / sodipodi-ctrl.cpp
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);
118 static void
119 sp_ctrl_destroy (GtkObject *object)
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);
137 static void
138 sp_ctrl_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
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     }
213 static void
214 sp_ctrl_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags)
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);
283 static double
284 sp_ctrl_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item)
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;
298 static void
299 sp_ctrl_build_cache (SPCtrl *ctrl)
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     }
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)
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;
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;
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 :