Code

Rendering optimisation, which gives best results for zoomed in drawings with complex...
[inkscape.git] / src / display / nr-arena-item.cpp
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 static void
67 nr_arena_item_init (NRArenaItem *item)
68 {
69         item->arena = NULL;
70         item->parent = NULL;
71         item->next = item->prev = NULL;
73         item->key = 0;
75         item->state = 0;
76         item->sensitive = TRUE;
77         item->visible = TRUE;
79         memset(&item->bbox, 0, sizeof(item->bbox));
80         item->transform = NULL;
81         item->opacity = 255;
82         item->render_opacity = FALSE;
84 #ifdef arena_item_tile_cache
85   item->activity=0.0;
86   item->skipCaching=false;
87 #endif
89         item->transform = NULL;
90         item->clip = NULL;
91         item->mask = NULL;
92         item->px = NULL;
93         item->data = NULL;
94 }
96 static void
97 nr_arena_item_private_finalize (NRObject *object)
98 {
99         NRArenaItem *item=static_cast<NRArenaItem *>(object);
101 #ifdef arena_item_tile_cache
102   remove_caches(item);
103 #endif
105         if (item->px) {
106                 nr_free (item->px);
107         }
109         if (item->transform) {
110                 nr_free (item->transform);
111         }
113         ((NRObjectClass *) (parent_class))->finalize (object);
116 NRArenaItem *
117 nr_arena_item_children (NRArenaItem *item)
119         nr_return_val_if_fail (item != NULL, NULL);
120         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
122         if (NR_ARENA_ITEM_VIRTUAL (item, children))
123                 return NR_ARENA_ITEM_VIRTUAL (item, children) (item);
125         return NULL;
128 NRArenaItem *
129 nr_arena_item_last_child (NRArenaItem *item)
131         nr_return_val_if_fail (item != NULL, NULL);
132         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
134         if (NR_ARENA_ITEM_VIRTUAL (item, last_child)) {
135                 return NR_ARENA_ITEM_VIRTUAL (item, last_child) (item);
136         } else {
137                 NRArenaItem *ref;
138                 ref = nr_arena_item_children (item);
139                 if (ref) while (ref->next) ref = ref->next;
140                 return ref;
141         }
144 void
145 nr_arena_item_add_child (NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref)
147         nr_return_if_fail (item != NULL);
148         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
149         nr_return_if_fail (child != NULL);
150         nr_return_if_fail (NR_IS_ARENA_ITEM (child));
151         nr_return_if_fail (child->parent == NULL);
152         nr_return_if_fail (child->prev == NULL);
153         nr_return_if_fail (child->next == NULL);
154         nr_return_if_fail (child->arena == item->arena);
155         nr_return_if_fail (child != ref);
156         nr_return_if_fail (!ref || NR_IS_ARENA_ITEM (ref));
157         nr_return_if_fail (!ref || (ref->parent == item));
159         if (NR_ARENA_ITEM_VIRTUAL (item, add_child))
160                 NR_ARENA_ITEM_VIRTUAL (item, add_child) (item, child, ref);
163 void
164 nr_arena_item_remove_child (NRArenaItem *item, NRArenaItem *child)
166         nr_return_if_fail (item != NULL);
167         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
168         nr_return_if_fail (child != NULL);
169         nr_return_if_fail (NR_IS_ARENA_ITEM (child));
170         nr_return_if_fail (child->parent == item);
172         if (NR_ARENA_ITEM_VIRTUAL (item, remove_child))
173                 NR_ARENA_ITEM_VIRTUAL (item, remove_child) (item, child);
176 void
177 nr_arena_item_set_child_position (NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref)
179         nr_return_if_fail (item != NULL);
180         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
181         nr_return_if_fail (child != NULL);
182         nr_return_if_fail (NR_IS_ARENA_ITEM (child));
183         nr_return_if_fail (child->parent == item);
184         nr_return_if_fail (!ref || NR_IS_ARENA_ITEM (ref));
185         nr_return_if_fail (!ref || (ref->parent == item));
187         if (NR_ARENA_ITEM_VIRTUAL (item, set_child_position))
188                 NR_ARENA_ITEM_VIRTUAL (item, set_child_position) (item, child, ref);
191 NRArenaItem *
192 nr_arena_item_ref (NRArenaItem *item)
194         nr_object_ref ((NRObject *) item);
196         return item;
199 NRArenaItem *
200 nr_arena_item_unref (NRArenaItem *item)
202         nr_object_unref ((NRObject *) item);
204         return NULL;
207 unsigned int
208 nr_arena_item_invoke_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset)
210         NRGC childgc(gc);
212         nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
213         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NR_ARENA_ITEM_STATE_INVALID);
214         nr_return_val_if_fail (!(state & NR_ARENA_ITEM_STATE_INVALID), NR_ARENA_ITEM_STATE_INVALID);
216 #ifdef NR_ARENA_ITEM_DEBUG_CASCADE
217         printf ("Update %s:%p %x %x %x\n", nr_type_name_from_instance ((GTypeInstance *) item), item, state, item->state, reset);
218 #endif
220         /* return if in error */
221         if (item->state & NR_ARENA_ITEM_STATE_INVALID) return item->state;
222         /* Set reset flags according to propagation status */
223         if (item->propagate) {
224                 reset |= ~item->state;
225                 item->propagate = FALSE;
226         }
227         /* Reset our state */
228         item->state &= ~reset;
229         /* Return if NOP */
230         if (!(~item->state & state)) return item->state;
231         /* Test whether to return immediately */
232         if (area && (item->state & NR_ARENA_ITEM_STATE_BBOX)) {
233                 if (!nr_rect_l_test_intersect (area, &item->bbox)) return item->state;
234         }
236         /* Reset image cache, if not to be kept */
237         if (!(item->state & NR_ARENA_ITEM_STATE_IMAGE) && (item->px)) {
238                 nr_free (item->px);
239                 item->px = NULL;
240         }
241 #ifdef arena_item_tile_cache
242   remove_caches(item);
243 #endif
245         /* Set up local gc */
246         childgc = *gc;
247         if (item->transform) {
248                 nr_matrix_multiply (&childgc.transform, item->transform, &childgc.transform);
249         }
251         /* Invoke the real method */
252         item->state = NR_ARENA_ITEM_VIRTUAL (item, update) (item, area, &childgc, state, reset);
253         if (item->state & NR_ARENA_ITEM_STATE_INVALID) return item->state;
254         /* Clipping */
255         if (item->clip) {
256                 unsigned int newstate;
257                 newstate = nr_arena_item_invoke_update (item->clip, area, &childgc, state, reset);
258                 if (newstate & NR_ARENA_ITEM_STATE_INVALID) {
259                         item->state |= NR_ARENA_ITEM_STATE_INVALID;
260                         return item->state;
261                 }
262                 nr_rect_l_intersect (&item->bbox, &item->bbox, &item->clip->bbox);
263         }
264         /* Masking */
265         if (item->mask) {
266                 unsigned int newstate;
267                 newstate = nr_arena_item_invoke_update (item->mask, area, &childgc, state, reset);
268                 if (newstate & NR_ARENA_ITEM_STATE_INVALID) {
269                         item->state |= NR_ARENA_ITEM_STATE_INVALID;
270                         return item->state;
271                 }
272                 nr_rect_l_intersect (&item->bbox, &item->bbox, &item->mask->bbox);
273         }
275         return item->state;
278 /**
279  *    Render item to pixblock.
280  *
281  *    \return Has NR_ARENA_ITEM_STATE_RENDER set on success.
282  */
284 unsigned int nr_arena_item_invoke_render(NRArenaItem *item, NRRectL const *area, NRPixBlock *pb, unsigned int flags)
286         NRRectL carea;
287         NRPixBlock *dpb;
288         NRPixBlock cpb;
289         unsigned int state;
291         nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
292         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NR_ARENA_ITEM_STATE_INVALID);
293         nr_return_val_if_fail (item->state & NR_ARENA_ITEM_STATE_BBOX, item->state);
295 #ifdef NR_ARENA_ITEM_VERBOSE
296         printf ("Invoke render %p: %d %d - %d %d\n", item, area->x0, area->y0, area->x1, area->y1);
297 #endif
299 #ifdef arena_item_tile_cache
300   item->activity*=0.5;
301 #endif
303         /* If we are outside bbox just return successfully */
304         if (!item->visible) return item->state | NR_ARENA_ITEM_STATE_RENDER;
305         nr_rect_l_intersect (&carea, area, &item->bbox);
306         if (nr_rect_l_test_empty (&carea)) return item->state | NR_ARENA_ITEM_STATE_RENDER;
308         if (item->px) {
309                 /* Has cache pixblock, render this and return */
310                 nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P,
311                                           /* fixme: This probably cannot overflow, because we render only if visible */
312                                           /* fixme: and pixel cache is there only for small items */
313                                           /* fixme: But this still needs extra check (Lauris) */
314                                           item->bbox.x0, item->bbox.y0,
315                                           item->bbox.x1, item->bbox.y1,
316                                           item->px, 4 * (item->bbox.x1 - item->bbox.x0), FALSE, FALSE);
317                 nr_blit_pixblock_pixblock (pb, &cpb);
318                 nr_pixblock_release (&cpb);
319                 pb->empty = FALSE;
320                 return item->state | NR_ARENA_ITEM_STATE_RENDER;
321         }
323         dpb = pb;
324   bool  canCache=false;
325 #ifdef arena_item_tile_cache  
326   bool checkCache=false;
327   int   tile_h=0,tile_v=0;
328 #endif  
329         /* Setup cache if we can */
330         if ((!(flags & NR_ARENA_ITEM_RENDER_NO_CACHE)) &&
331             (carea.x0 <= item->bbox.x0) && (carea.y0 <= item->bbox.y0) &&
332             (carea.x1 >= item->bbox.x1) && (carea.y1 >= item->bbox.y1) &&
333             (((item->bbox.x1 - item->bbox.x0) * (item->bbox.y1 - item->bbox.y0)) <= 4096)) {
334                 // Item bbox is fully in renderable area and size is acceptable
335                 carea.x0 = item->bbox.x0;
336                 carea.y0 = item->bbox.y0;
337                 carea.x1 = item->bbox.x1;
338                 carea.y1 = item->bbox.y1;
339                 item->px = nr_new (unsigned char, 4 * (carea.x1 - carea.x0) * (carea.y1 - carea.y0));
340                 nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P,
341                               carea.x0, carea.y0, carea.x1, carea.y1,
342                               item->px, 4 * (carea.x1 - carea.x0), TRUE, TRUE);
343         cpb.visible_area = pb->visible_area; 
344                 dpb = &cpb;
345                 // Set nocache flag for downstream rendering
346                 flags |= NR_ARENA_ITEM_RENDER_NO_CACHE;
347         } else {
348 #ifdef arena_item_tile_cache
349     if ( item->skipCaching ) {
350     } else {
351       int tl=area->x0&(~127);
352       int tt=area->y0&(~127);
353       if ( area->x1 <= tl+128 && area->y1 <= tt+128 ) {
354         checkCache=true;
355         tile_h=tl/128;
356         tile_v=tt/128;
357         int surf=(area->x1-area->x0)*(area->y1-area->y0);
358         if ( surf >= 4096 ) {
359           canCache=true;
360           carea.x0=tl;
361           carea.y0=tt;
362           carea.x1=tl+128;
363           carea.y1=tt+128;
364         }
365       }
366     }
367 #endif
368   }
370 #ifdef arena_item_tile_cache
371   item->activity+=1.0;
372 #endif
374 #ifdef arena_item_tile_cache
375   if ( checkCache ) {
376     NRPixBlock ipb, mpb;
377     bool       hasMask;
378     if ( test_cache(item,tile_h,tile_v,ipb,mpb,hasMask) ) {
379       // youpi! c'etait deja cache
380       if ( hasMask ) {
381         nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
382       } else if ( ((item->opacity != 255) && !item->render_opacity) ) {
383         nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
384       } else {
385         nr_blit_pixblock_pixblock (pb, &ipb);
386       }
387       pb->empty = FALSE;
388       return item->state | NR_ARENA_ITEM_STATE_RENDER;
389     }
390   }
391 #endif
392   if ( canCache ) {
393 #ifdef arena_item_tile_cache
394     // nota: exclusif de dpb != pb, donc pas de cas particulier a la fin
395     NRPixBlock ipb, mpb;
397     // struct timeval start_time,end_time;
398     // gettimeofday(&start_time,NULL);
399     GTimeVal start_time,end_time;
400     g_get_current_time (&start_time);
401     int    duration=0;
403     /* Setup and render item buffer */
404     nr_pixblock_setup_fast (&ipb, NR_PIXBLOCK_MODE_R8G8B8A8P, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
405     ipb.visible_area = pb->visible_area; 
406     state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, &ipb, flags);
407     if (state & NR_ARENA_ITEM_STATE_INVALID) {
408       /* Clean up and return error */
409       nr_pixblock_release (&ipb);
410       if (dpb != pb) nr_pixblock_release (dpb);
411       item->state |= NR_ARENA_ITEM_STATE_INVALID;
412       return item->state;
413     }
414     ipb.empty = FALSE;
416     if (item->clip || item->mask) {
417       /* Setup mask pixblock */
418       nr_pixblock_setup_fast (&mpb, NR_PIXBLOCK_MODE_A8, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
419       mpb.visible_area = pb->visible_area; 
420       /* Do clip if needed */
421       if (item->clip) {
422         state = nr_arena_item_invoke_clip (item->clip, &carea, &mpb);
423         if (state & NR_ARENA_ITEM_STATE_INVALID) {
424           /* Clean up and return error */
425           nr_pixblock_release (&mpb);
426           nr_pixblock_release (&ipb);
427           if (dpb != pb) nr_pixblock_release (dpb);
428           item->state |= NR_ARENA_ITEM_STATE_INVALID;
429           return item->state;
430         }
431         mpb.empty = FALSE;
432       }
433       /* Do mask if needed */
434       if (item->mask) {
435         NRPixBlock tpb;
436         /* Set up yet another temporary pixblock */
437         nr_pixblock_setup_fast (&tpb, NR_PIXBLOCK_MODE_R8G8B8A8N, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
438         tpb.visible_area = pb->visible_area; 
439         state = NR_ARENA_ITEM_VIRTUAL (item->mask, render) (item->mask, &carea, &tpb, flags);
440         if (state & NR_ARENA_ITEM_STATE_INVALID) {
441           /* Clean up and return error */
442           nr_pixblock_release (&tpb);
443           nr_pixblock_release (&mpb);
444           nr_pixblock_release (&ipb);
445           if (dpb != pb) nr_pixblock_release (dpb);
446           item->state |= NR_ARENA_ITEM_STATE_INVALID;
447           return item->state;
448         }
449         /* Composite with clip */
450         if (item->clip) {
451           int x, y;
452           for (y = carea.y0; y < carea.y1; y++) {
453             unsigned char *s, *d;
454             s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
455             d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
456             for (x = carea.x0; x < carea.x1; x++) {
457               unsigned int m;
458               m = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
459               d[0] = NR_PREMUL (d[0], m);
460               s += 4;
461               d += 1;
462             }
463           }
464         } else {
465           int x, y;
466           for (y = carea.y0; y < carea.y1; y++) {
467             unsigned char *s, *d;
468             s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
469             d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
470             for (x = carea.x0; x < carea.x1; x++) {
471               unsigned int m;
472               m = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
473               d[0] = m;
474               s += 4;
475               d += 1;
476             }
477           }
478           mpb.empty = FALSE;
479         }
480         nr_pixblock_release (&tpb);
481       }
482       /* Multiply with opacity if needed */
483       if ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE) {
484         int x, y;
485         unsigned int a;
486         a = item->opacity;
487         for (y = carea.y0; y < carea.y1; y++) {
488           unsigned char *d;
489           d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
490           for (x = carea.x0; x < carea.x1; x++) {
491             d[0] = NR_PREMUL (d[0], a);
492             d += 1;
493           }
494         }
495       }
496       /* Compose rendering pixblock int destination */
497       // gettimeofday(&end_time,NULL);
498       g_get_current_time (&end_time);
499       duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
500       if ( !(ipb.empty) ) {
501         nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
502         if ( insert_cache(item,tile_h,tile_v,&ipb,&mpb,item->activity,(double)duration) ) {
503         } else {
504           nr_pixblock_release (&mpb);
505           nr_pixblock_release (&ipb);
506         }
507         dpb->empty = FALSE;
508       } else {
509         nr_pixblock_release (&ipb);
510       }
511     } else if ( ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE) ) {
512       /* Opacity only */
513       // gettimeofday(&end_time,NULL);
514       g_get_current_time (&end_time);
515       duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
516       if ( !(ipb.empty) ) {
517         nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
518         if ( insert_cache(item,tile_h,tile_v,&ipb,NULL,item->activity,(double)duration) ) {
519         } else {
520           nr_pixblock_release (&ipb);
521         }
522         dpb->empty = FALSE;
523       } else {
524         nr_pixblock_release (&ipb);
525       }
526     } else {
527       // gettimeofday(&end_time,NULL);
528       g_get_current_time (&end_time);
529       duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
530       if ( !(ipb.empty) ) {
531         nr_blit_pixblock_pixblock (dpb, &ipb);
532         if ( insert_cache(item,tile_h,tile_v,&ipb,NULL,item->activity,(double)duration) ) {
533         } else {
534           nr_pixblock_release (&ipb);
535         }
536         dpb->empty = FALSE;
537       } else {
538         nr_pixblock_release (&ipb);
539       }
540     }
541 #endif
542   } else {
543     /* Determine, whether we need temporary buffer */
544     if (item->clip || item->mask || ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE)) {
545       NRPixBlock ipb, mpb;
547       /* Setup and render item buffer */
548       nr_pixblock_setup_fast (&ipb, NR_PIXBLOCK_MODE_R8G8B8A8P, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
549       ipb.visible_area = pb->visible_area; 
550       state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, &ipb, flags);
551       if (state & NR_ARENA_ITEM_STATE_INVALID) {
552         /* Clean up and return error */
553         nr_pixblock_release (&ipb);
554         if (dpb != pb) nr_pixblock_release (dpb);
555         item->state |= NR_ARENA_ITEM_STATE_INVALID;
556         return item->state;
557       }
558       ipb.empty = FALSE;
560       if (item->clip || item->mask) {
561         /* Setup mask pixblock */
562         nr_pixblock_setup_fast (&mpb, NR_PIXBLOCK_MODE_A8, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
563         mpb.visible_area = pb->visible_area; 
564         /* Do clip if needed */
565         if (item->clip) {
566           state = nr_arena_item_invoke_clip (item->clip, &carea, &mpb);
567           if (state & NR_ARENA_ITEM_STATE_INVALID) {
568             /* Clean up and return error */
569             nr_pixblock_release (&mpb);
570             nr_pixblock_release (&ipb);
571             if (dpb != pb) nr_pixblock_release (dpb);
572             item->state |= NR_ARENA_ITEM_STATE_INVALID;
573             return item->state;
574           }
575           mpb.empty = FALSE;
576         }
577         /* Do mask if needed */
578         if (item->mask) {
579           NRPixBlock tpb;
580           /* Set up yet another temporary pixblock */
581           nr_pixblock_setup_fast (&tpb, NR_PIXBLOCK_MODE_R8G8B8A8N, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
582           tpb.visible_area = pb->visible_area; 
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;
672 unsigned int
673 nr_arena_item_invoke_clip (NRArenaItem *item, NRRectL *area, NRPixBlock *pb)
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 clip by %p: %d %d - %d %d, item bbox %d %d - %d %d\n", item, area->x0, area->y0, area->x1, area->y1, (&item->bbox)->x0, (&item->bbox)->y0, (&item->bbox)->x1, (&item->bbox)->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                 }
693         }
695         return item->state;
698 NRArenaItem *
699 nr_arena_item_invoke_pick (NRArenaItem *item, NR::Point p, double delta, unsigned int sticky)
701         nr_return_val_if_fail (item != NULL, NULL);
702         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
704         // Sometimes there's no BBOX in item->state, reason unknown (bug 992817); I made this not an assert to remove the warning
705         if (!(item->state & NR_ARENA_ITEM_STATE_BBOX) || !(item->state & NR_ARENA_ITEM_STATE_PICK))
706                 return NULL;
708         if (!sticky && !(item->visible && item->sensitive)) return NULL;
709         
710         // TODO: rewrite using NR::Rect
711         const double x = p[NR::X];
712         const double y = p[NR::Y];
713         
714         if (((x + delta) >= item->bbox.x0) &&
715             ((x - delta) <  item->bbox.x1) &&
716             ((y + delta) >= item->bbox.y0) &&
717             ((y - delta) <  item->bbox.y1)) {
718                 if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->pick)
719                         return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->pick (item, p, delta, sticky);
720         }
722         return NULL;
725 void
726 nr_arena_item_request_update (NRArenaItem *item, unsigned int reset, unsigned int propagate)
728         nr_return_if_fail (item != NULL);
729         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
730         nr_return_if_fail (!(reset & NR_ARENA_ITEM_STATE_INVALID));
732         if (propagate && !item->propagate) item->propagate = TRUE;
734         if (item->state & reset) {
735                 item->state &= ~reset;
736                 if (item->parent) {
737                         nr_arena_item_request_update (item->parent, reset, FALSE);
738                 } else {
739                         nr_arena_request_update (item->arena, item);
740                 }
741         }
744 void
745 nr_arena_item_request_render (NRArenaItem *item)
747         nr_return_if_fail (item != NULL);
748         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
750         nr_arena_request_render_rect (item->arena, &item->bbox);
753 /* Public */
755 NRArenaItem *
756 nr_arena_item_unparent (NRArenaItem *item)
758         nr_return_val_if_fail (item != NULL, NULL);
759         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
761         nr_arena_item_request_render (item);
763         if (item->parent) {
764                 nr_arena_item_remove_child (item->parent, item);
765         }
767         return NULL;
770 void
771 nr_arena_item_append_child (NRArenaItem *parent, NRArenaItem *child)
773         nr_return_if_fail (parent != NULL);
774         nr_return_if_fail (NR_IS_ARENA_ITEM (parent));
775         nr_return_if_fail (child != NULL);
776         nr_return_if_fail (NR_IS_ARENA_ITEM (child));
777         nr_return_if_fail (parent->arena == child->arena);
778         nr_return_if_fail (child->parent == NULL);
779         nr_return_if_fail (child->prev == NULL);
780         nr_return_if_fail (child->next == NULL);
782         nr_arena_item_add_child (parent, child, nr_arena_item_last_child (parent));
785 void
786 nr_arena_item_set_transform(NRArenaItem *item, NR::Matrix const &transform)
788         NRMatrix const t(transform);
789         nr_arena_item_set_transform(item, &t);
792 void
793 nr_arena_item_set_transform(NRArenaItem *item, NRMatrix const *transform)
795         const NRMatrix *ms, *md;
797         nr_return_if_fail (item != NULL);
798         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
800         if (!transform && !item->transform) return;
802         md = (item->transform) ? item->transform : &NR_MATRIX_IDENTITY;
803         ms = (transform) ? transform : &NR_MATRIX_IDENTITY;
805         if (!NR_MATRIX_DF_TEST_CLOSE (md, ms, NR_EPSILON)) {
806                 nr_arena_item_request_render (item);
807                 if (!transform || nr_matrix_test_identity (transform, NR_EPSILON)) {
808                         /* Set to identity affine */
809                         if (item->transform) nr_free (item->transform);
810                         item->transform = NULL;
811                 } else {
812                         if (!item->transform) item->transform = nr_new (NRMatrix, 1);
813                         *item->transform = *transform;
814                 }
815                 nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
816         }
819 void
820 nr_arena_item_set_opacity (NRArenaItem *item, double opacity)
822         nr_return_if_fail (item != NULL);
823         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
825         nr_arena_item_request_render (item);
827         item->opacity = (unsigned int) (opacity * 255.9999);
830 void
831 nr_arena_item_set_sensitive (NRArenaItem *item, unsigned int sensitive)
833         nr_return_if_fail (item != NULL);
834         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
836         /* fixme: mess with pick/repick... */
838         item->sensitive = sensitive;
841 void
842 nr_arena_item_set_visible (NRArenaItem *item, unsigned int visible)
844         nr_return_if_fail (item != NULL);
845         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
847         item->visible = visible;
849         nr_arena_item_request_render (item);
852 void
853 nr_arena_item_set_clip (NRArenaItem *item, NRArenaItem *clip)
855         nr_return_if_fail (item != NULL);
856         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
857         nr_return_if_fail (!clip || NR_IS_ARENA_ITEM (clip));
859         if (clip != item->clip) {
860                 nr_arena_item_request_render (item);
861                 if (item->clip) item->clip = nr_arena_item_detach_unref (item, item->clip);
862                 if (clip) item->clip = nr_arena_item_attach_ref (item, clip, NULL, NULL);
863                 nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
864         }
867 void
868 nr_arena_item_set_mask (NRArenaItem *item, NRArenaItem *mask)
870         nr_return_if_fail (item != NULL);
871         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
872         nr_return_if_fail (!mask || NR_IS_ARENA_ITEM (mask));
874         if (mask != item->mask) {
875                 nr_arena_item_request_render (item);
876                 if (item->mask) item->mask = nr_arena_item_detach_unref (item, item->mask);
877                 if (mask) item->mask = nr_arena_item_attach_ref (item, mask, NULL, NULL);
878                 nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
879         }
882 void
883 nr_arena_item_set_order (NRArenaItem *item, int order)
885         NRArenaItem *children, *child, *ref;
886         int pos;
888         nr_return_if_fail (item != NULL);
889         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
891         if (!item->parent) return;
893         children = nr_arena_item_children (item->parent);
895         ref = NULL;
896         pos = 0;
897         for (child = children; child != NULL; child = child->next) {
898                 if (pos >= order) break;
899                 if (child != item) {
900                         ref = child;
901                         pos += 1;
902                 }
903         }
905         nr_arena_item_set_child_position (item->parent, item, ref);
908 /* Helpers */
910 NRArenaItem *
911 nr_arena_item_attach_ref (NRArenaItem *parent, NRArenaItem *child, NRArenaItem *prev, NRArenaItem *next)
913         nr_return_val_if_fail (parent != NULL, NULL);
914         nr_return_val_if_fail (NR_IS_ARENA_ITEM (parent), NULL);
915         nr_return_val_if_fail (child != NULL, NULL);
916         nr_return_val_if_fail (NR_IS_ARENA_ITEM (child), NULL);
917         nr_return_val_if_fail (child->parent == NULL, NULL);
918         nr_return_val_if_fail (child->prev == NULL, NULL);
919         nr_return_val_if_fail (child->next == NULL, NULL);
920         nr_return_val_if_fail (!prev || NR_IS_ARENA_ITEM (prev), NULL);
921         nr_return_val_if_fail (!prev || (prev->parent == parent), NULL);
922         nr_return_val_if_fail (!prev || (prev->next == next), NULL);
923         nr_return_val_if_fail (!next || NR_IS_ARENA_ITEM (next), NULL);
924         nr_return_val_if_fail (!next || (next->parent == parent), NULL);
925         nr_return_val_if_fail (!next || (next->prev == prev), NULL);
927         child->parent = parent;
928         child->prev = prev;
929         child->next = next;
931         if (prev) prev->next = child;
932         if (next) next->prev = child;
934         return child;
937 NRArenaItem *
938 nr_arena_item_detach_unref (NRArenaItem *parent, NRArenaItem *child)
940         NRArenaItem *prev, *next;
942         nr_return_val_if_fail (parent != NULL, NULL);
943         nr_return_val_if_fail (NR_IS_ARENA_ITEM (parent), NULL);
944         nr_return_val_if_fail (child != NULL, NULL);
945         nr_return_val_if_fail (NR_IS_ARENA_ITEM (child), NULL);
946         nr_return_val_if_fail (child->parent == parent, NULL);
948         prev = child->prev;
949         next = child->next;
951         child->parent = NULL;
952         child->prev = NULL;
953         child->next = NULL;
955         if (prev) prev->next = next;
956         if (next) next->prev = prev;
958         return next;
961 /*
962  *
963  * caches
964  *
965  */
967 #ifdef arena_item_tile_cache
968 typedef struct cache_entry {
969   int             key;
970   double          score;
971   NRArenaItem*    owner;
972   int             th,tv;
973   int             prev,next;
974   NRPixBlock      ipb;
975   bool            hasMask;
976   NRPixBlock      mpb;
977 } cache_entry;
979 int hash_max=2048,hash_fill=1024;
981 int            *keys=NULL;
982 int            nbCch=0;
984 int            nbEnt=0,maxEnt=0;
985 cache_entry*   entries=NULL;
987 //#define tile_cache_stats
988 #ifdef tile_cache_stats
989 double         hits=0,misses=0;
990 int            hitMissCount=0;
991 #endif
993 int hash_that(NRArenaItem* owner,int th,int tv)
995   int res=GPOINTER_TO_INT(owner);
996   res*=17;
997   res+=th;
998   res*=59;
999   res+=tv;
1000   res*=217;
1001   if ( res < 0 ) res=-res;
1002   res%=hash_max;
1003   return res;
1006 bool  test_cache(NRArenaItem* owner,int th,int tv,NRPixBlock &ipb,NRPixBlock &mpb,bool &hasMask)
1008   if ( keys == NULL ) {
1009     hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1010     hash_fill=(hash_max*3)/4;
1011     keys=(int*)malloc(hash_max*sizeof(int));
1012     for (int i=0;i<hash_max;i++) keys[i]=-1;
1013   }
1014   int key=hash_that(owner,th,tv);
1015   if ( keys[key] < 0 ) {
1016 #ifdef tile_cache_stats
1017     misses+=1.0;
1018 #endif
1019     return false;
1020   }
1021   int cur=keys[key];
1022   while ( cur >= 0 && cur < nbEnt ) {
1023     if ( entries[cur].owner == owner && entries[cur].th == th && entries[cur].tv == tv ) {
1024       hasMask=entries[cur].hasMask;
1025       ipb=entries[cur].ipb;
1026       mpb=entries[cur].mpb;
1027 #ifdef tile_cache_stats
1028       hits+=1.0;
1029 #endif
1030       return true;
1031     }
1032     cur=entries[cur].next;
1033   }
1034 #ifdef tile_cache_stats
1035   misses+=1.0;
1036 #endif
1037   return false;
1039 void  remove_one_cache(int no)
1041   if ( no < 0 || no >= nbEnt ) return;
1043   nr_pixblock_release(&entries[no].ipb);
1044   if ( entries[no].hasMask ) nr_pixblock_release(&entries[no].mpb);
1046   if ( entries[no].prev >= 0 ) entries[entries[no].prev].next=entries[no].next;
1047   if ( entries[no].next >= 0 ) entries[entries[no].next].prev=entries[no].prev;
1048   if ( entries[no].prev < 0 ) keys[entries[no].key]=entries[no].next;
1049   entries[no].prev=entries[no].next=entries[no].key=-1;
1051   if ( no == nbEnt-1 ) {
1052     nbEnt--;
1053     return;
1054   }
1055   entries[no]=entries[--nbEnt];
1056   if ( entries[no].prev >= 0 ) entries[entries[no].prev].next=no;
1057   if ( entries[no].next >= 0 ) entries[entries[no].next].prev=no;
1058   if ( entries[no].prev < 0 ) keys[entries[no].key]=no;
1060 void  remove_caches(NRArenaItem* owner)
1062   if ( keys == NULL ) {
1063     hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1064     hash_fill=(hash_max*3)/4;
1065     keys=(int*)malloc(hash_max*sizeof(int));
1066     for (int i=0;i<hash_max;i++) keys[i]=-1;
1067   }
1068   for (int i=nbEnt-1;i>=0;i--) {
1069     if ( entries[i].owner == owner ) {
1070       remove_one_cache(i);
1071     }
1072   }
1074 void  age_cache(void)
1076   for (int i=0;i<nbEnt;i++) entries[i].score*=0.95;
1078 bool  insert_cache(NRArenaItem* owner,int th,int tv,NRPixBlock *ipb,NRPixBlock *mpb,double activity,double duration)
1080   if ( keys == NULL ) {
1081     hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1082     hash_fill=(hash_max*3)/4;
1083     keys=(int*)malloc(hash_max*sizeof(int));
1084     for (int i=0;i<hash_max;i++) keys[i]=-1;
1085   }
1086   for (int i=0;i<nbEnt;i++) entries[i].score*=0.95;
1087 #ifdef tile_cache_stats
1088   hits*=0.95;
1089   misses*=0.95;
1090   hitMissCount++;
1091   if ( hitMissCount > 100 ) {
1092     hitMissCount=0;
1093     printf("hit/miss = %f  used/total=%i/%i\n",(misses>0.001)?hits/misses:100000.0,nbEnt,hash_max); // localizing ok
1094   }
1095 #endif
1096   int    key=hash_that(owner,th,tv);
1097   double nScore=/*activity**/duration;
1099   if ( keys[key] >= 0 ) {
1100     int cur=keys[key];
1101     while ( cur >= 0 && cur < nbEnt ) {
1102       if ( entries[cur].owner == owner && entries[cur].th == th && entries[cur].tv == tv ) {
1103         remove_one_cache(cur);
1104         break;
1105       }
1106       cur=entries[cur].next;
1107     }
1108   }
1110   bool doAdd=false;
1111   if ( nbEnt < hash_fill ) {
1112     doAdd=true;
1113   } else {
1114     double    worstS=entries[0].score;
1115     int       worstE=0;
1116     for (int i=1;i<nbEnt;i++) {
1117       if ( entries[i].score < worstS ) {
1118         worstS=entries[i].score;
1119         worstE=i;
1120       }
1121     }
1122     if ( worstS < nScore ) {
1123       doAdd=true;
1124       remove_one_cache(worstE);
1125     }
1126   }
1127   if ( doAdd == false ) return false;
1128   if ( nbEnt >= maxEnt ) {
1129     maxEnt=2*nbEnt+1;
1130     entries=(cache_entry*)realloc(entries,maxEnt*sizeof(cache_entry));
1131   }
1132   entries[nbEnt].key=key;
1133   entries[nbEnt].score=nScore;
1134   entries[nbEnt].owner=owner;
1135   entries[nbEnt].th=th;
1136   entries[nbEnt].tv=tv;
1137   entries[nbEnt].prev=entries[nbEnt].next=-1;
1138   entries[nbEnt].ipb=*ipb;
1139   if ( mpb ) {
1140     entries[nbEnt].hasMask=true;
1141     entries[nbEnt].mpb=*mpb;
1142   } else {
1143     entries[nbEnt].hasMask=false;
1144   }
1145   entries[nbEnt].next=keys[key];
1146   if ( entries[nbEnt].next >= 0 ) entries[entries[nbEnt].next].prev=nbEnt;
1147   keys[key]=nbEnt;
1149   nbEnt++;
1150   return true;
1152 #endif