Code

581bee231054d8c8bf5563c72434c7105a0fb741
[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 "gc-core.h"
25 #include "nr-filter.h"
26 #include "libnr/nr-rect.h"
27 #include "nr-arena-group.h"
29 namespace GC = Inkscape::GC;
31 static void nr_arena_item_class_init (NRArenaItemClass *klass);
32 static void nr_arena_item_init (NRArenaItem *item);
33 static void nr_arena_item_private_finalize (NRObject *object);
35 #ifdef arena_item_tile_cache
36 bool  insert_cache(NRArenaItem* owner,int th,int tv,NRPixBlock *ipb,NRPixBlock *mpb,double activity,double duration);
37 void  remove_caches(NRArenaItem* owner);
38 bool  test_cache(NRArenaItem* owner,int th,int tv,NRPixBlock &ipb,NRPixBlock &mpb,bool &hasMask);
39 #endif
41 static NRObjectClass *parent_class;
43 NRType
44 nr_arena_item_get_type (void)
45 {
46         static NRType type = 0;
47         if (!type) {
48                 type = nr_object_register_type (NR_TYPE_OBJECT,
49                                                 "NRArenaItem",
50                                                 sizeof (NRArenaItemClass),
51                                                 sizeof (NRArenaItem),
52                                                 (void (*) (NRObjectClass *)) nr_arena_item_class_init,
53                                                 (void (*) (NRObject *)) nr_arena_item_init);
54         }
55         return type;
56 }
58 static void
59 nr_arena_item_class_init (NRArenaItemClass *klass)
60 {
61         NRObjectClass *object_class;
63         object_class = (NRObjectClass *) klass;
65         parent_class = ((NRObjectClass *) klass)->parent;
67         object_class->finalize = nr_arena_item_private_finalize;
68         object_class->cpp_ctor = NRObject::invoke_ctor<NRArenaItem>;
69 }
71 static void
72 nr_arena_item_init (NRArenaItem *item)
73 {
74         item->arena = NULL;
75         item->parent = NULL;
76         item->next = item->prev = NULL;
78         item->key = 0;
80         item->state = 0;
81         item->sensitive = TRUE;
82         item->visible = TRUE;
84         memset(&item->bbox, 0, sizeof(item->bbox));
85         item->transform = NULL;
86         item->opacity = 255;
87         item->render_opacity = FALSE;
89 #ifdef arena_item_tile_cache
90   item->activity=0.0;
91   item->skipCaching=false;
92 #endif
94         item->transform = NULL;
95         item->clip = NULL;
96         item->mask = NULL;
97         item->px = NULL;
98         item->data = NULL;
99         item->filter = NULL;
100         item->background_pb = NULL;
101         item->background_new = false;
104 static void
105 nr_arena_item_private_finalize (NRObject *object)
107         NRArenaItem *item=static_cast<NRArenaItem *>(object);
109 #ifdef arena_item_tile_cache
110   remove_caches(item);
111 #endif
113         item->px = NULL;
114         item->transform = NULL;
116         ((NRObjectClass *) (parent_class))->finalize (object);
119 NRArenaItem *
120 nr_arena_item_children (NRArenaItem *item)
122         nr_return_val_if_fail (item != NULL, NULL);
123         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
125         if (NR_ARENA_ITEM_VIRTUAL (item, children))
126                 return NR_ARENA_ITEM_VIRTUAL (item, children) (item);
128         return NULL;
131 NRArenaItem *
132 nr_arena_item_last_child (NRArenaItem *item)
134         nr_return_val_if_fail (item != NULL, NULL);
135         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
137         if (NR_ARENA_ITEM_VIRTUAL (item, last_child)) {
138                 return NR_ARENA_ITEM_VIRTUAL (item, last_child) (item);
139         } else {
140                 NRArenaItem *ref;
141                 ref = nr_arena_item_children (item);
142                 if (ref) while (ref->next) ref = ref->next;
143                 return ref;
144         }
147 void
148 nr_arena_item_add_child (NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref)
150         nr_return_if_fail (item != NULL);
151         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
152         nr_return_if_fail (child != NULL);
153         nr_return_if_fail (NR_IS_ARENA_ITEM (child));
154         nr_return_if_fail (child->parent == NULL);
155         nr_return_if_fail (child->prev == NULL);
156         nr_return_if_fail (child->next == NULL);
157         nr_return_if_fail (child->arena == item->arena);
158         nr_return_if_fail (child != ref);
159         nr_return_if_fail (!ref || NR_IS_ARENA_ITEM (ref));
160         nr_return_if_fail (!ref || (ref->parent == item));
162         if (NR_ARENA_ITEM_VIRTUAL (item, add_child))
163                 NR_ARENA_ITEM_VIRTUAL (item, add_child) (item, child, ref);
166 void
167 nr_arena_item_remove_child (NRArenaItem *item, NRArenaItem *child)
169         nr_return_if_fail (item != NULL);
170         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
171         nr_return_if_fail (child != NULL);
172         nr_return_if_fail (NR_IS_ARENA_ITEM (child));
173         nr_return_if_fail (child->parent == item);
175         if (NR_ARENA_ITEM_VIRTUAL (item, remove_child))
176                 NR_ARENA_ITEM_VIRTUAL (item, remove_child) (item, child);
179 void
180 nr_arena_item_set_child_position (NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref)
182         nr_return_if_fail (item != NULL);
183         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
184         nr_return_if_fail (child != NULL);
185         nr_return_if_fail (NR_IS_ARENA_ITEM (child));
186         nr_return_if_fail (child->parent == item);
187         nr_return_if_fail (!ref || NR_IS_ARENA_ITEM (ref));
188         nr_return_if_fail (!ref || (ref->parent == item));
190         if (NR_ARENA_ITEM_VIRTUAL (item, set_child_position))
191                 NR_ARENA_ITEM_VIRTUAL (item, set_child_position) (item, child, ref);
194 NRArenaItem *
195 nr_arena_item_ref (NRArenaItem *item)
197         nr_object_ref ((NRObject *) item);
199         return item;
202 NRArenaItem *
203 nr_arena_item_unref (NRArenaItem *item)
205         nr_object_unref ((NRObject *) item);
207         return NULL;
210 unsigned int
211 nr_arena_item_invoke_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset)
213         NRGC childgc(gc);
215         nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
216         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NR_ARENA_ITEM_STATE_INVALID);
217         nr_return_val_if_fail (!(state & NR_ARENA_ITEM_STATE_INVALID), NR_ARENA_ITEM_STATE_INVALID);
219 #ifdef NR_ARENA_ITEM_DEBUG_CASCADE
220         printf ("Update %s:%p %x %x %x\n", nr_type_name_from_instance ((GTypeInstance *) item), item, state, item->state, reset);
221 #endif
223         /* return if in error */
224         if (item->state & NR_ARENA_ITEM_STATE_INVALID) return item->state;
225         /* Set reset flags according to propagation status */
226         if (item->propagate) {
227                 reset |= ~item->state;
228                 item->propagate = FALSE;
229         }
230         /* Reset our state */
231         item->state &= ~reset;
232         /* Return if NOP */
233         if (!(~item->state & state)) return item->state;
234         /* Test whether to return immediately */
235         if (area && (item->state & NR_ARENA_ITEM_STATE_BBOX)) {
236                 if (!nr_rect_l_test_intersect (area, &item->bbox)) return item->state;
237         }
239         /* Reset image cache, if not to be kept */
240         if (!(item->state & NR_ARENA_ITEM_STATE_IMAGE) && (item->px)) {
241                 item->px = NULL;
242         }
243 #ifdef arena_item_tile_cache
244   remove_caches(item);
245 #endif
247         /* Set up local gc */
248         childgc = *gc;
249         if (item->transform) {
250                 nr_matrix_multiply (&childgc.transform, item->transform, &childgc.transform);
251         }
252         /* Remember the transformation matrix */
253         item->ctm = childgc.transform;
255         /* Invoke the real method */
256         item->state = NR_ARENA_ITEM_VIRTUAL (item, update) (item, area, &childgc, state, reset);
257         if (item->state & NR_ARENA_ITEM_STATE_INVALID) return item->state;
258         /* Enlarge the bounding box to contain filter effects */
259         if(item->filter) {
260           item->filter->bbox_enlarge(item->bbox);
261         }
262             
263         /* Clipping */
264         if (item->clip) {
265                 unsigned int newstate;
266                 newstate = nr_arena_item_invoke_update (item->clip, area, &childgc, state, reset);
267                 if (newstate & NR_ARENA_ITEM_STATE_INVALID) {
268                         item->state |= NR_ARENA_ITEM_STATE_INVALID;
269                         return item->state;
270                 }
271                 nr_rect_l_intersect (&item->bbox, &item->bbox, &item->clip->bbox);
272         }
273         /* Masking */
274         if (item->mask) {
275                 unsigned int newstate;
276                 newstate = nr_arena_item_invoke_update (item->mask, area, &childgc, state, reset);
277                 if (newstate & NR_ARENA_ITEM_STATE_INVALID) {
278                         item->state |= NR_ARENA_ITEM_STATE_INVALID;
279                         return item->state;
280                 }
281                 nr_rect_l_intersect (&item->bbox, &item->bbox, &item->mask->bbox);
282         }
284         return item->state;
287 /**
288  *    Render item to pixblock.
289  *
290  *    \return Has NR_ARENA_ITEM_STATE_RENDER set on success.
291  */
293 unsigned int nr_arena_item_invoke_render(NRArenaItem *item, NRRectL const *area, NRPixBlock *pb, unsigned int flags)
295         NRRectL carea;
296         NRPixBlock *dpb;
297         NRPixBlock cpb;
298         unsigned int state;
300         nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
301         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NR_ARENA_ITEM_STATE_INVALID);
302         nr_return_val_if_fail (item->state & NR_ARENA_ITEM_STATE_BBOX, item->state);
304 #ifdef NR_ARENA_ITEM_VERBOSE
305         printf ("Invoke render %p: %d %d - %d %d\n", item, area->x0, area->y0, area->x1, area->y1);
306 #endif
308 #ifdef arena_item_tile_cache
309   item->activity*=0.5;
310 #endif
312         /* If we are outside bbox just return successfully */
313         if (!item->visible) return item->state | NR_ARENA_ITEM_STATE_RENDER;
314         nr_rect_l_intersect (&carea, area, &item->bbox);
315         if (nr_rect_l_test_empty (&carea)) return item->state | NR_ARENA_ITEM_STATE_RENDER;
316         if(item->filter) {
317           nr_rect_l_enlarge(&carea, item->filter->get_enlarge(item->ctm));
318           nr_rect_l_intersect(&carea, &carea, &item->bbox);
319         }
321         if (item->px) {
322                 /* Has cache pixblock, render this and return */
323                 nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P,
324                                           /* fixme: This probably cannot overflow, because we render only if visible */
325                                           /* fixme: and pixel cache is there only for small items */
326                                           /* fixme: But this still needs extra check (Lauris) */
327                                           item->bbox.x0, item->bbox.y0,
328                                           item->bbox.x1, item->bbox.y1,
329                                           item->px, 4 * (item->bbox.x1 - item->bbox.x0), FALSE, FALSE);
330                 nr_blit_pixblock_pixblock (pb, &cpb);
331                 nr_pixblock_release (&cpb);
332                 pb->empty = FALSE;
333                 return item->state | NR_ARENA_ITEM_STATE_RENDER;
334         }
336         dpb = pb;
337   bool  canCache=false;
338 #ifdef arena_item_tile_cache  
339   bool checkCache=false;
340   int   tile_h=0,tile_v=0;
341 #endif  
342         /* Setup cache if we can */
343         if ((!(flags & NR_ARENA_ITEM_RENDER_NO_CACHE)) &&
344             (carea.x0 <= item->bbox.x0) && (carea.y0 <= item->bbox.y0) &&
345             (carea.x1 >= item->bbox.x1) && (carea.y1 >= item->bbox.y1) &&
346             (((item->bbox.x1 - item->bbox.x0) * (item->bbox.y1 - item->bbox.y0)) <= 4096)) {
347                 // Item bbox is fully in renderable area and size is acceptable
348                 carea.x0 = item->bbox.x0;
349                 carea.y0 = item->bbox.y0;
350                 carea.x1 = item->bbox.x1;
351                 carea.y1 = item->bbox.y1;
352                 item->px = new (GC::ATOMIC) unsigned char[4 * (carea.x1 - carea.x0) * (carea.y1 - carea.y0)];
353                 nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P,
354                               carea.x0, carea.y0, carea.x1, carea.y1,
355                               item->px, 4 * (carea.x1 - carea.x0), TRUE, TRUE);
356         cpb.visible_area = pb->visible_area; 
357                 dpb = &cpb;
358                 // Set nocache flag for downstream rendering
359                 flags |= NR_ARENA_ITEM_RENDER_NO_CACHE;
360         } else {
361 #ifdef arena_item_tile_cache
362     if ( item->skipCaching ) {
363     } else {
364       int tl=area->x0&(~127);
365       int tt=area->y0&(~127);
366       if ( area->x1 <= tl+128 && area->y1 <= tt+128 ) {
367         checkCache=true;
368         tile_h=tl/128;
369         tile_v=tt/128;
370         int surf=(area->x1-area->x0)*(area->y1-area->y0);
371         if ( surf >= 4096 ) {
372           canCache=true;
373           carea.x0=tl;
374           carea.y0=tt;
375           carea.x1=tl+128;
376           carea.y1=tt+128;
377         }
378       }
379     }
380 #endif
381   }
383 #ifdef arena_item_tile_cache
384   item->activity+=1.0;
385 #endif
387 #ifdef arena_item_tile_cache
388   if ( checkCache ) {
389     NRPixBlock ipb, mpb;
390     bool       hasMask;
391     if ( test_cache(item,tile_h,tile_v,ipb,mpb,hasMask) ) {
392       // youpi! c'etait deja cache
393       if ( hasMask ) {
394         nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
395       } else if ( ((item->opacity != 255) && !item->render_opacity) ) {
396         nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
397       } else {
398         nr_blit_pixblock_pixblock (pb, &ipb);
399       }
400       pb->empty = FALSE;
401       return item->state | NR_ARENA_ITEM_STATE_RENDER;
402     }
403   }
404 #endif
405   if ( canCache ) {
406 #ifdef arena_item_tile_cache
407     // nota: exclusif de dpb != pb, donc pas de cas particulier a la fin
408     NRPixBlock ipb, mpb;
410     // struct timeval start_time,end_time;
411     // gettimeofday(&start_time,NULL);
412     GTimeVal start_time,end_time;
413     g_get_current_time (&start_time);
414     int    duration=0;
416     /* Setup and render item buffer */
417     nr_pixblock_setup_fast (&ipb, NR_PIXBLOCK_MODE_R8G8B8A8P, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
418     ipb.visible_area = pb->visible_area; 
419     state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, &ipb, flags);
420     if (state & NR_ARENA_ITEM_STATE_INVALID) {
421       /* Clean up and return error */
422       nr_pixblock_release (&ipb);
423       if (dpb != pb) nr_pixblock_release (dpb);
424       item->state |= NR_ARENA_ITEM_STATE_INVALID;
425       return item->state;
426     }
427     ipb.empty = FALSE;
429     if (item->clip || item->mask) {
430       /* Setup mask pixblock */
431       nr_pixblock_setup_fast (&mpb, NR_PIXBLOCK_MODE_A8, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
432       mpb.visible_area = pb->visible_area; 
433       /* Do clip if needed */
434       if (item->clip) {
435         state = nr_arena_item_invoke_clip (item->clip, &carea, &mpb);
436         if (state & NR_ARENA_ITEM_STATE_INVALID) {
437           /* Clean up and return error */
438           nr_pixblock_release (&mpb);
439           nr_pixblock_release (&ipb);
440           if (dpb != pb) nr_pixblock_release (dpb);
441           item->state |= NR_ARENA_ITEM_STATE_INVALID;
442           return item->state;
443         }
444         mpb.empty = FALSE;
445       }
446       /* Do mask if needed */
447       if (item->mask) {
448         NRPixBlock tpb;
449         /* Set up yet another temporary pixblock */
450         nr_pixblock_setup_fast (&tpb, NR_PIXBLOCK_MODE_R8G8B8A8N, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
451         tpb.visible_area = pb->visible_area; 
452         state = NR_ARENA_ITEM_VIRTUAL (item->mask, render) (item->mask, &carea, &tpb, flags);
453         if (state & NR_ARENA_ITEM_STATE_INVALID) {
454           /* Clean up and return error */
455           nr_pixblock_release (&tpb);
456           nr_pixblock_release (&mpb);
457           nr_pixblock_release (&ipb);
458           if (dpb != pb) nr_pixblock_release (dpb);
459           item->state |= NR_ARENA_ITEM_STATE_INVALID;
460           return item->state;
461         }
462         /* Composite with clip */
463         if (item->clip) {
464           int x, y;
465           for (y = carea.y0; y < carea.y1; y++) {
466             unsigned char *s, *d;
467             s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
468             d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
469             for (x = carea.x0; x < carea.x1; x++) {
470               unsigned int m;
471               m = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
472               d[0] = NR_PREMUL (d[0], m);
473               s += 4;
474               d += 1;
475             }
476           }
477         } else {
478           int x, y;
479           for (y = carea.y0; y < carea.y1; y++) {
480             unsigned char *s, *d;
481             s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
482             d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
483             for (x = carea.x0; x < carea.x1; x++) {
484               unsigned int m;
485               m = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
486               d[0] = m;
487               s += 4;
488               d += 1;
489             }
490           }
491           mpb.empty = FALSE;
492         }
493         nr_pixblock_release (&tpb);
494       }
495       /* Multiply with opacity if needed */
496       if ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE) {
497         int x, y;
498         unsigned int a;
499         a = item->opacity;
500         for (y = carea.y0; y < carea.y1; y++) {
501           unsigned char *d;
502           d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
503           for (x = carea.x0; x < carea.x1; x++) {
504             d[0] = NR_PREMUL (d[0], a);
505             d += 1;
506           }
507         }
508       }
509       /* Compose rendering pixblock int destination */
510       // gettimeofday(&end_time,NULL);
511       g_get_current_time (&end_time);
512       duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
513       if ( !(ipb.empty) ) {
514         nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
515         if ( insert_cache(item,tile_h,tile_v,&ipb,&mpb,item->activity,(double)duration) ) {
516         } else {
517           nr_pixblock_release (&mpb);
518           nr_pixblock_release (&ipb);
519         }
520         dpb->empty = FALSE;
521       } else {
522         nr_pixblock_release (&ipb);
523       }
524     } else if ( ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE) ) {
525       /* Opacity only */
526       // gettimeofday(&end_time,NULL);
527       g_get_current_time (&end_time);
528       duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
529       if ( !(ipb.empty) ) {
530         nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
531         if ( insert_cache(item,tile_h,tile_v,&ipb,NULL,item->activity,(double)duration) ) {
532         } else {
533           nr_pixblock_release (&ipb);
534         }
535         dpb->empty = FALSE;
536       } else {
537         nr_pixblock_release (&ipb);
538       }
539     } else {
540       // gettimeofday(&end_time,NULL);
541       g_get_current_time (&end_time);
542       duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
543       if ( !(ipb.empty) ) {
544         nr_blit_pixblock_pixblock (dpb, &ipb);
545         if ( insert_cache(item,tile_h,tile_v,&ipb,NULL,item->activity,(double)duration) ) {
546         } else {
547           nr_pixblock_release (&ipb);
548         }
549         dpb->empty = FALSE;
550       } else {
551         nr_pixblock_release (&ipb);
552       }
553     }
554 #endif
555   } else {
556     /* Determine, whether we need temporary buffer */
557     if (item->clip || item->mask
558         || ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE)
559         || item->filter || item->background_new
560         || (item->parent && item->parent->background_pb) )
561       {
562       NRPixBlock ipb, mpb;
564       /* Setup and render item buffer */
565       nr_pixblock_setup_fast (&ipb, NR_PIXBLOCK_MODE_R8G8B8A8P, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
566       /* If background access is used, save the pixblock address.
567        * This address is set to NULL at the end of this block */
568       if (item->background_new
569           || (item->parent && item->parent->background_pb)) {
570         item->background_pb = &ipb;
571       }
572       ipb.visible_area = pb->visible_area; 
573       state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, &ipb, flags);
574       if (state & NR_ARENA_ITEM_STATE_INVALID) {
575         /* Clean up and return error */
576         nr_pixblock_release (&ipb);
577         if (dpb != pb) nr_pixblock_release (dpb);
578         item->state |= NR_ARENA_ITEM_STATE_INVALID;
579         return item->state;
580       }
581       ipb.empty = FALSE;
583       /* Run filtering, if a filter is set for this object */
584       if(item->filter) {
585         item->filter->render(item, &ipb);
586       }
588       if (item->clip || item->mask) {
589         /* Setup mask pixblock */
590         nr_pixblock_setup_fast (&mpb, NR_PIXBLOCK_MODE_A8, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
591         mpb.visible_area = pb->visible_area; 
592         /* Do clip if needed */
593         if (item->clip) {
594           state = nr_arena_item_invoke_clip (item->clip, &carea, &mpb);
595           if (state & NR_ARENA_ITEM_STATE_INVALID) {
596             /* Clean up and return error */
597             nr_pixblock_release (&mpb);
598             nr_pixblock_release (&ipb);
599             if (dpb != pb) nr_pixblock_release (dpb);
600             item->state |= NR_ARENA_ITEM_STATE_INVALID;
601             return item->state;
602           }
603           mpb.empty = FALSE;
604         }
605         /* Do mask if needed */
606         if (item->mask) {
607           NRPixBlock tpb;
608           /* Set up yet another temporary pixblock */
609           nr_pixblock_setup_fast (&tpb, NR_PIXBLOCK_MODE_R8G8B8A8N, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
610           tpb.visible_area = pb->visible_area; 
611           state = NR_ARENA_ITEM_VIRTUAL (item->mask, render) (item->mask, &carea, &tpb, flags);
612           if (state & NR_ARENA_ITEM_STATE_INVALID) {
613             /* Clean up and return error */
614             nr_pixblock_release (&tpb);
615             nr_pixblock_release (&mpb);
616             nr_pixblock_release (&ipb);
617             if (dpb != pb) nr_pixblock_release (dpb);
618             item->state |= NR_ARENA_ITEM_STATE_INVALID;
619             return item->state;
620           }
621           /* Composite with clip */
622           if (item->clip) {
623             int x, y;
624             for (y = carea.y0; y < carea.y1; y++) {
625               unsigned char *s, *d;
626               s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
627               d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
628               for (x = carea.x0; x < carea.x1; x++) {
629                 unsigned int m;
630                 m = NR_PREMUL_112(s[0]+s[1]+s[2], s[3]);
631                 d[0] = FAST_DIV_ROUND<3*255*255>(NR_PREMUL_123(d[0], m));
632                 s += 4;
633                 d += 1;
634               }
635             }
636           } else {
637             int x, y;
638             for (y = carea.y0; y < carea.y1; y++) {
639               unsigned char *s, *d;
640               s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
641               d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
642               for (x = carea.x0; x < carea.x1; x++) {
643                 unsigned int m;
644                 m = NR_PREMUL_112(s[0]+s[1]+s[2], s[3]);
645                 d[0] = FAST_DIV_ROUND<3*255>(m);
646                 s += 4;
647                 d += 1;
648               }
649             }
650             mpb.empty = FALSE;
651           }
652           nr_pixblock_release (&tpb);
653         }
654         /* Multiply with opacity if needed */
655         if ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE) {
656           int x, y;
657           unsigned int a;
658           a = item->opacity;
659           for (y = carea.y0; y < carea.y1; y++) {
660             unsigned char *d;
661             d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
662             for (x = carea.x0; x < carea.x1; x++) {
663               d[0] = NR_PREMUL_111 (d[0], a);
664               d += 1;
665             }
666           }
667         }
668         /* Compose rendering pixblock int destination */
669         nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
670         nr_pixblock_release (&mpb);
671         /* This pointer wouldn't be valid outside this block, so clear it */
672         item->background_pb = NULL;
673       } else {
674         /* Opacity only */
675         nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
676       }
677       nr_pixblock_release (&ipb);
678       dpb->empty = FALSE;
679     } else {
680       /* Just render */
681       state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, dpb, flags);
682       if (state & NR_ARENA_ITEM_STATE_INVALID) {
683         /* Clean up and return error */
684         if (dpb != pb) nr_pixblock_release (dpb);
685         item->state |= NR_ARENA_ITEM_STATE_INVALID;
686         return item->state;
687       }
688       dpb->empty = FALSE;
689     }
691     if (dpb != pb) {
692       /* Have to blit from cache */
693       nr_blit_pixblock_pixblock (pb, dpb);
694       nr_pixblock_release (dpb);
695       pb->empty = FALSE;
696       item->state |= NR_ARENA_ITEM_STATE_IMAGE;
697     }
698   }
699         return item->state | NR_ARENA_ITEM_STATE_RENDER;
702 unsigned int
703 nr_arena_item_invoke_clip (NRArenaItem *item, NRRectL *area, NRPixBlock *pb)
705         nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
706         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NR_ARENA_ITEM_STATE_INVALID);
707         /* we originally short-circuited if the object state included
708          * NR_ARENA_ITEM_STATE_CLIP (and showed a warning on the console);
709          * anyone know why we stopped doing so?
710          */
711         nr_return_val_if_fail ((pb->area.x1 - pb->area.x0) >= (area->x1 - area->x0), NR_ARENA_ITEM_STATE_INVALID);
712         nr_return_val_if_fail ((pb->area.y1 - pb->area.y0) >= (area->y1 - area->y0), NR_ARENA_ITEM_STATE_INVALID);
714 #ifdef NR_ARENA_ITEM_VERBOSE
715         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);
716 #endif
718         if (item->visible && nr_rect_l_test_intersect (area, &item->bbox)) {
719                 /* Need render that item */
720                 if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->clip) {
721                         return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS(item))->clip (item, area, pb);
722                 }
723         }
725         return item->state;
728 NRArenaItem *
729 nr_arena_item_invoke_pick (NRArenaItem *item, NR::Point p, double delta, unsigned int sticky)
731         nr_return_val_if_fail (item != NULL, NULL);
732         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
734         // Sometimes there's no BBOX in item->state, reason unknown (bug 992817); I made this not an assert to remove the warning
735         if (!(item->state & NR_ARENA_ITEM_STATE_BBOX) || !(item->state & NR_ARENA_ITEM_STATE_PICK))
736                 return NULL;
738         if (!sticky && !(item->visible && item->sensitive)) return NULL;
739         
740         // TODO: rewrite using NR::Rect
741         const double x = p[NR::X];
742         const double y = p[NR::Y];
743         
744         if (((x + delta) >= item->bbox.x0) &&
745             ((x - delta) <  item->bbox.x1) &&
746             ((y + delta) >= item->bbox.y0) &&
747             ((y - delta) <  item->bbox.y1)) {
748                 if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->pick)
749                         return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->pick (item, p, delta, sticky);
750         }
752         return NULL;
755 void
756 nr_arena_item_request_update (NRArenaItem *item, unsigned int reset, unsigned int propagate)
758         nr_return_if_fail (item != NULL);
759         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
760         nr_return_if_fail (!(reset & NR_ARENA_ITEM_STATE_INVALID));
762         if (propagate && !item->propagate) item->propagate = TRUE;
764         if (item->state & reset) {
765                 item->state &= ~reset;
766                 if (item->parent) {
767                         nr_arena_item_request_update (item->parent, reset, FALSE);
768                 } else {
769                         nr_arena_request_update (item->arena, item);
770                 }
771         }
774 void
775 nr_arena_item_request_render (NRArenaItem *item)
777         nr_return_if_fail (item != NULL);
778         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
780         nr_arena_request_render_rect (item->arena, &item->bbox);
783 /* Public */
785 NRArenaItem *
786 nr_arena_item_unparent (NRArenaItem *item)
788         nr_return_val_if_fail (item != NULL, NULL);
789         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
791         nr_arena_item_request_render (item);
793         if (item->parent) {
794                 nr_arena_item_remove_child (item->parent, item);
795         }
797         return NULL;
800 void
801 nr_arena_item_append_child (NRArenaItem *parent, NRArenaItem *child)
803         nr_return_if_fail (parent != NULL);
804         nr_return_if_fail (NR_IS_ARENA_ITEM (parent));
805         nr_return_if_fail (child != NULL);
806         nr_return_if_fail (NR_IS_ARENA_ITEM (child));
807         nr_return_if_fail (parent->arena == child->arena);
808         nr_return_if_fail (child->parent == NULL);
809         nr_return_if_fail (child->prev == NULL);
810         nr_return_if_fail (child->next == NULL);
812         nr_arena_item_add_child (parent, child, nr_arena_item_last_child (parent));
815 void
816 nr_arena_item_set_transform(NRArenaItem *item, NR::Matrix const &transform)
818         NRMatrix const t(transform);
819         nr_arena_item_set_transform(item, &t);
822 void
823 nr_arena_item_set_transform(NRArenaItem *item, NRMatrix const *transform)
825         const NRMatrix *ms, *md;
827         nr_return_if_fail (item != NULL);
828         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
830         if (!transform && !item->transform) return;
832         md = (item->transform) ? item->transform : &NR_MATRIX_IDENTITY;
833         ms = (transform) ? transform : &NR_MATRIX_IDENTITY;
835         if (!NR_MATRIX_DF_TEST_CLOSE (md, ms, NR_EPSILON)) {
836                 nr_arena_item_request_render (item);
837                 if (!transform || nr_matrix_test_identity (transform, NR_EPSILON)) {
838                         /* Set to identity affine */
839                         item->transform = NULL;
840                 } else {
841                         if (!item->transform) item->transform = new (GC::ATOMIC) NRMatrix();
842                         *item->transform = *transform;
843                 }
844                 nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
845         }
848 void
849 nr_arena_item_set_opacity (NRArenaItem *item, double opacity)
851         nr_return_if_fail (item != NULL);
852         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
854         nr_arena_item_request_render (item);
856         item->opacity = (unsigned int) (opacity * 255.9999);
859 void
860 nr_arena_item_set_sensitive (NRArenaItem *item, unsigned int sensitive)
862         nr_return_if_fail (item != NULL);
863         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
865         /* fixme: mess with pick/repick... */
867         item->sensitive = sensitive;
870 void
871 nr_arena_item_set_visible (NRArenaItem *item, unsigned int visible)
873         nr_return_if_fail (item != NULL);
874         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
876         item->visible = visible;
878         nr_arena_item_request_render (item);
881 void
882 nr_arena_item_set_clip (NRArenaItem *item, NRArenaItem *clip)
884         nr_return_if_fail (item != NULL);
885         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
886         nr_return_if_fail (!clip || NR_IS_ARENA_ITEM (clip));
888         if (clip != item->clip) {
889                 nr_arena_item_request_render (item);
890                 if (item->clip) item->clip = nr_arena_item_detach (item, item->clip);
891                 if (clip) item->clip = nr_arena_item_attach (item, clip, NULL, NULL);
892                 nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
893         }
896 void
897 nr_arena_item_set_mask (NRArenaItem *item, NRArenaItem *mask)
899         nr_return_if_fail (item != NULL);
900         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
901         nr_return_if_fail (!mask || NR_IS_ARENA_ITEM (mask));
903         if (mask != item->mask) {
904                 nr_arena_item_request_render (item);
905                 if (item->mask) item->mask = nr_arena_item_detach (item, item->mask);
906                 if (mask) item->mask = nr_arena_item_attach (item, mask, NULL, NULL);
907                 nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
908         }
911 void
912 nr_arena_item_set_order (NRArenaItem *item, int order)
914         NRArenaItem *children, *child, *ref;
915         int pos;
917         nr_return_if_fail (item != NULL);
918         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
920         if (!item->parent) return;
922         children = nr_arena_item_children (item->parent);
924         ref = NULL;
925         pos = 0;
926         for (child = children; child != NULL; child = child->next) {
927                 if (pos >= order) break;
928                 if (child != item) {
929                         ref = child;
930                         pos += 1;
931                 }
932         }
934         nr_arena_item_set_child_position (item->parent, item, ref);
937 /** Returns a background image for use with filter effects. */
938 NRPixBlock *nr_arena_item_get_background (NRArenaItem const *item, int depth)
940   NRPixBlock *pb;
941   if (!item->background_pb) return NULL;
942   if (item->background_new) {
943     pb = new NRPixBlock();
944     nr_pixblock_setup_fast(pb, item->background_pb->mode,
945                            item->background_pb->area.x0,
946                            item->background_pb->area.y0,
947                            item->background_pb->area.x1,
948                            item->background_pb->area.y1, true);
949   } else if (item->parent) {
950     pb = nr_arena_item_get_background(item->parent, depth + 1);
951   } else return NULL;
953   if (depth > 0)
954     nr_blit_pixblock_pixblock(pb, item->background_pb);
956   return pb;
959 /* Helpers */
961 NRArenaItem *
962 nr_arena_item_attach (NRArenaItem *parent, NRArenaItem *child, NRArenaItem *prev, NRArenaItem *next)
964         nr_return_val_if_fail (parent != NULL, NULL);
965         nr_return_val_if_fail (NR_IS_ARENA_ITEM (parent), NULL);
966         nr_return_val_if_fail (child != NULL, NULL);
967         nr_return_val_if_fail (NR_IS_ARENA_ITEM (child), NULL);
968         nr_return_val_if_fail (child->parent == NULL, NULL);
969         nr_return_val_if_fail (child->prev == NULL, NULL);
970         nr_return_val_if_fail (child->next == NULL, NULL);
971         nr_return_val_if_fail (!prev || NR_IS_ARENA_ITEM (prev), NULL);
972         nr_return_val_if_fail (!prev || (prev->parent == parent), NULL);
973         nr_return_val_if_fail (!prev || (prev->next == next), NULL);
974         nr_return_val_if_fail (!next || NR_IS_ARENA_ITEM (next), NULL);
975         nr_return_val_if_fail (!next || (next->parent == parent), NULL);
976         nr_return_val_if_fail (!next || (next->prev == prev), NULL);
978         child->parent = parent;
979         child->prev = prev;
980         child->next = next;
982         if (prev) prev->next = child;
983         if (next) next->prev = child;
985         return child;
988 NRArenaItem *
989 nr_arena_item_detach (NRArenaItem *parent, NRArenaItem *child)
991         NRArenaItem *prev, *next;
993         nr_return_val_if_fail (parent != NULL, NULL);
994         nr_return_val_if_fail (NR_IS_ARENA_ITEM (parent), NULL);
995         nr_return_val_if_fail (child != NULL, NULL);
996         nr_return_val_if_fail (NR_IS_ARENA_ITEM (child), NULL);
997         nr_return_val_if_fail (child->parent == parent, NULL);
999         prev = child->prev;
1000         next = child->next;
1002         child->parent = NULL;
1003         child->prev = NULL;
1004         child->next = NULL;
1006         if (prev) prev->next = next;
1007         if (next) next->prev = prev;
1009         return next;
1012 /*
1013  *
1014  * caches
1015  *
1016  */
1018 #ifdef arena_item_tile_cache
1019 typedef struct cache_entry {
1020   int             key;
1021   double          score;
1022   NRArenaItem*    owner;
1023   int             th,tv;
1024   int             prev,next;
1025   NRPixBlock      ipb;
1026   bool            hasMask;
1027   NRPixBlock      mpb;
1028 } cache_entry;
1030 int hash_max=2048,hash_fill=1024;
1032 int            *keys=NULL;
1033 int            nbCch=0;
1035 int            nbEnt=0,maxEnt=0;
1036 cache_entry*   entries=NULL;
1038 //#define tile_cache_stats
1039 #ifdef tile_cache_stats
1040 double         hits=0,misses=0;
1041 int            hitMissCount=0;
1042 #endif
1044 int hash_that(NRArenaItem* owner,int th,int tv)
1046   int res=GPOINTER_TO_INT(owner);
1047   res*=17;
1048   res+=th;
1049   res*=59;
1050   res+=tv;
1051   res*=217;
1052   if ( res < 0 ) res=-res;
1053   res%=hash_max;
1054   return res;
1057 bool  test_cache(NRArenaItem* owner,int th,int tv,NRPixBlock &ipb,NRPixBlock &mpb,bool &hasMask)
1059   if ( keys == NULL ) {
1060     hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1061     hash_fill=(hash_max*3)/4;
1062     keys=(int*)malloc(hash_max*sizeof(int));
1063     for (int i=0;i<hash_max;i++) keys[i]=-1;
1064   }
1065   int key=hash_that(owner,th,tv);
1066   if ( keys[key] < 0 ) {
1067 #ifdef tile_cache_stats
1068     misses+=1.0;
1069 #endif
1070     return false;
1071   }
1072   int cur=keys[key];
1073   while ( cur >= 0 && cur < nbEnt ) {
1074     if ( entries[cur].owner == owner && entries[cur].th == th && entries[cur].tv == tv ) {
1075       hasMask=entries[cur].hasMask;
1076       ipb=entries[cur].ipb;
1077       mpb=entries[cur].mpb;
1078 #ifdef tile_cache_stats
1079       hits+=1.0;
1080 #endif
1081       return true;
1082     }
1083     cur=entries[cur].next;
1084   }
1085 #ifdef tile_cache_stats
1086   misses+=1.0;
1087 #endif
1088   return false;
1090 void  remove_one_cache(int no)
1092   if ( no < 0 || no >= nbEnt ) return;
1094   nr_pixblock_release(&entries[no].ipb);
1095   if ( entries[no].hasMask ) nr_pixblock_release(&entries[no].mpb);
1097   if ( entries[no].prev >= 0 ) entries[entries[no].prev].next=entries[no].next;
1098   if ( entries[no].next >= 0 ) entries[entries[no].next].prev=entries[no].prev;
1099   if ( entries[no].prev < 0 ) keys[entries[no].key]=entries[no].next;
1100   entries[no].prev=entries[no].next=entries[no].key=-1;
1102   if ( no == nbEnt-1 ) {
1103     nbEnt--;
1104     return;
1105   }
1106   entries[no]=entries[--nbEnt];
1107   if ( entries[no].prev >= 0 ) entries[entries[no].prev].next=no;
1108   if ( entries[no].next >= 0 ) entries[entries[no].next].prev=no;
1109   if ( entries[no].prev < 0 ) keys[entries[no].key]=no;
1111 void  remove_caches(NRArenaItem* owner)
1113   if ( keys == NULL ) {
1114     hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1115     hash_fill=(hash_max*3)/4;
1116     keys=(int*)malloc(hash_max*sizeof(int));
1117     for (int i=0;i<hash_max;i++) keys[i]=-1;
1118   }
1119   for (int i=nbEnt-1;i>=0;i--) {
1120     if ( entries[i].owner == owner ) {
1121       remove_one_cache(i);
1122     }
1123   }
1125 void  age_cache(void)
1127   for (int i=0;i<nbEnt;i++) entries[i].score*=0.95;
1129 bool  insert_cache(NRArenaItem* owner,int th,int tv,NRPixBlock *ipb,NRPixBlock *mpb,double activity,double duration)
1131   if ( keys == NULL ) {
1132     hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1133     hash_fill=(hash_max*3)/4;
1134     keys=(int*)malloc(hash_max*sizeof(int));
1135     for (int i=0;i<hash_max;i++) keys[i]=-1;
1136   }
1137   for (int i=0;i<nbEnt;i++) entries[i].score*=0.95;
1138 #ifdef tile_cache_stats
1139   hits*=0.95;
1140   misses*=0.95;
1141   hitMissCount++;
1142   if ( hitMissCount > 100 ) {
1143     hitMissCount=0;
1144     printf("hit/miss = %f  used/total=%i/%i\n",(misses>0.001)?hits/misses:100000.0,nbEnt,hash_max); // localizing ok
1145   }
1146 #endif
1147   int    key=hash_that(owner,th,tv);
1148   double nScore=/*activity**/duration;
1150   if ( keys[key] >= 0 ) {
1151     int cur=keys[key];
1152     while ( cur >= 0 && cur < nbEnt ) {
1153       if ( entries[cur].owner == owner && entries[cur].th == th && entries[cur].tv == tv ) {
1154         remove_one_cache(cur);
1155         break;
1156       }
1157       cur=entries[cur].next;
1158     }
1159   }
1161   bool doAdd=false;
1162   if ( nbEnt < hash_fill ) {
1163     doAdd=true;
1164   } else {
1165     double    worstS=entries[0].score;
1166     int       worstE=0;
1167     for (int i=1;i<nbEnt;i++) {
1168       if ( entries[i].score < worstS ) {
1169         worstS=entries[i].score;
1170         worstE=i;
1171       }
1172     }
1173     if ( worstS < nScore ) {
1174       doAdd=true;
1175       remove_one_cache(worstE);
1176     }
1177   }
1178   if ( doAdd == false ) return false;
1179   if ( nbEnt >= maxEnt ) {
1180     maxEnt=2*nbEnt+1;
1181     entries=(cache_entry*)realloc(entries,maxEnt*sizeof(cache_entry));
1182   }
1183   entries[nbEnt].key=key;
1184   entries[nbEnt].score=nScore;
1185   entries[nbEnt].owner=owner;
1186   entries[nbEnt].th=th;
1187   entries[nbEnt].tv=tv;
1188   entries[nbEnt].prev=entries[nbEnt].next=-1;
1189   entries[nbEnt].ipb=*ipb;
1190   if ( mpb ) {
1191     entries[nbEnt].hasMask=true;
1192     entries[nbEnt].mpb=*mpb;
1193   } else {
1194     entries[nbEnt].hasMask=false;
1195   }
1196   entries[nbEnt].next=keys[key];
1197   if ( entries[nbEnt].next >= 0 ) entries[entries[nbEnt].next].prev=nbEnt;
1198   keys[key]=nbEnt;
1200   nbEnt++;
1201   return true;
1203 #endif