1 #define __NR_ARENA_ITEM_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 #define noNR_ARENA_ITEM_VERBOSE
16 #define noNR_ARENA_ITEM_DEBUG_CASCADE
19 #include <libnr/nr-blit.h>
20 #include <libnr/nr-pixops.h>
21 #include "nr-arena.h"
22 #include "nr-arena-item.h"
23 //#include "nr-arena-group.h"
26 static void nr_arena_item_class_init (NRArenaItemClass *klass);
27 static void nr_arena_item_init (NRArenaItem *item);
28 static void nr_arena_item_private_finalize (NRObject *object);
30 #ifdef arena_item_tile_cache
31 bool insert_cache(NRArenaItem* owner,int th,int tv,NRPixBlock *ipb,NRPixBlock *mpb,double activity,double duration);
32 void remove_caches(NRArenaItem* owner);
33 bool test_cache(NRArenaItem* owner,int th,int tv,NRPixBlock &ipb,NRPixBlock &mpb,bool &hasMask);
34 #endif
36 static NRObjectClass *parent_class;
38 NRType
39 nr_arena_item_get_type (void)
40 {
41 static NRType type = 0;
42 if (!type) {
43 type = nr_object_register_type (NR_TYPE_OBJECT,
44 "NRArenaItem",
45 sizeof (NRArenaItemClass),
46 sizeof (NRArenaItem),
47 (void (*) (NRObjectClass *)) nr_arena_item_class_init,
48 (void (*) (NRObject *)) nr_arena_item_init);
49 }
50 return type;
51 }
53 static void
54 nr_arena_item_class_init (NRArenaItemClass *klass)
55 {
56 NRObjectClass *object_class;
58 object_class = (NRObjectClass *) klass;
60 parent_class = ((NRObjectClass *) klass)->parent;
62 object_class->finalize = nr_arena_item_private_finalize;
63 object_class->cpp_ctor = NRObject::invoke_ctor<NRArenaItem>;
64 }
66 NRArenaItem::NRArenaItem() {
67 // clear all reverse-pointing pointers before finalization
68 clearOnceInaccessible(&arena);
69 clearOnceInaccessible(&parent);
70 clearOnceInaccessible(&prev);
71 }
73 static void
74 nr_arena_item_init (NRArenaItem *item)
75 {
76 item->arena = NULL;
77 item->parent = NULL;
78 item->next = item->prev = NULL;
80 item->key = 0;
82 item->state = 0;
83 item->sensitive = TRUE;
84 item->visible = TRUE;
86 memset(&item->bbox, 0, sizeof(item->bbox));
87 item->transform = NULL;
88 item->opacity = 255;
89 item->render_opacity = FALSE;
91 #ifdef arena_item_tile_cache
92 item->activity=0.0;
93 item->skipCaching=false;
94 #endif
96 item->transform = NULL;
97 item->clip = NULL;
98 item->mask = NULL;
99 item->px = NULL;
100 item->data = NULL;
101 }
103 static void
104 nr_arena_item_private_finalize (NRObject *object)
105 {
106 NRArenaItem *item=static_cast<NRArenaItem *>(object);
108 #ifdef arena_item_tile_cache
109 remove_caches(item);
110 #endif
112 if (item->px) {
113 nr_free (item->px);
114 }
116 if (item->transform) {
117 nr_free (item->transform);
118 }
120 ((NRObjectClass *) (parent_class))->finalize (object);
121 }
123 NRArenaItem *
124 nr_arena_item_children (NRArenaItem *item)
125 {
126 nr_return_val_if_fail (item != NULL, NULL);
127 nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
129 if (NR_ARENA_ITEM_VIRTUAL (item, children))
130 return NR_ARENA_ITEM_VIRTUAL (item, children) (item);
132 return NULL;
133 }
135 NRArenaItem *
136 nr_arena_item_last_child (NRArenaItem *item)
137 {
138 nr_return_val_if_fail (item != NULL, NULL);
139 nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
141 if (NR_ARENA_ITEM_VIRTUAL (item, last_child)) {
142 return NR_ARENA_ITEM_VIRTUAL (item, last_child) (item);
143 } else {
144 NRArenaItem *ref;
145 ref = nr_arena_item_children (item);
146 if (ref) while (ref->next) ref = ref->next;
147 return ref;
148 }
149 }
151 void
152 nr_arena_item_add_child (NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref)
153 {
154 nr_return_if_fail (item != NULL);
155 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
156 nr_return_if_fail (child != NULL);
157 nr_return_if_fail (NR_IS_ARENA_ITEM (child));
158 nr_return_if_fail (child->parent == NULL);
159 nr_return_if_fail (child->prev == NULL);
160 nr_return_if_fail (child->next == NULL);
161 nr_return_if_fail (child->arena == item->arena);
162 nr_return_if_fail (child != ref);
163 nr_return_if_fail (!ref || NR_IS_ARENA_ITEM (ref));
164 nr_return_if_fail (!ref || (ref->parent == item));
166 if (NR_ARENA_ITEM_VIRTUAL (item, add_child))
167 NR_ARENA_ITEM_VIRTUAL (item, add_child) (item, child, ref);
168 }
170 void
171 nr_arena_item_remove_child (NRArenaItem *item, NRArenaItem *child)
172 {
173 nr_return_if_fail (item != NULL);
174 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
175 nr_return_if_fail (child != NULL);
176 nr_return_if_fail (NR_IS_ARENA_ITEM (child));
177 nr_return_if_fail (child->parent == item);
179 if (NR_ARENA_ITEM_VIRTUAL (item, remove_child))
180 NR_ARENA_ITEM_VIRTUAL (item, remove_child) (item, child);
181 }
183 void
184 nr_arena_item_set_child_position (NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref)
185 {
186 nr_return_if_fail (item != NULL);
187 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
188 nr_return_if_fail (child != NULL);
189 nr_return_if_fail (NR_IS_ARENA_ITEM (child));
190 nr_return_if_fail (child->parent == item);
191 nr_return_if_fail (!ref || NR_IS_ARENA_ITEM (ref));
192 nr_return_if_fail (!ref || (ref->parent == item));
194 if (NR_ARENA_ITEM_VIRTUAL (item, set_child_position))
195 NR_ARENA_ITEM_VIRTUAL (item, set_child_position) (item, child, ref);
196 }
198 NRArenaItem *
199 nr_arena_item_ref (NRArenaItem *item)
200 {
201 nr_object_ref ((NRObject *) item);
203 return item;
204 }
206 NRArenaItem *
207 nr_arena_item_unref (NRArenaItem *item)
208 {
209 nr_object_unref ((NRObject *) item);
211 return NULL;
212 }
214 unsigned int
215 nr_arena_item_invoke_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset)
216 {
217 NRGC childgc(gc);
219 nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
220 nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NR_ARENA_ITEM_STATE_INVALID);
221 nr_return_val_if_fail (!(state & NR_ARENA_ITEM_STATE_INVALID), NR_ARENA_ITEM_STATE_INVALID);
223 #ifdef NR_ARENA_ITEM_DEBUG_CASCADE
224 printf ("Update %s:%p %x %x %x\n", nr_type_name_from_instance ((GTypeInstance *) item), item, state, item->state, reset);
225 #endif
227 /* return if in error */
228 if (item->state & NR_ARENA_ITEM_STATE_INVALID) return item->state;
229 /* Set reset flags according to propagation status */
230 if (item->propagate) {
231 reset |= ~item->state;
232 item->propagate = FALSE;
233 }
234 /* Reset our state */
235 item->state &= ~reset;
236 /* Return if NOP */
237 if (!(~item->state & state)) return item->state;
238 /* Test whether to return immediately */
239 if (area && (item->state & NR_ARENA_ITEM_STATE_BBOX)) {
240 if (!nr_rect_l_test_intersect (area, &item->bbox)) return item->state;
241 }
243 /* Reset image cache, if not to be kept */
244 if (!(item->state & NR_ARENA_ITEM_STATE_IMAGE) && (item->px)) {
245 nr_free (item->px);
246 item->px = NULL;
247 }
248 #ifdef arena_item_tile_cache
249 remove_caches(item);
250 #endif
252 /* Set up local gc */
253 childgc = *gc;
254 if (item->transform) {
255 nr_matrix_multiply (&childgc.transform, item->transform, &childgc.transform);
256 }
258 /* Invoke the real method */
259 item->state = NR_ARENA_ITEM_VIRTUAL (item, update) (item, area, &childgc, state, reset);
260 if (item->state & NR_ARENA_ITEM_STATE_INVALID) return item->state;
261 /* Clipping */
262 if (item->clip) {
263 unsigned int newstate;
264 newstate = nr_arena_item_invoke_update (item->clip, area, &childgc, state, reset);
265 if (newstate & NR_ARENA_ITEM_STATE_INVALID) {
266 item->state |= NR_ARENA_ITEM_STATE_INVALID;
267 return item->state;
268 }
269 nr_rect_l_intersect (&item->bbox, &item->bbox, &item->clip->bbox);
270 }
271 /* Masking */
272 if (item->mask) {
273 unsigned int newstate;
274 newstate = nr_arena_item_invoke_update (item->mask, area, &childgc, state, reset);
275 if (newstate & NR_ARENA_ITEM_STATE_INVALID) {
276 item->state |= NR_ARENA_ITEM_STATE_INVALID;
277 return item->state;
278 }
279 nr_rect_l_intersect (&item->bbox, &item->bbox, &item->mask->bbox);
280 }
282 return item->state;
283 }
285 /**
286 * Render item to pixblock.
287 *
288 * \return Has NR_ARENA_ITEM_STATE_RENDER set on success.
289 */
291 unsigned int nr_arena_item_invoke_render(NRArenaItem *item, NRRectL const *area, NRPixBlock *pb, unsigned int flags)
292 {
293 NRRectL carea;
294 NRPixBlock *dpb;
295 NRPixBlock cpb;
296 unsigned int state;
298 nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
299 nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NR_ARENA_ITEM_STATE_INVALID);
300 nr_return_val_if_fail (item->state & NR_ARENA_ITEM_STATE_BBOX, item->state);
302 #ifdef NR_ARENA_ITEM_VERBOSE
303 printf ("Invoke render %p: %d %d - %d %d\n", item, area->x0, area->y0, area->x1, area->y1);
304 #endif
306 #ifdef arena_item_tile_cache
307 item->activity*=0.5;
308 #endif
310 /* If we are outside bbox just return successfully */
311 if (!item->visible) return item->state | NR_ARENA_ITEM_STATE_RENDER;
312 nr_rect_l_intersect (&carea, area, &item->bbox);
313 if (nr_rect_l_test_empty (&carea)) return item->state | NR_ARENA_ITEM_STATE_RENDER;
315 if (item->px) {
316 /* Has cache pixblock, render this and return */
317 nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P,
318 /* fixme: This probably cannot overflow, because we render only if visible */
319 /* fixme: and pixel cache is there only for small items */
320 /* fixme: But this still needs extra check (Lauris) */
321 item->bbox.x0, item->bbox.y0,
322 item->bbox.x1, item->bbox.y1,
323 item->px, 4 * (item->bbox.x1 - item->bbox.x0), FALSE, FALSE);
324 nr_blit_pixblock_pixblock (pb, &cpb);
325 nr_pixblock_release (&cpb);
326 pb->empty = FALSE;
327 return item->state | NR_ARENA_ITEM_STATE_RENDER;
328 }
330 dpb = pb;
331 bool canCache=false;
332 #ifdef arena_item_tile_cache
333 bool checkCache=false;
334 int tile_h=0,tile_v=0;
335 #endif
336 /* Setup cache if we can */
337 if ((!(flags & NR_ARENA_ITEM_RENDER_NO_CACHE)) &&
338 (carea.x0 <= item->bbox.x0) && (carea.y0 <= item->bbox.y0) &&
339 (carea.x1 >= item->bbox.x1) && (carea.y1 >= item->bbox.y1) &&
340 (((item->bbox.x1 - item->bbox.x0) * (item->bbox.y1 - item->bbox.y0)) <= 4096)) {
341 // Item bbox is fully in renderable area and size is acceptable
342 carea.x0 = item->bbox.x0;
343 carea.y0 = item->bbox.y0;
344 carea.x1 = item->bbox.x1;
345 carea.y1 = item->bbox.y1;
346 item->px = nr_new (unsigned char, 4 * (carea.x1 - carea.x0) * (carea.y1 - carea.y0));
347 nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P,
348 carea.x0, carea.y0, carea.x1, carea.y1,
349 item->px, 4 * (carea.x1 - carea.x0), TRUE, TRUE);
350 dpb = &cpb;
351 // Set nocache flag for downstream rendering
352 flags |= NR_ARENA_ITEM_RENDER_NO_CACHE;
353 } else {
354 #ifdef arena_item_tile_cache
355 if ( item->skipCaching ) {
356 } else {
357 int tl=area->x0&(~127);
358 int tt=area->y0&(~127);
359 if ( area->x1 <= tl+128 && area->y1 <= tt+128 ) {
360 checkCache=true;
361 tile_h=tl/128;
362 tile_v=tt/128;
363 int surf=(area->x1-area->x0)*(area->y1-area->y0);
364 if ( surf >= 4096 ) {
365 canCache=true;
366 carea.x0=tl;
367 carea.y0=tt;
368 carea.x1=tl+128;
369 carea.y1=tt+128;
370 }
371 }
372 }
373 #endif
374 }
376 #ifdef arena_item_tile_cache
377 item->activity+=1.0;
378 #endif
380 #ifdef arena_item_tile_cache
381 if ( checkCache ) {
382 NRPixBlock ipb, mpb;
383 bool hasMask;
384 if ( test_cache(item,tile_h,tile_v,ipb,mpb,hasMask) ) {
385 // youpi! c'etait deja cache
386 if ( hasMask ) {
387 nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
388 } else if ( ((item->opacity != 255) && !item->render_opacity) ) {
389 nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
390 } else {
391 nr_blit_pixblock_pixblock (pb, &ipb);
392 }
393 pb->empty = FALSE;
394 return item->state | NR_ARENA_ITEM_STATE_RENDER;
395 }
396 }
397 #endif
398 if ( canCache ) {
399 #ifdef arena_item_tile_cache
400 // nota: exclusif de dpb != pb, donc pas de cas particulier a la fin
401 NRPixBlock ipb, mpb;
403 // struct timeval start_time,end_time;
404 // gettimeofday(&start_time,NULL);
405 GTimeVal start_time,end_time;
406 g_get_current_time (&start_time);
407 int duration=0;
409 /* Setup and render item buffer */
410 nr_pixblock_setup_fast (&ipb, NR_PIXBLOCK_MODE_R8G8B8A8P, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
411 state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, &ipb, flags);
412 if (state & NR_ARENA_ITEM_STATE_INVALID) {
413 /* Clean up and return error */
414 nr_pixblock_release (&ipb);
415 if (dpb != pb) nr_pixblock_release (dpb);
416 item->state |= NR_ARENA_ITEM_STATE_INVALID;
417 return item->state;
418 }
419 ipb.empty = FALSE;
421 if (item->clip || item->mask) {
422 /* Setup mask pixblock */
423 nr_pixblock_setup_fast (&mpb, NR_PIXBLOCK_MODE_A8, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
424 /* Do clip if needed */
425 if (item->clip) {
426 state = nr_arena_item_invoke_clip (item->clip, &carea, &mpb);
427 if (state & NR_ARENA_ITEM_STATE_INVALID) {
428 /* Clean up and return error */
429 nr_pixblock_release (&mpb);
430 nr_pixblock_release (&ipb);
431 if (dpb != pb) nr_pixblock_release (dpb);
432 item->state |= NR_ARENA_ITEM_STATE_INVALID;
433 return item->state;
434 }
435 mpb.empty = FALSE;
436 }
437 /* Do mask if needed */
438 if (item->mask) {
439 NRPixBlock tpb;
440 /* Set up yet another temporary pixblock */
441 nr_pixblock_setup_fast (&tpb, NR_PIXBLOCK_MODE_R8G8B8A8N, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
442 state = NR_ARENA_ITEM_VIRTUAL (item->mask, render) (item->mask, &carea, &tpb, flags);
443 if (state & NR_ARENA_ITEM_STATE_INVALID) {
444 /* Clean up and return error */
445 nr_pixblock_release (&tpb);
446 nr_pixblock_release (&mpb);
447 nr_pixblock_release (&ipb);
448 if (dpb != pb) nr_pixblock_release (dpb);
449 item->state |= NR_ARENA_ITEM_STATE_INVALID;
450 return item->state;
451 }
452 /* Composite with clip */
453 if (item->clip) {
454 int x, y;
455 for (y = carea.y0; y < carea.y1; y++) {
456 unsigned char *s, *d;
457 s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
458 d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
459 for (x = carea.x0; x < carea.x1; x++) {
460 unsigned int m;
461 m = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
462 d[0] = NR_PREMUL (d[0], m);
463 s += 4;
464 d += 1;
465 }
466 }
467 } else {
468 int x, y;
469 for (y = carea.y0; y < carea.y1; y++) {
470 unsigned char *s, *d;
471 s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
472 d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
473 for (x = carea.x0; x < carea.x1; x++) {
474 unsigned int m;
475 m = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
476 d[0] = m;
477 s += 4;
478 d += 1;
479 }
480 }
481 mpb.empty = FALSE;
482 }
483 nr_pixblock_release (&tpb);
484 }
485 /* Multiply with opacity if needed */
486 if ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE) {
487 int x, y;
488 unsigned int a;
489 a = item->opacity;
490 for (y = carea.y0; y < carea.y1; y++) {
491 unsigned char *d;
492 d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
493 for (x = carea.x0; x < carea.x1; x++) {
494 d[0] = NR_PREMUL (d[0], a);
495 d += 1;
496 }
497 }
498 }
499 /* Compose rendering pixblock int destination */
500 // gettimeofday(&end_time,NULL);
501 g_get_current_time (&end_time);
502 duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
503 if ( !(ipb.empty) ) {
504 nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
505 if ( insert_cache(item,tile_h,tile_v,&ipb,&mpb,item->activity,(double)duration) ) {
506 } else {
507 nr_pixblock_release (&mpb);
508 nr_pixblock_release (&ipb);
509 }
510 dpb->empty = FALSE;
511 } else {
512 nr_pixblock_release (&ipb);
513 }
514 } else if ( ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE) ) {
515 /* Opacity only */
516 // gettimeofday(&end_time,NULL);
517 g_get_current_time (&end_time);
518 duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
519 if ( !(ipb.empty) ) {
520 nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
521 if ( insert_cache(item,tile_h,tile_v,&ipb,NULL,item->activity,(double)duration) ) {
522 } else {
523 nr_pixblock_release (&ipb);
524 }
525 dpb->empty = FALSE;
526 } else {
527 nr_pixblock_release (&ipb);
528 }
529 } else {
530 // gettimeofday(&end_time,NULL);
531 g_get_current_time (&end_time);
532 duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
533 if ( !(ipb.empty) ) {
534 nr_blit_pixblock_pixblock (dpb, &ipb);
535 if ( insert_cache(item,tile_h,tile_v,&ipb,NULL,item->activity,(double)duration) ) {
536 } else {
537 nr_pixblock_release (&ipb);
538 }
539 dpb->empty = FALSE;
540 } else {
541 nr_pixblock_release (&ipb);
542 }
543 }
544 #endif
545 } else {
546 /* Determine, whether we need temporary buffer */
547 if (item->clip || item->mask || ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE)) {
548 NRPixBlock ipb, mpb;
550 /* Setup and render item buffer */
551 nr_pixblock_setup_fast (&ipb, NR_PIXBLOCK_MODE_R8G8B8A8P, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
552 state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, &ipb, flags);
553 if (state & NR_ARENA_ITEM_STATE_INVALID) {
554 /* Clean up and return error */
555 nr_pixblock_release (&ipb);
556 if (dpb != pb) nr_pixblock_release (dpb);
557 item->state |= NR_ARENA_ITEM_STATE_INVALID;
558 return item->state;
559 }
560 ipb.empty = FALSE;
562 if (item->clip || item->mask) {
563 /* Setup mask pixblock */
564 nr_pixblock_setup_fast (&mpb, NR_PIXBLOCK_MODE_A8, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
565 /* Do clip if needed */
566 if (item->clip) {
567 state = nr_arena_item_invoke_clip (item->clip, &carea, &mpb);
568 if (state & NR_ARENA_ITEM_STATE_INVALID) {
569 /* Clean up and return error */
570 nr_pixblock_release (&mpb);
571 nr_pixblock_release (&ipb);
572 if (dpb != pb) nr_pixblock_release (dpb);
573 item->state |= NR_ARENA_ITEM_STATE_INVALID;
574 return item->state;
575 }
576 mpb.empty = FALSE;
577 }
578 /* Do mask if needed */
579 if (item->mask) {
580 NRPixBlock tpb;
581 /* Set up yet another temporary pixblock */
582 nr_pixblock_setup_fast (&tpb, NR_PIXBLOCK_MODE_R8G8B8A8N, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
583 state = NR_ARENA_ITEM_VIRTUAL (item->mask, render) (item->mask, &carea, &tpb, flags);
584 if (state & NR_ARENA_ITEM_STATE_INVALID) {
585 /* Clean up and return error */
586 nr_pixblock_release (&tpb);
587 nr_pixblock_release (&mpb);
588 nr_pixblock_release (&ipb);
589 if (dpb != pb) nr_pixblock_release (dpb);
590 item->state |= NR_ARENA_ITEM_STATE_INVALID;
591 return item->state;
592 }
593 /* Composite with clip */
594 if (item->clip) {
595 int x, y;
596 for (y = carea.y0; y < carea.y1; y++) {
597 unsigned char *s, *d;
598 s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
599 d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
600 for (x = carea.x0; x < carea.x1; x++) {
601 unsigned int m;
602 m = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
603 d[0] = NR_PREMUL (d[0], m);
604 s += 4;
605 d += 1;
606 }
607 }
608 } else {
609 int x, y;
610 for (y = carea.y0; y < carea.y1; y++) {
611 unsigned char *s, *d;
612 s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
613 d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
614 for (x = carea.x0; x < carea.x1; x++) {
615 unsigned int m;
616 m = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
617 d[0] = m;
618 s += 4;
619 d += 1;
620 }
621 }
622 mpb.empty = FALSE;
623 }
624 nr_pixblock_release (&tpb);
625 }
626 /* Multiply with opacity if needed */
627 if ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE) {
628 int x, y;
629 unsigned int a;
630 a = item->opacity;
631 for (y = carea.y0; y < carea.y1; y++) {
632 unsigned char *d;
633 d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
634 for (x = carea.x0; x < carea.x1; x++) {
635 d[0] = NR_PREMUL (d[0], a);
636 d += 1;
637 }
638 }
639 }
640 /* Compose rendering pixblock int destination */
641 nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
642 nr_pixblock_release (&mpb);
643 } else {
644 /* Opacity only */
645 nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
646 }
647 nr_pixblock_release (&ipb);
648 dpb->empty = FALSE;
649 } else {
650 /* Just render */
651 state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, dpb, flags);
652 if (state & NR_ARENA_ITEM_STATE_INVALID) {
653 /* Clean up and return error */
654 if (dpb != pb) nr_pixblock_release (dpb);
655 item->state |= NR_ARENA_ITEM_STATE_INVALID;
656 return item->state;
657 }
658 dpb->empty = FALSE;
659 }
661 if (dpb != pb) {
662 /* Have to blit from cache */
663 nr_blit_pixblock_pixblock (pb, dpb);
664 nr_pixblock_release (dpb);
665 pb->empty = FALSE;
666 item->state |= NR_ARENA_ITEM_STATE_IMAGE;
667 }
668 }
669 return item->state | NR_ARENA_ITEM_STATE_RENDER;
670 }
672 unsigned int
673 nr_arena_item_invoke_clip (NRArenaItem *item, NRRectL *area, NRPixBlock *pb)
674 {
675 nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
676 nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NR_ARENA_ITEM_STATE_INVALID);
677 /* we originally short-circuited if the object state included
678 * NR_ARENA_ITEM_STATE_CLIP (and showed a warning on the console);
679 * anyone know why we stopped doing so?
680 */
681 nr_return_val_if_fail ((pb->area.x1 - pb->area.x0) >= (area->x1 - area->x0), NR_ARENA_ITEM_STATE_INVALID);
682 nr_return_val_if_fail ((pb->area.y1 - pb->area.y0) >= (area->y1 - area->y0), NR_ARENA_ITEM_STATE_INVALID);
684 #ifdef NR_ARENA_ITEM_VERBOSE
685 printf ("Invoke render %p: %d %d - %d %d\n", item, area->x0, area->y0, area->x1, area->y1);
686 #endif
688 if (item->visible && nr_rect_l_test_intersect (area, &item->bbox)) {
689 /* Need render that item */
690 if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->clip)
691 return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS(item))->clip (item, area, pb);
692 }
694 return item->state;
695 }
697 NRArenaItem *
698 nr_arena_item_invoke_pick (NRArenaItem *item, NR::Point p, double delta, unsigned int sticky)
699 {
700 nr_return_val_if_fail (item != NULL, NULL);
701 nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
703 // Sometimes there's no BBOX in item->state, reason unknown (bug 992817); I made this not an assert to remove the warning
704 if (!(item->state & NR_ARENA_ITEM_STATE_BBOX) || !(item->state & NR_ARENA_ITEM_STATE_PICK))
705 return NULL;
707 if (!sticky && !(item->visible && item->sensitive)) return NULL;
709 // TODO: rewrite using NR::Rect
710 const double x = p[NR::X];
711 const double y = p[NR::Y];
713 if (((x + delta) >= item->bbox.x0) &&
714 ((x - delta) < item->bbox.x1) &&
715 ((y + delta) >= item->bbox.y0) &&
716 ((y - delta) < item->bbox.y1)) {
717 if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->pick)
718 return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->pick (item, p, delta, sticky);
719 }
721 return NULL;
722 }
724 void
725 nr_arena_item_request_update (NRArenaItem *item, unsigned int reset, unsigned int propagate)
726 {
727 nr_return_if_fail (item != NULL);
728 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
729 nr_return_if_fail (!(reset & NR_ARENA_ITEM_STATE_INVALID));
731 if (propagate && !item->propagate) item->propagate = TRUE;
733 if (item->state & reset) {
734 item->state &= ~reset;
735 if (item->parent) {
736 nr_arena_item_request_update (item->parent, reset, FALSE);
737 } else {
738 nr_arena_request_update (item->arena, item);
739 }
740 }
741 }
743 void
744 nr_arena_item_request_render (NRArenaItem *item)
745 {
746 nr_return_if_fail (item != NULL);
747 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
749 nr_arena_request_render_rect (item->arena, &item->bbox);
750 }
752 /* Public */
754 NRArenaItem *
755 nr_arena_item_unparent (NRArenaItem *item)
756 {
757 nr_return_val_if_fail (item != NULL, NULL);
758 nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
760 nr_arena_item_request_render (item);
762 if (item->parent) {
763 nr_arena_item_remove_child (item->parent, item);
764 }
766 return NULL;
767 }
769 void
770 nr_arena_item_append_child (NRArenaItem *parent, NRArenaItem *child)
771 {
772 nr_return_if_fail (parent != NULL);
773 nr_return_if_fail (NR_IS_ARENA_ITEM (parent));
774 nr_return_if_fail (child != NULL);
775 nr_return_if_fail (NR_IS_ARENA_ITEM (child));
776 nr_return_if_fail (parent->arena == child->arena);
777 nr_return_if_fail (child->parent == NULL);
778 nr_return_if_fail (child->prev == NULL);
779 nr_return_if_fail (child->next == NULL);
781 nr_arena_item_add_child (parent, child, nr_arena_item_last_child (parent));
782 }
784 void
785 nr_arena_item_set_transform(NRArenaItem *item, NR::Matrix const &transform)
786 {
787 NRMatrix const t(transform);
788 nr_arena_item_set_transform(item, &t);
789 }
791 void
792 nr_arena_item_set_transform(NRArenaItem *item, NRMatrix const *transform)
793 {
794 const NRMatrix *ms, *md;
796 nr_return_if_fail (item != NULL);
797 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
799 if (!transform && !item->transform) return;
801 md = (item->transform) ? item->transform : &NR_MATRIX_IDENTITY;
802 ms = (transform) ? transform : &NR_MATRIX_IDENTITY;
804 if (!NR_MATRIX_DF_TEST_CLOSE (md, ms, NR_EPSILON)) {
805 nr_arena_item_request_render (item);
806 if (!transform || nr_matrix_test_identity (transform, NR_EPSILON)) {
807 /* Set to identity affine */
808 if (item->transform) nr_free (item->transform);
809 item->transform = NULL;
810 } else {
811 if (!item->transform) item->transform = nr_new (NRMatrix, 1);
812 *item->transform = *transform;
813 }
814 nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
815 }
816 }
818 void
819 nr_arena_item_set_opacity (NRArenaItem *item, double opacity)
820 {
821 nr_return_if_fail (item != NULL);
822 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
824 nr_arena_item_request_render (item);
826 item->opacity = (unsigned int) (opacity * 255.9999);
827 }
829 void
830 nr_arena_item_set_sensitive (NRArenaItem *item, unsigned int sensitive)
831 {
832 nr_return_if_fail (item != NULL);
833 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
835 /* fixme: mess with pick/repick... */
837 item->sensitive = sensitive;
838 }
840 void
841 nr_arena_item_set_visible (NRArenaItem *item, unsigned int visible)
842 {
843 nr_return_if_fail (item != NULL);
844 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
846 item->visible = visible;
848 nr_arena_item_request_render (item);
849 }
851 void
852 nr_arena_item_set_clip (NRArenaItem *item, NRArenaItem *clip)
853 {
854 nr_return_if_fail (item != NULL);
855 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
856 nr_return_if_fail (!clip || NR_IS_ARENA_ITEM (clip));
858 if (clip != item->clip) {
859 nr_arena_item_request_render (item);
860 if (item->clip) item->clip = nr_arena_item_detach_unref (item, item->clip);
861 if (clip) item->clip = nr_arena_item_attach_ref (item, clip, NULL, NULL);
862 nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
863 }
864 }
866 void
867 nr_arena_item_set_mask (NRArenaItem *item, NRArenaItem *mask)
868 {
869 nr_return_if_fail (item != NULL);
870 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
871 nr_return_if_fail (!mask || NR_IS_ARENA_ITEM (mask));
873 if (mask != item->mask) {
874 nr_arena_item_request_render (item);
875 if (item->mask) item->mask = nr_arena_item_detach_unref (item, item->mask);
876 if (mask) item->mask = nr_arena_item_attach_ref (item, mask, NULL, NULL);
877 nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
878 }
879 }
881 void
882 nr_arena_item_set_order (NRArenaItem *item, int order)
883 {
884 NRArenaItem *children, *child, *ref;
885 int pos;
887 nr_return_if_fail (item != NULL);
888 nr_return_if_fail (NR_IS_ARENA_ITEM (item));
890 if (!item->parent) return;
892 children = nr_arena_item_children (item->parent);
894 ref = NULL;
895 pos = 0;
896 for (child = children; child != NULL; child = child->next) {
897 if (pos >= order) break;
898 if (child != item) {
899 ref = child;
900 pos += 1;
901 }
902 }
904 nr_arena_item_set_child_position (item->parent, item, ref);
905 }
907 /* Helpers */
909 NRArenaItem *
910 nr_arena_item_attach_ref (NRArenaItem *parent, NRArenaItem *child, NRArenaItem *prev, NRArenaItem *next)
911 {
912 nr_return_val_if_fail (parent != NULL, NULL);
913 nr_return_val_if_fail (NR_IS_ARENA_ITEM (parent), NULL);
914 nr_return_val_if_fail (child != NULL, NULL);
915 nr_return_val_if_fail (NR_IS_ARENA_ITEM (child), NULL);
916 nr_return_val_if_fail (child->parent == NULL, NULL);
917 nr_return_val_if_fail (child->prev == NULL, NULL);
918 nr_return_val_if_fail (child->next == NULL, NULL);
919 nr_return_val_if_fail (!prev || NR_IS_ARENA_ITEM (prev), NULL);
920 nr_return_val_if_fail (!prev || (prev->parent == parent), NULL);
921 nr_return_val_if_fail (!prev || (prev->next == next), NULL);
922 nr_return_val_if_fail (!next || NR_IS_ARENA_ITEM (next), NULL);
923 nr_return_val_if_fail (!next || (next->parent == parent), NULL);
924 nr_return_val_if_fail (!next || (next->prev == prev), NULL);
926 child->parent = parent;
927 child->prev = prev;
928 child->next = next;
930 if (prev) prev->next = child;
931 if (next) next->prev = child;
933 return child;
934 }
936 NRArenaItem *
937 nr_arena_item_detach_unref (NRArenaItem *parent, NRArenaItem *child)
938 {
939 NRArenaItem *prev, *next;
941 nr_return_val_if_fail (parent != NULL, NULL);
942 nr_return_val_if_fail (NR_IS_ARENA_ITEM (parent), NULL);
943 nr_return_val_if_fail (child != NULL, NULL);
944 nr_return_val_if_fail (NR_IS_ARENA_ITEM (child), NULL);
945 nr_return_val_if_fail (child->parent == parent, NULL);
947 prev = child->prev;
948 next = child->next;
950 child->parent = NULL;
951 child->prev = NULL;
952 child->next = NULL;
954 if (prev) prev->next = next;
955 if (next) next->prev = prev;
957 return next;
958 }
960 /*
961 *
962 * caches
963 *
964 */
966 #ifdef arena_item_tile_cache
967 typedef struct cache_entry {
968 int key;
969 double score;
970 NRArenaItem* owner;
971 int th,tv;
972 int prev,next;
973 NRPixBlock ipb;
974 bool hasMask;
975 NRPixBlock mpb;
976 } cache_entry;
978 int hash_max=2048,hash_fill=1024;
980 int *keys=NULL;
981 int nbCch=0;
983 int nbEnt=0,maxEnt=0;
984 cache_entry* entries=NULL;
986 //#define tile_cache_stats
987 #ifdef tile_cache_stats
988 double hits=0,misses=0;
989 int hitMissCount=0;
990 #endif
992 int hash_that(NRArenaItem* owner,int th,int tv)
993 {
994 int res=GPOINTER_TO_INT(owner);
995 res*=17;
996 res+=th;
997 res*=59;
998 res+=tv;
999 res*=217;
1000 if ( res < 0 ) res=-res;
1001 res%=hash_max;
1002 return res;
1003 }
1005 bool test_cache(NRArenaItem* owner,int th,int tv,NRPixBlock &ipb,NRPixBlock &mpb,bool &hasMask)
1006 {
1007 if ( keys == NULL ) {
1008 hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1009 hash_fill=(hash_max*3)/4;
1010 keys=(int*)malloc(hash_max*sizeof(int));
1011 for (int i=0;i<hash_max;i++) keys[i]=-1;
1012 }
1013 int key=hash_that(owner,th,tv);
1014 if ( keys[key] < 0 ) {
1015 #ifdef tile_cache_stats
1016 misses+=1.0;
1017 #endif
1018 return false;
1019 }
1020 int cur=keys[key];
1021 while ( cur >= 0 && cur < nbEnt ) {
1022 if ( entries[cur].owner == owner && entries[cur].th == th && entries[cur].tv == tv ) {
1023 hasMask=entries[cur].hasMask;
1024 ipb=entries[cur].ipb;
1025 mpb=entries[cur].mpb;
1026 #ifdef tile_cache_stats
1027 hits+=1.0;
1028 #endif
1029 return true;
1030 }
1031 cur=entries[cur].next;
1032 }
1033 #ifdef tile_cache_stats
1034 misses+=1.0;
1035 #endif
1036 return false;
1037 }
1038 void remove_one_cache(int no)
1039 {
1040 if ( no < 0 || no >= nbEnt ) return;
1042 nr_pixblock_release(&entries[no].ipb);
1043 if ( entries[no].hasMask ) nr_pixblock_release(&entries[no].mpb);
1045 if ( entries[no].prev >= 0 ) entries[entries[no].prev].next=entries[no].next;
1046 if ( entries[no].next >= 0 ) entries[entries[no].next].prev=entries[no].prev;
1047 if ( entries[no].prev < 0 ) keys[entries[no].key]=entries[no].next;
1048 entries[no].prev=entries[no].next=entries[no].key=-1;
1050 if ( no == nbEnt-1 ) {
1051 nbEnt--;
1052 return;
1053 }
1054 entries[no]=entries[--nbEnt];
1055 if ( entries[no].prev >= 0 ) entries[entries[no].prev].next=no;
1056 if ( entries[no].next >= 0 ) entries[entries[no].next].prev=no;
1057 if ( entries[no].prev < 0 ) keys[entries[no].key]=no;
1058 }
1059 void remove_caches(NRArenaItem* owner)
1060 {
1061 if ( keys == NULL ) {
1062 hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1063 hash_fill=(hash_max*3)/4;
1064 keys=(int*)malloc(hash_max*sizeof(int));
1065 for (int i=0;i<hash_max;i++) keys[i]=-1;
1066 }
1067 for (int i=nbEnt-1;i>=0;i--) {
1068 if ( entries[i].owner == owner ) {
1069 remove_one_cache(i);
1070 }
1071 }
1072 }
1073 void age_cache(void)
1074 {
1075 for (int i=0;i<nbEnt;i++) entries[i].score*=0.95;
1076 }
1077 bool insert_cache(NRArenaItem* owner,int th,int tv,NRPixBlock *ipb,NRPixBlock *mpb,double activity,double duration)
1078 {
1079 if ( keys == NULL ) {
1080 hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1081 hash_fill=(hash_max*3)/4;
1082 keys=(int*)malloc(hash_max*sizeof(int));
1083 for (int i=0;i<hash_max;i++) keys[i]=-1;
1084 }
1085 for (int i=0;i<nbEnt;i++) entries[i].score*=0.95;
1086 #ifdef tile_cache_stats
1087 hits*=0.95;
1088 misses*=0.95;
1089 hitMissCount++;
1090 if ( hitMissCount > 100 ) {
1091 hitMissCount=0;
1092 printf("hit/miss = %f used/total=%i/%i\n",(misses>0.001)?hits/misses:100000.0,nbEnt,hash_max); // localizing ok
1093 }
1094 #endif
1095 int key=hash_that(owner,th,tv);
1096 double nScore=/*activity**/duration;
1098 if ( keys[key] >= 0 ) {
1099 int cur=keys[key];
1100 while ( cur >= 0 && cur < nbEnt ) {
1101 if ( entries[cur].owner == owner && entries[cur].th == th && entries[cur].tv == tv ) {
1102 remove_one_cache(cur);
1103 break;
1104 }
1105 cur=entries[cur].next;
1106 }
1107 }
1109 bool doAdd=false;
1110 if ( nbEnt < hash_fill ) {
1111 doAdd=true;
1112 } else {
1113 double worstS=entries[0].score;
1114 int worstE=0;
1115 for (int i=1;i<nbEnt;i++) {
1116 if ( entries[i].score < worstS ) {
1117 worstS=entries[i].score;
1118 worstE=i;
1119 }
1120 }
1121 if ( worstS < nScore ) {
1122 doAdd=true;
1123 remove_one_cache(worstE);
1124 }
1125 }
1126 if ( doAdd == false ) return false;
1127 if ( nbEnt >= maxEnt ) {
1128 maxEnt=2*nbEnt+1;
1129 entries=(cache_entry*)realloc(entries,maxEnt*sizeof(cache_entry));
1130 }
1131 entries[nbEnt].key=key;
1132 entries[nbEnt].score=nScore;
1133 entries[nbEnt].owner=owner;
1134 entries[nbEnt].th=th;
1135 entries[nbEnt].tv=tv;
1136 entries[nbEnt].prev=entries[nbEnt].next=-1;
1137 entries[nbEnt].ipb=*ipb;
1138 if ( mpb ) {
1139 entries[nbEnt].hasMask=true;
1140 entries[nbEnt].mpb=*mpb;
1141 } else {
1142 entries[nbEnt].hasMask=false;
1143 }
1144 entries[nbEnt].next=keys[key];
1145 if ( entries[nbEnt].next >= 0 ) entries[entries[nbEnt].next].prev=nbEnt;
1146 keys[key]=nbEnt;
1148 nbEnt++;
1149 return true;
1150 }
1151 #endif