Code

add document to action events
[inkscape.git] / src / display / canvas-arena.cpp
1 #define __SP_CANVAS_ARENA_C__
3 /*
4  * RGBA display list system for inkscape
5  *
6  * Author:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *
9  * Copyright (C) 2001-2002 Lauris Kaplinski
10  * Copyright (C) 2001 Ximian, Inc.
11  *
12  * Released under GNU GPL, read the file 'COPYING' for more information
13  */
15 #include <libnr/nr-blit.h>
16 #include <gtk/gtksignal.h>
18 #include <display/display-forward.h>
19 #include <display/sp-canvas-util.h>
20 #include <helper/sp-marshal.h>
21 #include <display/nr-arena.h>
22 #include <display/nr-arena-group.h>
23 #include <display/canvas-arena.h>
24 #include <display/inkscape-cairo.h>
26 enum {
27         ARENA_EVENT,
28         LAST_SIGNAL
29 };
31 static void sp_canvas_arena_class_init(SPCanvasArenaClass *klass);
32 static void sp_canvas_arena_init(SPCanvasArena *group);
33 static void sp_canvas_arena_destroy(GtkObject *object);
35 static void sp_canvas_arena_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
36 static void sp_canvas_arena_render (SPCanvasItem *item, SPCanvasBuf *buf);
37 static double sp_canvas_arena_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
38 static gint sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event);
40 static gint sp_canvas_arena_send_event (SPCanvasArena *arena, GdkEvent *event);
42 static void sp_canvas_arena_request_update (NRArena *arena, NRArenaItem *item, void *data);
43 static void sp_canvas_arena_request_render (NRArena *arena, NRRectL *area, void *data);
45 NRArenaEventVector carenaev = {
46         {NULL},
47         sp_canvas_arena_request_update,
48         sp_canvas_arena_request_render
49 };
51 static SPCanvasItemClass *parent_class;
52 static guint signals[LAST_SIGNAL] = {0};
54 GtkType
55 sp_canvas_arena_get_type (void)
56 {
57         static GtkType type = 0;
58         if (!type) {
59                 GtkTypeInfo info = {
60                         "SPCanvasArena",
61                         sizeof (SPCanvasArena),
62                         sizeof (SPCanvasArenaClass),
63                         (GtkClassInitFunc) sp_canvas_arena_class_init,
64                         (GtkObjectInitFunc) sp_canvas_arena_init,
65                         NULL, NULL, NULL
66                 };
67                 type = gtk_type_unique (SP_TYPE_CANVAS_ITEM, &info);
68         }
69         return type;
70 }
72 static void
73 sp_canvas_arena_class_init (SPCanvasArenaClass *klass)
74 {
75         GtkObjectClass *object_class;
76         SPCanvasItemClass *item_class;
78         object_class = (GtkObjectClass *) klass;
79         item_class = (SPCanvasItemClass *) klass;
81         parent_class = (SPCanvasItemClass*)gtk_type_class (SP_TYPE_CANVAS_ITEM);
83         signals[ARENA_EVENT] = gtk_signal_new ("arena_event",
84                                                GTK_RUN_LAST,
85                                                GTK_CLASS_TYPE(object_class),
86                                                GTK_SIGNAL_OFFSET (SPCanvasArenaClass, arena_event),
87                                                sp_marshal_INT__POINTER_POINTER,
88                                                GTK_TYPE_INT, 2, GTK_TYPE_POINTER, GTK_TYPE_POINTER);
90         object_class->destroy = sp_canvas_arena_destroy;
92         item_class->update = sp_canvas_arena_update;
93         item_class->render = sp_canvas_arena_render;
94         item_class->point = sp_canvas_arena_point;
95         item_class->event = sp_canvas_arena_event;
96 }
98 static void
99 sp_canvas_arena_init (SPCanvasArena *arena)
101         arena->sticky = FALSE;
103         arena->arena = NRArena::create();
104         arena->arena->canvasarena = arena;
105         arena->root = NRArenaGroup::create(arena->arena);
106         nr_arena_group_set_transparent (NR_ARENA_GROUP (arena->root), TRUE);
108         arena->active = NULL;
110         nr_active_object_add_listener ((NRActiveObject *) arena->arena, (NRObjectEventVector *) &carenaev, sizeof (carenaev), arena);
113 static void
114 sp_canvas_arena_destroy (GtkObject *object)
116         SPCanvasArena *arena = SP_CANVAS_ARENA (object);
118         if (arena->active) {
119                 nr_object_unref ((NRObject *) arena->active);
120                 arena->active = NULL;
121         }
123         if (arena->root) {
124                 nr_arena_item_unref (arena->root);
125                 arena->root = NULL;
126         }
128         if (arena->arena) {
129                 nr_active_object_remove_listener_by_data ((NRActiveObject *) arena->arena, arena);
131                 nr_object_unref ((NRObject *) arena->arena);
132                 arena->arena = NULL;
133         }
135         if (GTK_OBJECT_CLASS (parent_class)->destroy)
136                 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
139 static void
140 sp_canvas_arena_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
142         SPCanvasArena *arena = SP_CANVAS_ARENA (item);
144         if (((SPCanvasItemClass *) parent_class)->update)
145                 (* ((SPCanvasItemClass *) parent_class)->update) (item, affine, flags);
147         arena->gc.transform = affine;
149         guint reset;
150         reset = (flags & SP_CANVAS_UPDATE_AFFINE)? NR_ARENA_ITEM_STATE_ALL : NR_ARENA_ITEM_STATE_NONE;
152         nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_ALL, reset);
154         item->x1 = arena->root->bbox.x0 - 1;
155         item->y1 = arena->root->bbox.y0 - 1;
156         item->x2 = arena->root->bbox.x1 + 1;
157         item->y2 = arena->root->bbox.y1 + 1;
159         if (arena->cursor) {
160                 /* Mess with enter/leave notifiers */
161                 NRArenaItem *new_arena = nr_arena_item_invoke_pick (arena->root, arena->c, arena->arena->delta, arena->sticky);
162                 if (new_arena != arena->active) {
163                         GdkEventCrossing ec;
164                         ec.window = GTK_WIDGET (item->canvas)->window;
165                         ec.send_event = TRUE;
166                         ec.subwindow = ec.window;
167                         ec.time = GDK_CURRENT_TIME;
168                         ec.x = arena->c[NR::X];
169                         ec.y = arena->c[NR::Y];
170                         /* fixme: */
171                         if (arena->active) {
172                                 ec.type = GDK_LEAVE_NOTIFY;
173                                 sp_canvas_arena_send_event (arena, (GdkEvent *) &ec);
174                         }
175                         /* fixme: This is not optimal - better track ::destroy (Lauris) */
176                         if (arena->active) nr_object_unref ((NRObject *) arena->active);
177                         arena->active = new_arena;
178                         if (arena->active) nr_object_ref ((NRObject *) arena->active);
179                         if (arena->active) {
180                                 ec.type = GDK_ENTER_NOTIFY;
181                                 sp_canvas_arena_send_event (arena, (GdkEvent *) &ec);
182                         }
183                 }
184         }
187 static void
188 sp_canvas_arena_render (SPCanvasItem *item, SPCanvasBuf *buf)
190         gint bw, bh, sw, sh;
191         gint x, y;
193         SPCanvasArena *arena = SP_CANVAS_ARENA (item);
195         nr_arena_item_invoke_update (arena->root, NULL, &arena->gc,
196                                      NR_ARENA_ITEM_STATE_BBOX | NR_ARENA_ITEM_STATE_RENDER,
197                                      NR_ARENA_ITEM_STATE_NONE);
199         sp_canvas_prepare_buffer(buf);
200   
201         bw = buf->rect.x1 - buf->rect.x0;
202         bh = buf->rect.y1 - buf->rect.y0;
203         if ((bw < 1) || (bh < 1)) return;
205         // FIXME: currently this function is a huge waste. It receives a buffer but creates a new one and loops 
206         // within the large one, doing arena painting in several blocks. This just makes no sense because the 
207         // buf that we are given is already only a strip of the screen, created by one iteration of a loop in
208         // sp_canvas_paint_rect_internal. With the current numbers, this function's buffer is always 1/4 
209         // smaller than the one we get, because they both are the same number of bytes but
210         // buf uses 3 bytes per pixel (24bpp, packed) while the pixblock created here uses 4 bytes (32bpp).
211         // Eventually I want to switch buf to using 4 bytes (see comment in canvas.cpp) and then remove 
212         // from here the sw/sh calculation, the loop, and creating the intermediate buffer, allowing arena 
213         // just render into buf in one go.  
215         if (arena->arena->rendermode != RENDERMODE_OUTLINE) { // use 256K as a compromise to not slow down gradients
216                 /* 256K is the cached buffer and we need 4 channels */
217                 if (bw * bh < 65536) { // 256K/4
218                         /* We can go with single buffer */
219                         sw = bw;
220                         sh = bh;
221                 } else if (bw <= 4096) {
222                         /* Go with row buffer */
223                         sw = bw;
224                         sh = 65536 / bw;
225                 } else if (bh <= 4096) {
226                         /* Go with column buffer */
227                         sw = 65536 / bh;
228                         sh = bh;
229                 } else {
230                         sw = 256;
231                         sh = 256;
232                 }
233         } else { // paths only, so 1M works faster
234                 /* 1M is the cached buffer and we need 4 channels */
235                 if (bw * bh < 262144) { // 1M/4
236                         /* We can go with single buffer */
237                         sw = bw;
238                         sh = bh;
239                 } else if (bw <= 8192) {
240                         /* Go with row buffer */
241                         sw = bw;
242                         sh = 262144 / bw;
243                 } else if (bh <= 8192) {
244                         /* Go with column buffer */
245                         sw = 262144 / bh;
246                         sh = bh;
247                 } else {
248                         sw = 512;
249                         sh = 512;
250                 }
251         }
253 /*
254 This define chooses between two modes: When on, arena renders into a temporary 
255 32bpp buffer, and the result is then squished into the SPCanvasBuf. When off, arena 
256 renders directly to SPCanvasBuf. However currently this gives no speed advantage, 
257 perhaps because the lack of squishing is offset by the need for arena items to render 
258 to the inconvenient (and probably slower) 24bpp buffer. When SPCanvasBuf is 
259 switched to 32bpp and cairo drawing, however, this define should be removed to 
260 streamline rendering. 
261 */
262 #define STRICT_RGBA
264         for (y = buf->rect.y0; y < buf->rect.y1; y += sh) {
265                 for (x = buf->rect.x0; x < buf->rect.x1; x += sw) {
266                         NRRectL area;
267 #ifdef STRICT_RGBA
268                         NRPixBlock pb;
269 #endif
270                         NRPixBlock cb;
272                         area.x0 = x;
273                         area.y0 = y;
274                         area.x1 = MIN (x + sw, buf->rect.x1);
275                         area.y1 = MIN (y + sh, buf->rect.y1);
277 #ifdef STRICT_RGBA
278                         nr_pixblock_setup_fast (&pb, NR_PIXBLOCK_MODE_R8G8B8A8P, area.x0, area.y0, area.x1, area.y1, TRUE);
279 #endif
281 // CAIRO FIXME: switch this to R8G8B8A8P and 4 * ...
282                         nr_pixblock_setup_extern (&cb, NR_PIXBLOCK_MODE_R8G8B8, area.x0, area.y0, area.x1, area.y1,
283                                                   buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + 3 * (x - buf->rect.x0),
284                                                   buf->buf_rowstride,
285                                                   FALSE, FALSE);
287 #ifdef STRICT_RGBA
288                         pb.visible_area = buf->visible_rect;
290                         if (pb.data.px != NULL) {
291             cairo_t *ct = nr_create_cairo_context (&area, &pb);
293                                 nr_arena_item_invoke_render (ct, arena->root, &area, &pb, 0);
295                                 if (pb.empty == FALSE) {
297                                         if (arena->arena->rendermode == RENDERMODE_OUTLINE) {
298                                                 // currently we only use cairo in outline mode
300                                                 // ENDIANNESS FIX
301                                                 // Inkscape and GTK use fixed byte order in their buffers: r, g, b, a.
302                                                 // Cairo reads/writes buffer values as in32s and therefore depends on the hardware byte order 
303                                                 // (little-endian vs big-endian). 
304                                                 // Until we move ALL of inkscape rendering and screen display to cairo, 
305                                                 // we must reverse the order for big-endian architectures (e.g. PowerPC).
306                                                 if (G_BYTE_ORDER == G_BIG_ENDIAN) {
307                                                         unsigned char *start = NR_PIXBLOCK_PX(&pb);
308                                                         unsigned char *end = start + pb.rs * (pb.area.y1 - pb.area.y0);
309                                                         for (unsigned char *i = start; i < end; i += 4) {
310                                                                 unsigned char tmp0 = i[0];
311                                                                 unsigned char tmp1 = i[1];
312                                                                 i[0] = i[3];
313                                                                 i[1] = i[2];
314                                                                 i[2] = tmp1;
315                                                                 i[3] = tmp0;
316                                                         }
317                                                 }
318                                         }
320                                    // this does the 32->24 squishing, using an assembler routine:
321                               nr_blit_pixblock_pixblock (&cb, &pb);
322                                 }
324             cairo_surface_t *cst = cairo_get_target(ct);
325             cairo_destroy (ct);
326             cairo_surface_finish (cst);
327             cairo_surface_destroy (cst);
328                         }
330                         nr_pixblock_release (&pb);
331 #else
332                         cb.visible_area = buf->visible_rect; 
333                         nr_arena_item_invoke_render (NULL, arena->root, &area, &cb, 0);
334 #endif
336                         nr_pixblock_release (&cb);
337                 }
338         }
341 static double
342 sp_canvas_arena_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
344         SPCanvasArena *arena = SP_CANVAS_ARENA (item);
346         nr_arena_item_invoke_update (arena->root, NULL, &arena->gc,
347                                      NR_ARENA_ITEM_STATE_BBOX | NR_ARENA_ITEM_STATE_PICK,
348                                      NR_ARENA_ITEM_STATE_NONE);
350         NRArenaItem *picked = nr_arena_item_invoke_pick (arena->root, p, arena->arena->delta, arena->sticky);
352         arena->picked = picked;
354         if (picked) {
355                 *actual_item = item;
356                 return 0.0;
357         }
359         return 1e18;
362 static gint
363 sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event)
365         NRArenaItem *new_arena;
366         /* fixme: This sucks, we have to handle enter/leave notifiers */
368         SPCanvasArena *arena = SP_CANVAS_ARENA (item);
370         gint ret = FALSE;
372         switch (event->type) {
373         case GDK_ENTER_NOTIFY:
374                 if (!arena->cursor) {
375                         if (arena->active) {
376                                 //g_warning ("Cursor entered to arena with already active item");
377                                 nr_object_unref ((NRObject *) arena->active);
378                         }
379                         arena->cursor = TRUE;
381                         /* TODO ... event -> arena transform? */
382                         arena->c = NR::Point(event->crossing.x, event->crossing.y);
384                         /* fixme: Not sure abut this, but seems the right thing (Lauris) */
385                         nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_PICK, NR_ARENA_ITEM_STATE_NONE);
386                         arena->active = nr_arena_item_invoke_pick (arena->root, arena->c, arena->arena->delta, arena->sticky);
387                         if (arena->active) nr_object_ref ((NRObject *) arena->active);
388                         ret = sp_canvas_arena_send_event (arena, event);
389                 }
390                 break;
391         case GDK_LEAVE_NOTIFY:
392                 if (arena->cursor) {
393                         ret = sp_canvas_arena_send_event (arena, event);
394                         if (arena->active) nr_object_unref ((NRObject *) arena->active);
395                         arena->active = NULL;
396                         arena->cursor = FALSE;
397                 }
398                 break;
399         case GDK_MOTION_NOTIFY:
400                 /* TODO ... event -> arena transform? */
401                 arena->c = NR::Point(event->motion.x, event->motion.y);
403                 /* fixme: Not sure abut this, but seems the right thing (Lauris) */
404                 nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_PICK, NR_ARENA_ITEM_STATE_NONE);
405                 new_arena = nr_arena_item_invoke_pick (arena->root, arena->c, arena->arena->delta, arena->sticky);
406                 if (new_arena != arena->active) {
407                         GdkEventCrossing ec;
408                         ec.window = event->motion.window;
409                         ec.send_event = event->motion.send_event;
410                         ec.subwindow = event->motion.window;
411                         ec.time = event->motion.time;
412                         ec.x = event->motion.x;
413                         ec.y = event->motion.y;
414                         /* fixme: */
415                         if (arena->active) {
416                                 ec.type = GDK_LEAVE_NOTIFY;
417                                 ret = sp_canvas_arena_send_event (arena, (GdkEvent *) &ec);
418                         }
419                         if (arena->active) nr_object_unref ((NRObject *) arena->active);
420                         arena->active = new_arena;
421                         if (arena->active) nr_object_ref ((NRObject *) arena->active);
422                         if (arena->active) {
423                                 ec.type = GDK_ENTER_NOTIFY;
424                                 ret = sp_canvas_arena_send_event (arena, (GdkEvent *) &ec);
425                         }
426                 }
427                 ret = sp_canvas_arena_send_event (arena, event);
428                 break;
429         default:
430                 /* Just send event */
431                 ret = sp_canvas_arena_send_event (arena, event);
432                 break;
433         }
435         return ret;
438 static gint
439 sp_canvas_arena_send_event (SPCanvasArena *arena, GdkEvent *event)
441         gint ret = FALSE;
443         /* Send event to arena */
444         gtk_signal_emit (GTK_OBJECT (arena), signals[ARENA_EVENT], arena->active, event, &ret);
446         return ret;
449 static void
450 sp_canvas_arena_request_update (NRArena *arena, NRArenaItem *item, void *data)
452         sp_canvas_item_request_update (SP_CANVAS_ITEM (data));
455 static void
456 sp_canvas_arena_request_render (NRArena *arena, NRRectL *area, void *data)
458         sp_canvas_request_redraw (SP_CANVAS_ITEM (data)->canvas, area->x0, area->y0, area->x1, area->y1);
461 void
462 sp_canvas_arena_set_pick_delta (SPCanvasArena *ca, gdouble delta)
464         g_return_if_fail (ca != NULL);
465         g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
467         /* fixme: repick? */
468         ca->delta = delta;
471 void
472 sp_canvas_arena_set_sticky (SPCanvasArena *ca, gboolean sticky)
474         g_return_if_fail (ca != NULL);
475         g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
477         /* fixme: repick? */
478         ca->sticky = sticky;
481 void
482 sp_canvas_arena_render_pixblock (SPCanvasArena *ca, NRPixBlock *pb)
484         NRRectL area;
486         g_return_if_fail (ca != NULL);
487         g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
489         /* fixme: */
490         pb->empty = FALSE;
492         area.x0 = pb->area.x0;
493         area.y0 = pb->area.y0;
494         area.x1 = pb->area.x1;
495         area.y1 = pb->area.y1;
497         nr_arena_item_invoke_render (NULL, ca->root, &area, pb, 0);