Code

noop: CodingStyle: re-indent a few files that had mixtures of spaces & tabs for inden...
[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);
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;
392         case GDK_LEAVE_NOTIFY:
393             if (arena->cursor) {
394                 ret = sp_canvas_arena_send_event (arena, event);
395                 if (arena->active) nr_object_unref ((NRObject *) arena->active);
396                 arena->active = NULL;
397                 arena->cursor = FALSE;
398             }
399             break;
401         case GDK_MOTION_NOTIFY:
402             /* TODO ... event -> arena transform? */
403             arena->c = NR::Point(event->motion.x, event->motion.y);
405             /* fixme: Not sure abut this, but seems the right thing (Lauris) */
406             nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_PICK, NR_ARENA_ITEM_STATE_NONE);
407             new_arena = nr_arena_item_invoke_pick (arena->root, arena->c, arena->arena->delta, arena->sticky);
408             if (new_arena != arena->active) {
409                 GdkEventCrossing ec;
410                 ec.window = event->motion.window;
411                 ec.send_event = event->motion.send_event;
412                 ec.subwindow = event->motion.window;
413                 ec.time = event->motion.time;
414                 ec.x = event->motion.x;
415                 ec.y = event->motion.y;
416                 /* fixme: */
417                 if (arena->active) {
418                     ec.type = GDK_LEAVE_NOTIFY;
419                     ret = sp_canvas_arena_send_event (arena, (GdkEvent *) &ec);
420                 }
421                 if (arena->active) nr_object_unref ((NRObject *) arena->active);
422                 arena->active = new_arena;
423                 if (arena->active) nr_object_ref ((NRObject *) arena->active);
424                 if (arena->active) {
425                     ec.type = GDK_ENTER_NOTIFY;
426                     ret = sp_canvas_arena_send_event (arena, (GdkEvent *) &ec);
427                 }
428             }
429             ret = sp_canvas_arena_send_event (arena, event);
430             break;
432         default:
433             /* Just send event */
434             ret = sp_canvas_arena_send_event (arena, event);
435             break;
436     }
438     return ret;
441 static gint
442 sp_canvas_arena_send_event (SPCanvasArena *arena, GdkEvent *event)
444     gint ret = FALSE;
446     /* Send event to arena */
447     gtk_signal_emit (GTK_OBJECT (arena), signals[ARENA_EVENT], arena->active, event, &ret);
449     return ret;
452 static void
453 sp_canvas_arena_request_update (NRArena *arena, NRArenaItem *item, void *data)
455     sp_canvas_item_request_update (SP_CANVAS_ITEM (data));
458 static void
459 sp_canvas_arena_request_render (NRArena *arena, NRRectL *area, void *data)
461     sp_canvas_request_redraw (SP_CANVAS_ITEM (data)->canvas, area->x0, area->y0, area->x1, area->y1);
464 void
465 sp_canvas_arena_set_pick_delta (SPCanvasArena *ca, gdouble delta)
467     g_return_if_fail (ca != NULL);
468     g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
470     /* fixme: repick? */
471     ca->delta = delta;
474 void
475 sp_canvas_arena_set_sticky (SPCanvasArena *ca, gboolean sticky)
477     g_return_if_fail (ca != NULL);
478     g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
480     /* fixme: repick? */
481     ca->sticky = sticky;
484 void
485 sp_canvas_arena_render_pixblock (SPCanvasArena *ca, NRPixBlock *pb)
487     NRRectL area;
489     g_return_if_fail (ca != NULL);
490     g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
492     /* fixme: */
493     pb->empty = FALSE;
495     area.x0 = pb->area.x0;
496     area.y0 = pb->area.y0;
497     area.x1 = pb->area.x1;
498     area.y1 = pb->area.y1;
500     nr_arena_item_invoke_render (NULL, ca->root, &area, pb, 0);
504 /*
505   Local Variables:
506   mode:c++
507   c-file-style:"stroustrup"
508   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
509   indent-tabs-mode:nil
510   fill-column:99
511   End:
512 */
513 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :