a7615b3d7c571ca3556c03797fe98585c64fe9e4
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);
109 }
111 static void
112 sp_canvas_arena_destroy (GtkObject *object)
113 {
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);
135 }
137 static void
138 sp_canvas_arena_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
139 {
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 }
183 }
185 static void
186 sp_canvas_arena_render (SPCanvasItem *item, SPCanvasBuf *buf)
187 {
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);
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 /* fixme: RGB transformed bitmap blit is not implemented (Lauris) */
252 /* And even if it would be, unless it uses MMX there is little reason to go RGB */
253 // CAIRO FIXME: undefine this so that arena renders directly into SPCanvasBuf, without blitting and squishing (32bpp -> 24bpp packed) from a pixblock
254 #define STRICT_RGBA
256 for (y = buf->rect.y0; y < buf->rect.y1; y += sh) {
257 for (x = buf->rect.x0; x < buf->rect.x1; x += sw) {
258 NRRectL area;
259 #ifdef STRICT_RGBA
260 NRPixBlock pb;
261 #endif
262 NRPixBlock cb;
264 area.x0 = x;
265 area.y0 = y;
266 area.x1 = MIN (x + sw, buf->rect.x1);
267 area.y1 = MIN (y + sh, buf->rect.y1);
269 #ifdef STRICT_RGBA
270 nr_pixblock_setup_fast (&pb, NR_PIXBLOCK_MODE_R8G8B8A8P, area.x0, area.y0, area.x1, area.y1, TRUE);
271 /* fixme: */
272 pb.empty = FALSE;
273 #endif
275 // CAIRO FIXME: switch this to R8G8B8A8P and 4 * ...
276 nr_pixblock_setup_extern (&cb, NR_PIXBLOCK_MODE_R8G8B8, area.x0, area.y0, area.x1, area.y1,
277 buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + 3 * (x - buf->rect.x0),
278 buf->buf_rowstride,
279 FALSE, FALSE);
281 #ifdef STRICT_RGBA
282 pb.visible_area = buf->visible_rect;
283 if (pb.data.px != NULL) {
284 nr_arena_item_invoke_render (arena->root, &area, &pb, 0);
285 nr_blit_pixblock_pixblock (&cb, &pb);
286 }
287 nr_pixblock_release (&pb);
288 #else
289 cb.visible_area = buf->visible_rect;
290 nr_arena_item_invoke_render (arena->root, &area, &cb, 0);
291 #endif
293 nr_pixblock_release (&cb);
294 }
295 }
296 }
298 static double
299 sp_canvas_arena_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item)
300 {
301 SPCanvasArena *arena = SP_CANVAS_ARENA (item);
303 nr_arena_item_invoke_update (arena->root, NULL, &arena->gc,
304 NR_ARENA_ITEM_STATE_BBOX | NR_ARENA_ITEM_STATE_PICK,
305 NR_ARENA_ITEM_STATE_NONE);
307 NRArenaItem *picked = nr_arena_item_invoke_pick (arena->root, p, arena->arena->delta, arena->sticky);
309 arena->picked = picked;
311 if (picked) {
312 *actual_item = item;
313 return 0.0;
314 }
316 return 1e18;
317 }
319 static gint
320 sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event)
321 {
322 NRArenaItem *new_arena;
323 /* fixme: This sucks, we have to handle enter/leave notifiers */
325 SPCanvasArena *arena = SP_CANVAS_ARENA (item);
327 gint ret = FALSE;
329 switch (event->type) {
330 case GDK_ENTER_NOTIFY:
331 if (!arena->cursor) {
332 if (arena->active) {
333 //g_warning ("Cursor entered to arena with already active item");
334 nr_object_unref ((NRObject *) arena->active);
335 }
336 arena->cursor = TRUE;
338 /* TODO ... event -> arena transform? */
339 arena->c = NR::Point(event->crossing.x, event->crossing.y);
341 /* fixme: Not sure abut this, but seems the right thing (Lauris) */
342 nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_PICK, NR_ARENA_ITEM_STATE_NONE);
343 arena->active = nr_arena_item_invoke_pick (arena->root, arena->c, arena->arena->delta, arena->sticky);
344 if (arena->active) nr_object_ref ((NRObject *) arena->active);
345 ret = sp_canvas_arena_send_event (arena, event);
346 }
347 break;
348 case GDK_LEAVE_NOTIFY:
349 if (arena->cursor) {
350 ret = sp_canvas_arena_send_event (arena, event);
351 if (arena->active) nr_object_unref ((NRObject *) arena->active);
352 arena->active = NULL;
353 arena->cursor = FALSE;
354 }
355 break;
356 case GDK_MOTION_NOTIFY:
357 /* TODO ... event -> arena transform? */
358 arena->c = NR::Point(event->motion.x, event->motion.y);
360 /* fixme: Not sure abut this, but seems the right thing (Lauris) */
361 nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, NR_ARENA_ITEM_STATE_PICK, NR_ARENA_ITEM_STATE_NONE);
362 new_arena = nr_arena_item_invoke_pick (arena->root, arena->c, arena->arena->delta, arena->sticky);
363 if (new_arena != arena->active) {
364 GdkEventCrossing ec;
365 ec.window = event->motion.window;
366 ec.send_event = event->motion.send_event;
367 ec.subwindow = event->motion.window;
368 ec.time = event->motion.time;
369 ec.x = event->motion.x;
370 ec.y = event->motion.y;
371 /* fixme: */
372 if (arena->active) {
373 ec.type = GDK_LEAVE_NOTIFY;
374 ret = sp_canvas_arena_send_event (arena, (GdkEvent *) &ec);
375 }
376 if (arena->active) nr_object_unref ((NRObject *) arena->active);
377 arena->active = new_arena;
378 if (arena->active) nr_object_ref ((NRObject *) arena->active);
379 if (arena->active) {
380 ec.type = GDK_ENTER_NOTIFY;
381 ret = sp_canvas_arena_send_event (arena, (GdkEvent *) &ec);
382 }
383 }
384 ret = sp_canvas_arena_send_event (arena, event);
385 break;
386 default:
387 /* Just send event */
388 ret = sp_canvas_arena_send_event (arena, event);
389 break;
390 }
392 return ret;
393 }
395 static gint
396 sp_canvas_arena_send_event (SPCanvasArena *arena, GdkEvent *event)
397 {
398 gint ret = FALSE;
400 /* Send event to arena */
401 gtk_signal_emit (GTK_OBJECT (arena), signals[ARENA_EVENT], arena->active, event, &ret);
403 return ret;
404 }
406 static void
407 sp_canvas_arena_request_update (NRArena *arena, NRArenaItem *item, void *data)
408 {
409 sp_canvas_item_request_update (SP_CANVAS_ITEM (data));
410 }
412 static void
413 sp_canvas_arena_request_render (NRArena *arena, NRRectL *area, void *data)
414 {
415 sp_canvas_request_redraw (SP_CANVAS_ITEM (data)->canvas, area->x0, area->y0, area->x1, area->y1);
416 }
418 void
419 sp_canvas_arena_set_pick_delta (SPCanvasArena *ca, gdouble delta)
420 {
421 g_return_if_fail (ca != NULL);
422 g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
424 /* fixme: repick? */
425 ca->delta = delta;
426 }
428 void
429 sp_canvas_arena_set_sticky (SPCanvasArena *ca, gboolean sticky)
430 {
431 g_return_if_fail (ca != NULL);
432 g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
434 /* fixme: repick? */
435 ca->sticky = sticky;
436 }
438 void
439 sp_canvas_arena_render_pixblock (SPCanvasArena *ca, NRPixBlock *pb)
440 {
441 NRRectL area;
443 g_return_if_fail (ca != NULL);
444 g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
446 /* fixme: */
447 pb->empty = FALSE;
449 area.x0 = pb->area.x0;
450 area.y0 = pb->area.y0;
451 area.x1 = pb->area.x1;
452 area.y1 = pb->area.y1;
454 nr_arena_item_invoke_render (ca->root, &area, pb, 0);
455 }