Code

do not redraw until moved for the first time; this fixes the flicker at 0,0 whenever...
[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 "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 const GTypeInfo 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;
114 static void
115 sp_ctrl_destroy (GtkObject *object)
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);
128 static void
129 sp_ctrl_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
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;
144         case ARG_MODE:
145                 ctrl->mode = (SPCtrlModeType)(GTK_VALUE_INT (*arg));
146                 ctrl->build = FALSE;
147                 sp_canvas_item_request_update (item);
148                 break;
149         case ARG_ANCHOR:
150                 ctrl->anchor = (GtkAnchorType)(GTK_VALUE_INT (*arg));
151                 ctrl->build = FALSE;
152                 sp_canvas_item_request_update (item);
153                 break;
154         case ARG_SIZE:
155                 ctrl->span = (gint) ((GTK_VALUE_DOUBLE (*arg) - 1.0) / 2.0 + 0.5);
156                 ctrl->defined = (ctrl->span > 0);
157                 ctrl->build = FALSE;
158                 sp_canvas_item_request_update (item);
159                 break;
160         case ARG_FILLED:
161                 ctrl->filled = GTK_VALUE_BOOL (*arg);
162                 ctrl->build = FALSE;
163                 sp_canvas_item_request_update (item);
164                 break;
165         case ARG_FILL_COLOR:
166                 ctrl->fill_color = GTK_VALUE_INT (*arg);
167                 ctrl->build = FALSE;
168                 sp_canvas_item_request_update (item);
169                 break;
170         case ARG_STROKED:
171                 ctrl->stroked = GTK_VALUE_BOOL (*arg);
172                 ctrl->build = FALSE;
173                 sp_canvas_item_request_update (item);
174                 break;
175         case ARG_STROKE_COLOR:
176                 ctrl->stroke_color = GTK_VALUE_INT (*arg);
177                 ctrl->build = FALSE;
178                 sp_canvas_item_request_update (item);
179                 break;
180         case ARG_PIXBUF:
181                 pixbuf  = (GdkPixbuf*)(GTK_VALUE_POINTER (*arg));
182                 if (gdk_pixbuf_get_has_alpha (pixbuf)) {
183                         ctrl->pixbuf = pixbuf;
184                 } else {
185                         ctrl->pixbuf = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
186                         gdk_pixbuf_unref (pixbuf);
187                 }
188                 ctrl->build = FALSE;
189                 break;
190         default:
191                 break;
192         }
195 static void
196 sp_ctrl_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
198         SPCtrl *ctrl;
199         gint x, y;
201         ctrl = SP_CTRL (item);
203         if (((SPCanvasItemClass *) parent_class)->update)
204                 (* ((SPCanvasItemClass *) parent_class)->update) (item, affine, flags);
206         sp_canvas_item_reset_bounds (item);
208         if (!ctrl->_moved) return;
210         if (ctrl->shown) {
211                 sp_canvas_request_redraw (item->canvas, ctrl->box.x0, ctrl->box.y0, ctrl->box.x1 + 1, ctrl->box.y1 + 1);
212         }
214         if (!ctrl->defined) return;
216         x = (gint) ((affine[4] > 0) ? (affine[4] + 0.5) : (affine[4] - 0.5)) - ctrl->span;
217         y = (gint) ((affine[5] > 0) ? (affine[5] + 0.5) : (affine[5] - 0.5)) - ctrl->span;
219         switch (ctrl->anchor) {
220         case GTK_ANCHOR_N:
221         case GTK_ANCHOR_CENTER:
222         case GTK_ANCHOR_S:
223                 break;
224         case GTK_ANCHOR_NW:
225         case GTK_ANCHOR_W:
226         case GTK_ANCHOR_SW:
227                 x += ctrl->span;
228                 break;
229         case GTK_ANCHOR_NE:
230         case GTK_ANCHOR_E:
231         case GTK_ANCHOR_SE:
232                 x -= (ctrl->span + 1);
233                 break;
234         }
236         switch (ctrl->anchor) {
237         case GTK_ANCHOR_W:
238         case GTK_ANCHOR_CENTER:
239         case GTK_ANCHOR_E:
240                 break;
241         case GTK_ANCHOR_NW:
242         case GTK_ANCHOR_N:
243         case GTK_ANCHOR_NE:
244                 y += ctrl->span;
245                 break;
246         case GTK_ANCHOR_SW:
247         case GTK_ANCHOR_S:
248         case GTK_ANCHOR_SE:
249                 y -= (ctrl->span + 1);
250                 break;
251         }
253         ctrl->box.x0 = x;
254         ctrl->box.y0 = y;
255         ctrl->box.x1 = ctrl->box.x0 + 2 * ctrl->span;
256         ctrl->box.y1 = ctrl->box.y0 + 2 * ctrl->span;
258         sp_canvas_update_bbox (item, ctrl->box.x0, ctrl->box.y0, ctrl->box.x1 + 1, ctrl->box.y1 + 1);
262 static double
263 sp_ctrl_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
265         SPCtrl *ctrl = SP_CTRL (item);
267         *actual_item = item;
268         
269         const double x = p[NR::X];
270         const double y = p[NR::Y];
271         
272         if ((x >= ctrl->box.x0) && (x <= ctrl->box.x1) && (y >= ctrl->box.y0) && (y <= ctrl->box.y1)) return 0.0;
274         return 1e18;
277 static void
278 sp_ctrl_build_cache (SPCtrl *ctrl)
280         guchar * p, *q;
281         gint size, x, y, z, s, a, side, c;
282         guint8 fr, fg, fb, fa, sr, sg, sb, sa;
283         
284         if (ctrl->filled) {
285                fr = (ctrl->fill_color >> 24) & 0xff;
286                fg = (ctrl->fill_color >> 16) & 0xff;
287                fb = (ctrl->fill_color >> 8) & 0xff;
288                fa = (ctrl->fill_color) & 0xff;
289         } else { fr = 0x00; fg = 0x00; fb = 0x00; fa = 0x00; }
290         if (ctrl->stroked) {
291                 sr = (ctrl->stroke_color >> 24) & 0xff;
292                 sg = (ctrl->stroke_color >> 16) & 0xff;
293                 sb = (ctrl->stroke_color >> 8) & 0xff;
294                 sa = (ctrl->stroke_color) & 0xff;
295         } else { sr = fr; sg = fg; sb = fb; sa = fa; }
298         side = (ctrl->span * 2 +1);
299         c = ctrl->span ;
300         g_free (ctrl->cache);
301         size = (side) * (side) * 4;
302         ctrl->cache = (guchar*)g_malloc (size);
303         if (side < 2) return;
304         
305         switch (ctrl->shape) {
306         case SP_CTRL_SHAPE_SQUARE: 
307                 p = ctrl->cache;
308                 for (x=0; x < side; x++) { *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; }
309                 for (y = 2; y < side; y++) {
310                         *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa;
311                         for (x=2; x < side; x++) { *p++ = fr; *p++ = fg; *p++ = fb; *p++ = fa; }
312                         *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa;
313                 }
314                 for (x=0; x < side; x++) { *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; }
315                 ctrl->build = TRUE;
316                 break;
317         case SP_CTRL_SHAPE_DIAMOND:
318                 p = ctrl->cache;
319                 for (y = 0; y < side; y++) {
320                         z = abs (c - y);
321                         for (x = 0; x < z; x++) {*p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;}
322                         *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++; 
323                         for (; x < side - z -1; x++) { *p++ = fr; *p++ = fg; *p++ = fb; *p++ = fa; }
324                         if (z != c) {*p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++;} 
325                         for (; x < side; x++) {*p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;}
326                 }
327                 break;
328         case SP_CTRL_SHAPE_CIRCLE:
329                 p = ctrl->cache;
330                 q = p + size -1;
331                 s = -1;
332                 for (y = 0; y <= c ; y++) {
333                         a = abs (c - y);
334                         z = (gint)(0.0 + sqrt ((c+.4)*(c+.4) - a*a));
335                         x = 0;
336                         while (x < c-z) {
337                                 *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
338                                 *q-- = 0x00; *q-- = 0x00; *q-- = 0x00; *q-- = 0x00; 
339                                 x++;
340                         }
341                         do {
342                                 *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa;   
343                                 *q-- = sa; *q-- = sb; *q-- = sg; *q-- = sr;
344                                 x++;
345                         } while (x < c-s);
346                         while (x < MIN(c+s+1, c+z)) {
347                                 *p++ = fr;   *p++ = fg;   *p++ = fb;   *p++ = fa;   
348                                 *q-- = fa;   *q-- = fb;   *q-- = fg;   *q-- = fr;   
349                                 x++;
350                         }
351                         do {
352                                 *p++ = sr;   *p++ = sg;   *p++ = sb;   *p++ = sa;   
353                                 *q-- = sa; *q-- = sb; *q-- = sg; *q-- = sr;
354                                 x++;
355                         } while (x <= c+z);
356                         while (x < side) {
357                                 *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; 
358                                 *q-- = 0x00; *q-- = 0x00; *q-- = 0x00; *q-- = 0x00; 
359                                 x++;
360                         }
361                         s = z;
362                 }
363                 ctrl->build = TRUE;
364                 break;
365         case SP_CTRL_SHAPE_CROSS:
366                 p = ctrl->cache;
367                 for (y = 0; y < side; y++) {
368                         z = abs (c - y);
369                         for (x = 0; x < c-z; x++) {*p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;}
370                         *p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++; 
371                         for (; x < c + z; x++) {*p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;}
372                         if (z != 0) {*p++ = sr; *p++ = sg; *p++ = sb; *p++ = sa; x++;} 
373                         for (; x < side; x++) {*p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;}
374                 }
375                 ctrl->build = TRUE;
376                 break;
377         case SP_CTRL_SHAPE_BITMAP:
378                 if (ctrl->pixbuf) {
379                         unsigned char *px;
380                         unsigned int rs;
381                         px = gdk_pixbuf_get_pixels (ctrl->pixbuf);
382                         rs = gdk_pixbuf_get_rowstride (ctrl->pixbuf);
383                         for (y = 0; y < side; y++){
384                                 unsigned char *s, *d;
385                                 s = px + y * rs;
386                                 d = ctrl->cache + 4 * side * y;
387                                 for (x = 0; x < side; x++) {
388                                         if (s[3] < 0x80) {
389                                                 d[0] = 0x00;
390                                                 d[1] = 0x00;
391                                                 d[2] = 0x00;
392                                                 d[3] = 0x00;
393                                         } else if (s[0] < 0x80) {
394                                                 d[0] = sr;
395                                                 d[1] = sg;
396                                                 d[2] = sb;
397                                                 d[3] = sa;
398                                         } else {
399                                                 d[0] = fr;
400                                                 d[1] = fg;
401                                                 d[2] = fb;
402                                                 d[3] = fa;
403                                         }
404                                         s += 4;
405                                         d += 4;
406                                 }
407                         }
408                 } else {
409                         g_print ("control has no pixmap\n");
410                 }
411                 ctrl->build = TRUE;
412                 break;
413         case SP_CTRL_SHAPE_IMAGE:
414                 if (ctrl->pixbuf) {
415                         guint r = gdk_pixbuf_get_rowstride (ctrl->pixbuf);
416                         guchar * pix;
417                         q = gdk_pixbuf_get_pixels (ctrl->pixbuf);
418                         p = ctrl->cache;
419                         for (y = 0; y < side; y++){
420                                 pix = q + (y * r);
421                                 for (x = 0; x < side; x++) {
422                                         *p++ = *pix++;
423                                         *p++ = *pix++;
424                                         *p++ = *pix++;
425                                         *p++ = *pix++;
426                                 }
427                         }
428                 } else { g_print ("control has no pixmap\n"); } 
429                 ctrl->build = TRUE;
430                 break;
431         default:
432                 break;
433         }
434         
437 // composite background, foreground, alpha for xor mode
438 #define COMPOSE_X(b,f,a) ( ( ((guchar) b) * ((guchar) (0xff - a)) + ((guchar) ((b ^ ~f) + b/4 - (b>127? 63 : 0))) * ((guchar) a) ) / 0xff )
439 // composite background, foreground, alpha for color mode
440 #define COMPOSE_N(b,f,a) ( ( ((guchar) b) * ((guchar) (0xff - a)) + ((guchar) f) * ((guchar) a) ) / 0xff )
442 static void
443 sp_ctrl_render (SPCanvasItem *item, SPCanvasBuf *buf)
445         gint y0, y1, y, x0,x1,x;
446         guchar * p, * q, a;
447         
448         SPCtrl *ctrl = SP_CTRL (item);
449         
450         if (!ctrl->defined) return;
451         if ((!ctrl->filled) && (!ctrl->stroked)) return;
452         
453         sp_canvas_prepare_buffer (buf);
454         
455         // the control-image is rendered into ctrl->cache
456         if (!ctrl->build) sp_ctrl_build_cache (ctrl);
457         
458         // then we render from ctrl->cache
459         y0 = MAX (ctrl->box.y0, buf->rect.y0);
460         y1 = MIN (ctrl->box.y1, buf->rect.y1 - 1);
461         x0 = MAX (ctrl->box.x0, buf->rect.x0);
462         x1 = MIN (ctrl->box.x1, buf->rect.x1 - 1);
464         bool colormode;
466         for (y = y0; y <= y1; y++) {
467                 p = buf->buf +    (y - buf->rect.y0) * buf->buf_rowstride + (x0 - buf->rect.x0) * 3;
468                 q = ctrl->cache + ((y - ctrl->box.y0) * (ctrl->span*2+1) + (x0 - ctrl->box.x0)) * 4;
469                 for (x = x0; x <= x1; x++) {
470                         a = *(q + 3);
471                         // 00000000 is the only way to get invisible; all other colors with alpha 00 are treated as mode_color with alpha ff
472                         colormode = false;
473                         if (a == 0x00 && !(q[0] == 0x00 && q[1] == 0x00 && q[2] == 0x00)) {
474                                 a = 0xff;
475                                 colormode = true;
476                         }
477                         if (ctrl->mode == SP_CTRL_MODE_COLOR || colormode) {
478                                 p[0] = COMPOSE_N (p[0], q[0], a);
479                                 p[1] = COMPOSE_N (p[1], q[1], a);
480                                 p[2] = COMPOSE_N (p[2], q[2], a);
481                                 q += 4;
482                                 p += 3;
483                         } else if (ctrl->mode == SP_CTRL_MODE_XOR) {
484                                 p[0] = COMPOSE_X (p[0], q[0], a);
485                                 p[1] = COMPOSE_X (p[1], q[1], a);
486                                 p[2] = COMPOSE_X (p[2], q[2], a);
487                                 q += 4;
488                                 p += 3;
489                         }
490                 }
491         }
492         ctrl->shown = TRUE;
495 void SPCtrl::moveto (NR::Point const p) {
496         sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (this), NR::Matrix(NR::translate (p)));
497   _moved = true;