Code

863d86f7c37e026ca4d6ae94dedfe3fba1d674cd
[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>
25 enum {
26         ARENA_EVENT,
27         LAST_SIGNAL
28 };
30 static void sp_canvas_arena_class_init(SPCanvasArenaClass *klass);
31 static void sp_canvas_arena_init(SPCanvasArena *group);
32 static void sp_canvas_arena_destroy(GtkObject *object);
34 static void sp_canvas_arena_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
35 static void sp_canvas_arena_render (SPCanvasItem *item, SPCanvasBuf *buf);
36 static double sp_canvas_arena_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item);
37 static gint sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event);
39 static gint sp_canvas_arena_send_event (SPCanvasArena *arena, GdkEvent *event);
41 static void sp_canvas_arena_request_update (NRArena *arena, NRArenaItem *item, void *data);
42 static void sp_canvas_arena_request_render (NRArena *arena, NRRectL *area, void *data);
44 NRArenaEventVector carenaev = {
45         {NULL},
46         sp_canvas_arena_request_update,
47         sp_canvas_arena_request_render
48 };
50 static SPCanvasItemClass *parent_class;
51 static guint signals[LAST_SIGNAL] = {0};
53 GtkType
54 sp_canvas_arena_get_type (void)
55 {
56         static GtkType type = 0;
57         if (!type) {
58                 GtkTypeInfo info = {
59                         "SPCanvasArena",
60                         sizeof (SPCanvasArena),
61                         sizeof (SPCanvasArenaClass),
62                         (GtkClassInitFunc) sp_canvas_arena_class_init,
63                         (GtkObjectInitFunc) sp_canvas_arena_init,
64                         NULL, NULL, NULL
65                 };
66                 type = gtk_type_unique (SP_TYPE_CANVAS_ITEM, &info);
67         }
68         return type;
69 }
71 static void
72 sp_canvas_arena_class_init (SPCanvasArenaClass *klass)
73 {
74         GtkObjectClass *object_class;
75         SPCanvasItemClass *item_class;
77         object_class = (GtkObjectClass *) klass;
78         item_class = (SPCanvasItemClass *) klass;
80         parent_class = (SPCanvasItemClass*)gtk_type_class (SP_TYPE_CANVAS_ITEM);
82         signals[ARENA_EVENT] = gtk_signal_new ("arena_event",
83                                                GTK_RUN_LAST,
84                                                GTK_CLASS_TYPE(object_class),
85                                                GTK_SIGNAL_OFFSET (SPCanvasArenaClass, arena_event),
86                                                sp_marshal_INT__POINTER_POINTER,
87                                                GTK_TYPE_INT, 2, GTK_TYPE_POINTER, GTK_TYPE_POINTER);
89         object_class->destroy = sp_canvas_arena_destroy;
91         item_class->update = sp_canvas_arena_update;
92         item_class->render = sp_canvas_arena_render;
93         item_class->point = sp_canvas_arena_point;
94         item_class->event = sp_canvas_arena_event;
95 }
97 static void
98 sp_canvas_arena_init (SPCanvasArena *arena)
99 {
100         arena->sticky = FALSE;
102         arena->arena = NRArena::create();
103         arena->root = NRArenaGroup::create(arena->arena);
104         nr_arena_group_set_transparent (NR_ARENA_GROUP (arena->root), TRUE);
106         arena->active = NULL;
108         nr_active_object_add_listener ((NRActiveObject *) arena->arena, (NRObjectEventVector *) &carenaev, sizeof (carenaev), arena);
111 static void
112 sp_canvas_arena_destroy (GtkObject *object)
114         SPCanvasArena *arena = SP_CANVAS_ARENA (object);
116         if (arena->active) {
117                 nr_object_unref ((NRObject *) arena->active);
118                 arena->active = NULL;
119         }
121         if (arena->root) {
122                 nr_arena_item_unref (arena->root);
123                 arena->root = NULL;
124         }
126         if (arena->arena) {
127                 nr_active_object_remove_listener_by_data ((NRActiveObject *) arena->arena, arena);
129                 nr_object_unref ((NRObject *) arena->arena);
130                 arena->arena = NULL;
131         }
133         if (GTK_OBJECT_CLASS (parent_class)->destroy)
134                 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
137 static void
138 sp_canvas_arena_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
140         SPCanvasArena *arena = SP_CANVAS_ARENA (item);
142         if (((SPCanvasItemClass *) parent_class)->update)
143                 (* ((SPCanvasItemClass *) parent_class)->update) (item, affine, flags);
145         arena->gc.transform = affine;
147         guint reset;
148         reset = (flags & SP_CANVAS_UPDATE_AFFINE)? NR_ARENA_ITEM_STATE_ALL : NR_ARENA_ITEM_STATE_NONE;
150         nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_ALL, reset);
152         item->x1 = arena->root->bbox.x0 - 1;
153         item->y1 = arena->root->bbox.y0 - 1;
154         item->x2 = arena->root->bbox.x1 + 1;
155         item->y2 = arena->root->bbox.y1 + 1;
157         if (arena->cursor) {
158                 /* Mess with enter/leave notifiers */
159                 NRArenaItem *new_arena = nr_arena_item_invoke_pick (arena->root, arena->c, arena->arena->delta, arena->sticky);
160                 if (new_arena != arena->active) {
161                         GdkEventCrossing ec;
162                         ec.window = GTK_WIDGET (item->canvas)->window;
163                         ec.send_event = TRUE;
164                         ec.subwindow = ec.window;
165                         ec.time = GDK_CURRENT_TIME;
166                         ec.x = arena->c[NR::X];
167                         ec.y = arena->c[NR::Y];
168                         /* fixme: */
169                         if (arena->active) {
170                                 ec.type = GDK_LEAVE_NOTIFY;
171                                 sp_canvas_arena_send_event (arena, (GdkEvent *) &ec);
172                         }
173                         /* fixme: This is not optimal - better track ::destroy (Lauris) */
174                         if (arena->active) nr_object_unref ((NRObject *) arena->active);
175                         arena->active = new_arena;
176                         if (arena->active) nr_object_ref ((NRObject *) arena->active);
177                         if (arena->active) {
178                                 ec.type = GDK_ENTER_NOTIFY;
179                                 sp_canvas_arena_send_event (arena, (GdkEvent *) &ec);
180                         }
181                 }
182         }
185 static void
186 sp_canvas_arena_render (SPCanvasItem *item, SPCanvasBuf *buf)
188         gint bw, bh, sw, sh;
189         gint x, y;
191         SPCanvasArena *arena = SP_CANVAS_ARENA (item);
193         nr_arena_item_invoke_update (arena->root, NULL, &arena->gc,
194                                      NR_ARENA_ITEM_STATE_BBOX | NR_ARENA_ITEM_STATE_RENDER,
195                                      NR_ARENA_ITEM_STATE_NONE);
197         sp_canvas_prepare_buffer(buf);
198   
199         bw = buf->rect.x1 - buf->rect.x0;
200         bh = buf->rect.y1 - buf->rect.y0;
201         if ((bw < 1) || (bh < 1)) return;
203         // FIXME: currently this function is a huge waste. It receives a buffer but creates a new one and loops 
204         // within the large one, doing arena painting in several blocks. This just makes no sense because the 
205         // buf that we are given is already only a strip of the screen, created by one iteration of a loop in
206         // sp_canvas_paint_rect_internal. With the current numbers, this function's buffer is always 1/4 
207         // smaller than the one we get, because they both are the same number of bytes but
208         // buf uses 3 bytes per pixel (24bpp, packed) while the pixblock created here uses 4 bytes (32bpp).
209         // Eventually I want to switch buf to using 4 bytes (see comment in canvas.cpp) and then remove 
210         // from here the sw/sh calculation, the loop, and creating the intermediate buffer, allowing arena 
211         // just render into buf in one go.  
213         if (arena->arena->rendermode != RENDERMODE_OUTLINE) { // use 256K as a compromise to not slow down gradients
214                 /* 256K is the cached buffer and we need 4 channels */
215                 if (bw * bh < 65536) { // 256K/4
216                         /* We can go with single buffer */
217                         sw = bw;
218                         sh = bh;
219                 } else if (bw <= 4096) {
220                         /* Go with row buffer */
221                         sw = bw;
222                         sh = 65536 / bw;
223                 } else if (bh <= 4096) {
224                         /* Go with column buffer */
225                         sw = 65536 / bh;
226                         sh = bh;
227                 } else {
228                         sw = 256;
229                         sh = 256;
230                 }
231         } else { // paths only, so 1M works faster
232                 /* 1M is the cached buffer and we need 4 channels */
233                 if (bw * bh < 262144) { // 1M/4
234                         /* We can go with single buffer */
235                         sw = bw;
236                         sh = bh;
237                 } else if (bw <= 8192) {
238                         /* Go with row buffer */
239                         sw = bw;
240                         sh = 262144 / bw;
241                 } else if (bh <= 8192) {
242                         /* Go with column buffer */
243                         sw = 262144 / bh;
244                         sh = bh;
245                 } else {
246                         sw = 512;
247                         sh = 512;
248                 }
249         }
251 /*
252 This define chooses between two modes: When on, arena renders into a temporary 
253 32bpp buffer, and the result is then squished into the SPCanvasBuf. When off, arena 
254 renders directly to SPCanvasBuf. However currently this gives no speed advantage, 
255 perhaps because the lack of squishing is offset by the need for arena items to render 
256 to the inconvenient (and probably slower) 24bpp buffer. When SPCanvasBuf is 
257 switched to 32bpp and cairo drawing, however, this define should be removed to 
258 streamline rendering. 
259 */
260 #define STRICT_RGBA
262         for (y = buf->rect.y0; y < buf->rect.y1; y += sh) {
263                 for (x = buf->rect.x0; x < buf->rect.x1; x += sw) {
264                         NRRectL area;
265 #ifdef STRICT_RGBA
266                         NRPixBlock pb;
267 #endif
268                         NRPixBlock cb;
270                         area.x0 = x;
271                         area.y0 = y;
272                         area.x1 = MIN (x + sw, buf->rect.x1);
273                         area.y1 = MIN (y + sh, buf->rect.y1);
275 #ifdef STRICT_RGBA
276                         nr_pixblock_setup_fast (&pb, NR_PIXBLOCK_MODE_R8G8B8A8P, area.x0, area.y0, area.x1, area.y1, TRUE);
277                         /* fixme: */
278                         pb.empty = FALSE;
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; 
289                         if (pb.data.px != NULL) {
290                             nr_arena_item_invoke_render (arena->root, &area, &pb, 0);
291                                 // this does the 32->24 squishing, using an assembler routine:
292                             nr_blit_pixblock_pixblock (&cb, &pb);
293                         }
294                         nr_pixblock_release (&pb);
295 #else
296             cb.visible_area = buf->visible_rect; 
297                         nr_arena_item_invoke_render (arena->root, &area, &cb, 0);
298 #endif
300                         nr_pixblock_release (&cb);
301                 }
302         }
305 static double
306 sp_canvas_arena_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
308         SPCanvasArena *arena = SP_CANVAS_ARENA (item);
310         nr_arena_item_invoke_update (arena->root, NULL, &arena->gc,
311                                      NR_ARENA_ITEM_STATE_BBOX | NR_ARENA_ITEM_STATE_PICK,
312                                      NR_ARENA_ITEM_STATE_NONE);
314         NRArenaItem *picked = nr_arena_item_invoke_pick (arena->root, p, arena->arena->delta, arena->sticky);
316         arena->picked = picked;
318         if (picked) {
319                 *actual_item = item;
320                 return 0.0;
321         }
323         return 1e18;
326 static gint
327 sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event)
329         NRArenaItem *new_arena;
330         /* fixme: This sucks, we have to handle enter/leave notifiers */
332         SPCanvasArena *arena = SP_CANVAS_ARENA (item);
334         gint ret = FALSE;
336         switch (event->type) {
337         case GDK_ENTER_NOTIFY:
338                 if (!arena->cursor) {
339                         if (arena->active) {
340                                 //g_warning ("Cursor entered to arena with already active item");
341                                 nr_object_unref ((NRObject *) arena->active);
342                         }
343                         arena->cursor = TRUE;
345                         /* TODO ... event -> arena transform? */
346                         arena->c = NR::Point(event->crossing.x, event->crossing.y);
348                         /* fixme: Not sure abut this, but seems the right thing (Lauris) */
349                         nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_PICK, NR_ARENA_ITEM_STATE_NONE);
350                         arena->active = nr_arena_item_invoke_pick (arena->root, arena->c, arena->arena->delta, arena->sticky);
351                         if (arena->active) nr_object_ref ((NRObject *) arena->active);
352                         ret = sp_canvas_arena_send_event (arena, event);
353                 }
354                 break;
355         case GDK_LEAVE_NOTIFY:
356                 if (arena->cursor) {
357                         ret = sp_canvas_arena_send_event (arena, event);
358                         if (arena->active) nr_object_unref ((NRObject *) arena->active);
359                         arena->active = NULL;
360                         arena->cursor = FALSE;
361                 }
362                 break;
363         case GDK_MOTION_NOTIFY:
364                 /* TODO ... event -> arena transform? */
365                 arena->c = NR::Point(event->motion.x, event->motion.y);
367                 /* fixme: Not sure abut this, but seems the right thing (Lauris) */
368                 nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_PICK, NR_ARENA_ITEM_STATE_NONE);
369                 new_arena = nr_arena_item_invoke_pick (arena->root, arena->c, arena->arena->delta, arena->sticky);
370                 if (new_arena != arena->active) {
371                         GdkEventCrossing ec;
372                         ec.window = event->motion.window;
373                         ec.send_event = event->motion.send_event;
374                         ec.subwindow = event->motion.window;
375                         ec.time = event->motion.time;
376                         ec.x = event->motion.x;
377                         ec.y = event->motion.y;
378                         /* fixme: */
379                         if (arena->active) {
380                                 ec.type = GDK_LEAVE_NOTIFY;
381                                 ret = sp_canvas_arena_send_event (arena, (GdkEvent *) &ec);
382                         }
383                         if (arena->active) nr_object_unref ((NRObject *) arena->active);
384                         arena->active = new_arena;
385                         if (arena->active) nr_object_ref ((NRObject *) arena->active);
386                         if (arena->active) {
387                                 ec.type = GDK_ENTER_NOTIFY;
388                                 ret = sp_canvas_arena_send_event (arena, (GdkEvent *) &ec);
389                         }
390                 }
391                 ret = sp_canvas_arena_send_event (arena, event);
392                 break;
393         default:
394                 /* Just send event */
395                 ret = sp_canvas_arena_send_event (arena, event);
396                 break;
397         }
399         return ret;
402 static gint
403 sp_canvas_arena_send_event (SPCanvasArena *arena, GdkEvent *event)
405         gint ret = FALSE;
407         /* Send event to arena */
408         gtk_signal_emit (GTK_OBJECT (arena), signals[ARENA_EVENT], arena->active, event, &ret);
410         return ret;
413 static void
414 sp_canvas_arena_request_update (NRArena *arena, NRArenaItem *item, void *data)
416         sp_canvas_item_request_update (SP_CANVAS_ITEM (data));
419 static void
420 sp_canvas_arena_request_render (NRArena *arena, NRRectL *area, void *data)
422         sp_canvas_request_redraw (SP_CANVAS_ITEM (data)->canvas, area->x0, area->y0, area->x1, area->y1);
425 void
426 sp_canvas_arena_set_pick_delta (SPCanvasArena *ca, gdouble delta)
428         g_return_if_fail (ca != NULL);
429         g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
431         /* fixme: repick? */
432         ca->delta = delta;
435 void
436 sp_canvas_arena_set_sticky (SPCanvasArena *ca, gboolean sticky)
438         g_return_if_fail (ca != NULL);
439         g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
441         /* fixme: repick? */
442         ca->sticky = sticky;
445 void
446 sp_canvas_arena_render_pixblock (SPCanvasArena *ca, NRPixBlock *pb)
448         NRRectL area;
450         g_return_if_fail (ca != NULL);
451         g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
453         /* fixme: */
454         pb->empty = FALSE;
456         area.x0 = pb->area.x0;
457         area.y0 = pb->area.y0;
458         area.x1 = pb->area.x1;
459         area.y1 = pb->area.y1;
461         nr_arena_item_invoke_render (ca->root, &area, pb, 0);