Code

a bunch of cairo fixmes in anticipation of a more direct rendering with less intermed...
[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         if (arena->arena->rendermode != RENDERMODE_OUTLINE) { // use 256K as a compromise to not slow down gradients
204                 /* 256K is the cached buffer and we need 4 channels */
205                 if (bw * bh < 65536) { // 256K/4
206                         /* We can go with single buffer */
207                         sw = bw;
208                         sh = bh;
209                 } else if (bw <= 4096) {
210                         /* Go with row buffer */
211                         sw = bw;
212                         sh = 65536 / bw;
213                 } else if (bh <= 4096) {
214                         /* Go with column buffer */
215                         sw = 65536 / bh;
216                         sh = bh;
217                 } else {
218                         sw = 256;
219                         sh = 256;
220                 }
221         } else { // paths only, so 1M works faster
222                 /* 1M is the cached buffer and we need 4 channels */
223                 if (bw * bh < 262144) { // 1M/4
224                         /* We can go with single buffer */
225                         sw = bw;
226                         sh = bh;
227                 } else if (bw <= 8192) {
228                         /* Go with row buffer */
229                         sw = bw;
230                         sh = 262144 / bw;
231                 } else if (bh <= 8192) {
232                         /* Go with column buffer */
233                         sw = 262144 / bh;
234                         sh = bh;
235                 } else {
236                         sw = 512;
237                         sh = 512;
238                 }
239         }
241 /* fixme: RGB transformed bitmap blit is not implemented (Lauris) */
242 /* And even if it would be, unless it uses MMX there is little reason to go RGB */
243 // CAIRO FIXME: undefine this so that arena renders directly into SPCanvasBuf, without blitting and squishing (32bpp -> 24bpp packed) from a pixblock
244 #define STRICT_RGBA
246         for (y = buf->rect.y0; y < buf->rect.y1; y += sh) {
247                 for (x = buf->rect.x0; x < buf->rect.x1; x += sw) {
248                         NRRectL area;
249 #ifdef STRICT_RGBA
250                         NRPixBlock pb;
251 #endif
252                         NRPixBlock cb;
254                         area.x0 = x;
255                         area.y0 = y;
256                         area.x1 = MIN (x + sw, buf->rect.x1);
257                         area.y1 = MIN (y + sh, buf->rect.y1);
259 #ifdef STRICT_RGBA
260                         nr_pixblock_setup_fast (&pb, NR_PIXBLOCK_MODE_R8G8B8A8P, area.x0, area.y0, area.x1, area.y1, TRUE);
261                         /* fixme: */
262                         pb.empty = FALSE;
263 #endif
265 // CAIRO FIXME: switch this to R8G8B8A8P and 4 * ...
266                         nr_pixblock_setup_extern (&cb, NR_PIXBLOCK_MODE_R8G8B8, area.x0, area.y0, area.x1, area.y1,
267                                                   buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + 3 * (x - buf->rect.x0),
268                                                   buf->buf_rowstride,
269                                                   FALSE, FALSE);
271 #ifdef STRICT_RGBA
272             pb.visible_area = buf->visible_rect; 
273                         if (pb.data.px != NULL) {
274                             nr_arena_item_invoke_render (arena->root, &area, &pb, 0);
275                             nr_blit_pixblock_pixblock (&cb, &pb);
276                         }
277                         nr_pixblock_release (&pb);
278 #else
279             cb.visible_area = buf->visible_rect; 
280                         nr_arena_item_invoke_render (arena->root, &area, &cb, 0);
281 #endif
283                         nr_pixblock_release (&cb);
284                 }
285         }
288 static double
289 sp_canvas_arena_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
291         SPCanvasArena *arena = SP_CANVAS_ARENA (item);
293         nr_arena_item_invoke_update (arena->root, NULL, &arena->gc,
294                                      NR_ARENA_ITEM_STATE_BBOX | NR_ARENA_ITEM_STATE_PICK,
295                                      NR_ARENA_ITEM_STATE_NONE);
297         NRArenaItem *picked = nr_arena_item_invoke_pick (arena->root, p, arena->arena->delta, arena->sticky);
299         arena->picked = picked;
301         if (picked) {
302                 *actual_item = item;
303                 return 0.0;
304         }
306         return 1e18;
309 static gint
310 sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event)
312         NRArenaItem *new_arena;
313         /* fixme: This sucks, we have to handle enter/leave notifiers */
315         SPCanvasArena *arena = SP_CANVAS_ARENA (item);
317         gint ret = FALSE;
319         switch (event->type) {
320         case GDK_ENTER_NOTIFY:
321                 if (!arena->cursor) {
322                         if (arena->active) {
323                                 //g_warning ("Cursor entered to arena with already active item");
324                                 nr_object_unref ((NRObject *) arena->active);
325                         }
326                         arena->cursor = TRUE;
328                         /* TODO ... event -> arena transform? */
329                         arena->c = NR::Point(event->crossing.x, event->crossing.y);
331                         /* fixme: Not sure abut this, but seems the right thing (Lauris) */
332                         nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_PICK, NR_ARENA_ITEM_STATE_NONE);
333                         arena->active = nr_arena_item_invoke_pick (arena->root, arena->c, arena->arena->delta, arena->sticky);
334                         if (arena->active) nr_object_ref ((NRObject *) arena->active);
335                         ret = sp_canvas_arena_send_event (arena, event);
336                 }
337                 break;
338         case GDK_LEAVE_NOTIFY:
339                 if (arena->cursor) {
340                         ret = sp_canvas_arena_send_event (arena, event);
341                         if (arena->active) nr_object_unref ((NRObject *) arena->active);
342                         arena->active = NULL;
343                         arena->cursor = FALSE;
344                 }
345                 break;
346         case GDK_MOTION_NOTIFY:
347                 /* TODO ... event -> arena transform? */
348                 arena->c = NR::Point(event->motion.x, event->motion.y);
350                 /* fixme: Not sure abut this, but seems the right thing (Lauris) */
351                 nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_PICK, NR_ARENA_ITEM_STATE_NONE);
352                 new_arena = nr_arena_item_invoke_pick (arena->root, arena->c, arena->arena->delta, arena->sticky);
353                 if (new_arena != arena->active) {
354                         GdkEventCrossing ec;
355                         ec.window = event->motion.window;
356                         ec.send_event = event->motion.send_event;
357                         ec.subwindow = event->motion.window;
358                         ec.time = event->motion.time;
359                         ec.x = event->motion.x;
360                         ec.y = event->motion.y;
361                         /* fixme: */
362                         if (arena->active) {
363                                 ec.type = GDK_LEAVE_NOTIFY;
364                                 ret = sp_canvas_arena_send_event (arena, (GdkEvent *) &ec);
365                         }
366                         if (arena->active) nr_object_unref ((NRObject *) arena->active);
367                         arena->active = new_arena;
368                         if (arena->active) nr_object_ref ((NRObject *) arena->active);
369                         if (arena->active) {
370                                 ec.type = GDK_ENTER_NOTIFY;
371                                 ret = sp_canvas_arena_send_event (arena, (GdkEvent *) &ec);
372                         }
373                 }
374                 ret = sp_canvas_arena_send_event (arena, event);
375                 break;
376         default:
377                 /* Just send event */
378                 ret = sp_canvas_arena_send_event (arena, event);
379                 break;
380         }
382         return ret;
385 static gint
386 sp_canvas_arena_send_event (SPCanvasArena *arena, GdkEvent *event)
388         gint ret = FALSE;
390         /* Send event to arena */
391         gtk_signal_emit (GTK_OBJECT (arena), signals[ARENA_EVENT], arena->active, event, &ret);
393         return ret;
396 static void
397 sp_canvas_arena_request_update (NRArena *arena, NRArenaItem *item, void *data)
399         sp_canvas_item_request_update (SP_CANVAS_ITEM (data));
402 static void
403 sp_canvas_arena_request_render (NRArena *arena, NRRectL *area, void *data)
405         sp_canvas_request_redraw (SP_CANVAS_ITEM (data)->canvas, area->x0, area->y0, area->x1, area->y1);
408 void
409 sp_canvas_arena_set_pick_delta (SPCanvasArena *ca, gdouble delta)
411         g_return_if_fail (ca != NULL);
412         g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
414         /* fixme: repick? */
415         ca->delta = delta;
418 void
419 sp_canvas_arena_set_sticky (SPCanvasArena *ca, gboolean sticky)
421         g_return_if_fail (ca != NULL);
422         g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
424         /* fixme: repick? */
425         ca->sticky = sticky;
428 void
429 sp_canvas_arena_render_pixblock (SPCanvasArena *ca, NRPixBlock *pb)
431         NRRectL area;
433         g_return_if_fail (ca != NULL);
434         g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
436         /* fixme: */
437         pb->empty = FALSE;
439         area.x0 = pb->area.x0;
440         area.y0 = pb->area.y0;
441         area.x1 = pb->area.x1;
442         area.y1 = pb->area.y1;
444         nr_arena_item_invoke_render (ca->root, &area, pb, 0);