Code

disable filters in outline mode
[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         bool outline = (item->arena->rendermode == RENDERMODE_OUTLINE);
302         nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
303         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NR_ARENA_ITEM_STATE_INVALID);
304         nr_return_val_if_fail (item->state & NR_ARENA_ITEM_STATE_BBOX, item->state);
306 #ifdef NR_ARENA_ITEM_VERBOSE
307         printf ("Invoke render %p: %d %d - %d %d\n", item, area->x0, area->y0, area->x1, area->y1);
308 #endif
310 #ifdef arena_item_tile_cache
311   item->activity*=0.5;
312 #endif
314         /* If we are outside bbox just return successfully */
315         if (!item->visible) return item->state | NR_ARENA_ITEM_STATE_RENDER;
316         nr_rect_l_intersect (&carea, area, &item->bbox);
317         if (nr_rect_l_test_empty (&carea)) return item->state | NR_ARENA_ITEM_STATE_RENDER;
318         if(item->filter && !outline) {
319           nr_rect_l_enlarge(&carea, item->filter->get_enlarge(item->ctm));
320           nr_rect_l_intersect(&carea, &carea, &item->bbox);
321         }
323         if (item->px) {
324                 /* Has cache pixblock, render this and return */
325                 nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P,
326                                           /* fixme: This probably cannot overflow, because we render only if visible */
327                                           /* fixme: and pixel cache is there only for small items */
328                                           /* fixme: But this still needs extra check (Lauris) */
329                                           item->bbox.x0, item->bbox.y0,
330                                           item->bbox.x1, item->bbox.y1,
331                                           item->px, 4 * (item->bbox.x1 - item->bbox.x0), FALSE, FALSE);
332                 nr_blit_pixblock_pixblock (pb, &cpb);
333                 nr_pixblock_release (&cpb);
334                 pb->empty = FALSE;
335                 return item->state | NR_ARENA_ITEM_STATE_RENDER;
336         }
338         dpb = pb;
339   bool  canCache=false;
340 #ifdef arena_item_tile_cache  
341   bool checkCache=false;
342   int   tile_h=0,tile_v=0;
343 #endif  
344         /* Setup cache if we can */
345         if ((!(flags & NR_ARENA_ITEM_RENDER_NO_CACHE)) &&
346             (carea.x0 <= item->bbox.x0) && (carea.y0 <= item->bbox.y0) &&
347             (carea.x1 >= item->bbox.x1) && (carea.y1 >= item->bbox.y1) &&
348             (((item->bbox.x1 - item->bbox.x0) * (item->bbox.y1 - item->bbox.y0)) <= 4096)) {
349                 // Item bbox is fully in renderable area and size is acceptable
350                 carea.x0 = item->bbox.x0;
351                 carea.y0 = item->bbox.y0;
352                 carea.x1 = item->bbox.x1;
353                 carea.y1 = item->bbox.y1;
354                 item->px = new (GC::ATOMIC) unsigned char[4 * (carea.x1 - carea.x0) * (carea.y1 - carea.y0)];
355                 nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P,
356                               carea.x0, carea.y0, carea.x1, carea.y1,
357                               item->px, 4 * (carea.x1 - carea.x0), TRUE, TRUE);
358         cpb.visible_area = pb->visible_area; 
359                 dpb = &cpb;
360                 // Set nocache flag for downstream rendering
361                 flags |= NR_ARENA_ITEM_RENDER_NO_CACHE;
362         } else {
363 #ifdef arena_item_tile_cache
364     if ( item->skipCaching ) {
365     } else {
366       int tl=area->x0&(~127);
367       int tt=area->y0&(~127);
368       if ( area->x1 <= tl+128 && area->y1 <= tt+128 ) {
369         checkCache=true;
370         tile_h=tl/128;
371         tile_v=tt/128;
372         int surf=(area->x1-area->x0)*(area->y1-area->y0);
373         if ( surf >= 4096 ) {
374           canCache=true;
375           carea.x0=tl;
376           carea.y0=tt;
377           carea.x1=tl+128;
378           carea.y1=tt+128;
379         }
380       }
381     }
382 #endif
383   }
385 #ifdef arena_item_tile_cache
386   item->activity+=1.0;
387 #endif
389 #ifdef arena_item_tile_cache
390   if ( checkCache ) {
391     NRPixBlock ipb, mpb;
392     bool       hasMask;
393     if ( test_cache(item,tile_h,tile_v,ipb,mpb,hasMask) ) {
394       // youpi! c'etait deja cache
395       if ( hasMask ) {
396         nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
397       } else if ( ((item->opacity != 255) && !item->render_opacity) ) {
398         nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
399       } else {
400         nr_blit_pixblock_pixblock (pb, &ipb);
401       }
402       pb->empty = FALSE;
403       return item->state | NR_ARENA_ITEM_STATE_RENDER;
404     }
405   }
406 #endif
407   if ( canCache ) {
408 #ifdef arena_item_tile_cache
409     // nota: exclusif de dpb != pb, donc pas de cas particulier a la fin
410     NRPixBlock ipb, mpb;
412     // struct timeval start_time,end_time;
413     // gettimeofday(&start_time,NULL);
414     GTimeVal start_time,end_time;
415     g_get_current_time (&start_time);
416     int    duration=0;
418     /* Setup and render item buffer */
419     nr_pixblock_setup_fast (&ipb, NR_PIXBLOCK_MODE_R8G8B8A8P, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
420     ipb.visible_area = pb->visible_area; 
421     state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, &ipb, flags);
422     if (state & NR_ARENA_ITEM_STATE_INVALID) {
423       /* Clean up and return error */
424       nr_pixblock_release (&ipb);
425       if (dpb != pb) nr_pixblock_release (dpb);
426       item->state |= NR_ARENA_ITEM_STATE_INVALID;
427       return item->state;
428     }
429     ipb.empty = FALSE;
431     if (item->clip || item->mask) {
432       /* Setup mask pixblock */
433       nr_pixblock_setup_fast (&mpb, NR_PIXBLOCK_MODE_A8, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
434       mpb.visible_area = pb->visible_area; 
435       /* Do clip if needed */
436       if (item->clip) {
437         state = nr_arena_item_invoke_clip (item->clip, &carea, &mpb);
438         if (state & NR_ARENA_ITEM_STATE_INVALID) {
439           /* Clean up and return error */
440           nr_pixblock_release (&mpb);
441           nr_pixblock_release (&ipb);
442           if (dpb != pb) nr_pixblock_release (dpb);
443           item->state |= NR_ARENA_ITEM_STATE_INVALID;
444           return item->state;
445         }
446         mpb.empty = FALSE;
447       }
448       /* Do mask if needed */
449       if (item->mask) {
450         NRPixBlock tpb;
451         /* Set up yet another temporary pixblock */
452         nr_pixblock_setup_fast (&tpb, NR_PIXBLOCK_MODE_R8G8B8A8N, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
453         tpb.visible_area = pb->visible_area; 
454         state = NR_ARENA_ITEM_VIRTUAL (item->mask, render) (item->mask, &carea, &tpb, flags);
455         if (state & NR_ARENA_ITEM_STATE_INVALID) {
456           /* Clean up and return error */
457           nr_pixblock_release (&tpb);
458           nr_pixblock_release (&mpb);
459           nr_pixblock_release (&ipb);
460           if (dpb != pb) nr_pixblock_release (dpb);
461           item->state |= NR_ARENA_ITEM_STATE_INVALID;
462           return item->state;
463         }
464         /* Composite with clip */
465         if (item->clip) {
466           int x, y;
467           for (y = carea.y0; y < carea.y1; y++) {
468             unsigned char *s, *d;
469             s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
470             d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
471             for (x = carea.x0; x < carea.x1; x++) {
472               unsigned int m;
473               m = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
474               d[0] = NR_PREMUL (d[0], m);
475               s += 4;
476               d += 1;
477             }
478           }
479         } else {
480           int x, y;
481           for (y = carea.y0; y < carea.y1; y++) {
482             unsigned char *s, *d;
483             s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
484             d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
485             for (x = carea.x0; x < carea.x1; x++) {
486               unsigned int m;
487               m = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
488               d[0] = m;
489               s += 4;
490               d += 1;
491             }
492           }
493           mpb.empty = FALSE;
494         }
495         nr_pixblock_release (&tpb);
496       }
497       /* Multiply with opacity if needed */
498       if ((item->opacity != 255) && !item->render_opacity && !outline) {
499         int x, y;
500         unsigned int a;
501         a = item->opacity;
502         for (y = carea.y0; y < carea.y1; y++) {
503           unsigned char *d;
504           d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
505           for (x = carea.x0; x < carea.x1; x++) {
506             d[0] = NR_PREMUL (d[0], a);
507             d += 1;
508           }
509         }
510       }
511       /* Compose rendering pixblock int destination */
512       // gettimeofday(&end_time,NULL);
513       g_get_current_time (&end_time);
514       duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
515       if ( !(ipb.empty) ) {
516         nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
517         if ( insert_cache(item,tile_h,tile_v,&ipb,&mpb,item->activity,(double)duration) ) {
518         } else {
519           nr_pixblock_release (&mpb);
520           nr_pixblock_release (&ipb);
521         }
522         dpb->empty = FALSE;
523       } else {
524         nr_pixblock_release (&ipb);
525       }
526     } else if ( ((item->opacity != 255) && !item->render_opacity && !outline) ) {
527       /* Opacity only */
528       // gettimeofday(&end_time,NULL);
529       g_get_current_time (&end_time);
530       duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
531       if ( !(ipb.empty) ) {
532         nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
533         if ( insert_cache(item,tile_h,tile_v,&ipb,NULL,item->activity,(double)duration) ) {
534         } else {
535           nr_pixblock_release (&ipb);
536         }
537         dpb->empty = FALSE;
538       } else {
539         nr_pixblock_release (&ipb);
540       }
541     } else {
542       // gettimeofday(&end_time,NULL);
543       g_get_current_time (&end_time);
544       duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
545       if ( !(ipb.empty) ) {
546         nr_blit_pixblock_pixblock (dpb, &ipb);
547         if ( insert_cache(item,tile_h,tile_v,&ipb,NULL,item->activity,(double)duration) ) {
548         } else {
549           nr_pixblock_release (&ipb);
550         }
551         dpb->empty = FALSE;
552       } else {
553         nr_pixblock_release (&ipb);
554       }
555     }
556 #endif
557   } else {
558     /* Determine, whether we need temporary buffer */
559     if (item->clip || item->mask
560         || ((item->opacity != 255) && !item->render_opacity && !outline)
561         || (item->filter && !outline) || item->background_new
562         || (item->parent && item->parent->background_pb) )
563       {
564       NRPixBlock ipb, mpb;
566       /* Setup and render item buffer */
567       nr_pixblock_setup_fast (&ipb, NR_PIXBLOCK_MODE_R8G8B8A8P, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
568       /* If background access is used, save the pixblock address.
569        * This address is set to NULL at the end of this block */
570       if (item->background_new
571           || (item->parent && item->parent->background_pb)) {
572         item->background_pb = &ipb;
573       }
574       ipb.visible_area = pb->visible_area; 
575       state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, &ipb, flags);
576       if (state & NR_ARENA_ITEM_STATE_INVALID) {
577         /* Clean up and return error */
578         nr_pixblock_release (&ipb);
579         if (dpb != pb) nr_pixblock_release (dpb);
580         item->state |= NR_ARENA_ITEM_STATE_INVALID;
581         return item->state;
582       }
583       ipb.empty = FALSE;
585       /* Run filtering, if a filter is set for this object */
586       if(item->filter && !outline) {
587         item->filter->render(item, &ipb);
588       }
590       if (item->clip || item->mask) {
591         /* Setup mask pixblock */
592         nr_pixblock_setup_fast (&mpb, NR_PIXBLOCK_MODE_A8, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
593         mpb.visible_area = pb->visible_area; 
594         /* Do clip if needed */
595         if (item->clip) {
596           state = nr_arena_item_invoke_clip (item->clip, &carea, &mpb);
597           if (state & NR_ARENA_ITEM_STATE_INVALID) {
598             /* Clean up and return error */
599             nr_pixblock_release (&mpb);
600             nr_pixblock_release (&ipb);
601             if (dpb != pb) nr_pixblock_release (dpb);
602             item->state |= NR_ARENA_ITEM_STATE_INVALID;
603             return item->state;
604           }
605           mpb.empty = FALSE;
606         }
607         /* Do mask if needed */
608         if (item->mask) {
609           NRPixBlock tpb;
610           /* Set up yet another temporary pixblock */
611           nr_pixblock_setup_fast (&tpb, NR_PIXBLOCK_MODE_R8G8B8A8N, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
612           tpb.visible_area = pb->visible_area; 
613           state = NR_ARENA_ITEM_VIRTUAL (item->mask, render) (item->mask, &carea, &tpb, flags);
614           if (state & NR_ARENA_ITEM_STATE_INVALID) {
615             /* Clean up and return error */
616             nr_pixblock_release (&tpb);
617             nr_pixblock_release (&mpb);
618             nr_pixblock_release (&ipb);
619             if (dpb != pb) nr_pixblock_release (dpb);
620             item->state |= NR_ARENA_ITEM_STATE_INVALID;
621             return item->state;
622           }
623           /* Composite with clip */
624           if (item->clip) {
625             int x, y;
626             for (y = carea.y0; y < carea.y1; y++) {
627               unsigned char *s, *d;
628               s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
629               d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
630               for (x = carea.x0; x < carea.x1; x++) {
631                 unsigned int m;
632                 m = NR_PREMUL_112(s[0]+s[1]+s[2], s[3]);
633                 d[0] = FAST_DIV_ROUND<3*255*255>(NR_PREMUL_123(d[0], m));
634                 s += 4;
635                 d += 1;
636               }
637             }
638           } else {
639             int x, y;
640             for (y = carea.y0; y < carea.y1; y++) {
641               unsigned char *s, *d;
642               s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
643               d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
644               for (x = carea.x0; x < carea.x1; x++) {
645                 unsigned int m;
646                 m = NR_PREMUL_112(s[0]+s[1]+s[2], s[3]);
647                 d[0] = FAST_DIV_ROUND<3*255>(m);
648                 s += 4;
649                 d += 1;
650               }
651             }
652             mpb.empty = FALSE;
653           }
654           nr_pixblock_release (&tpb);
655         }
656         /* Multiply with opacity if needed */
657         if ((item->opacity != 255) && !item->render_opacity && !outline) {
658           int x, y;
659           unsigned int a;
660           a = item->opacity;
661           for (y = carea.y0; y < carea.y1; y++) {
662             unsigned char *d;
663             d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
664             for (x = carea.x0; x < carea.x1; x++) {
665               d[0] = NR_PREMUL_111 (d[0], a);
666               d += 1;
667             }
668           }
669         }
670         /* Compose rendering pixblock int destination */
671         nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
672         nr_pixblock_release (&mpb);
673         /* This pointer wouldn't be valid outside this block, so clear it */
674         item->background_pb = NULL;
675       } else {
676         /* Opacity only */
677         nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
678       }
679       nr_pixblock_release (&ipb);
680       dpb->empty = FALSE;
681     } else {
682       /* Just render */
683       state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, dpb, flags);
684       if (state & NR_ARENA_ITEM_STATE_INVALID) {
685         /* Clean up and return error */
686         if (dpb != pb) nr_pixblock_release (dpb);
687         item->state |= NR_ARENA_ITEM_STATE_INVALID;
688         return item->state;
689       }
690       dpb->empty = FALSE;
691     }
693     if (dpb != pb) {
694       /* Have to blit from cache */
695       nr_blit_pixblock_pixblock (pb, dpb);
696       nr_pixblock_release (dpb);
697       pb->empty = FALSE;
698       item->state |= NR_ARENA_ITEM_STATE_IMAGE;
699     }
700   }
701         return item->state | NR_ARENA_ITEM_STATE_RENDER;
704 unsigned int
705 nr_arena_item_invoke_clip (NRArenaItem *item, NRRectL *area, NRPixBlock *pb)
707         nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
708         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NR_ARENA_ITEM_STATE_INVALID);
709         /* we originally short-circuited if the object state included
710          * NR_ARENA_ITEM_STATE_CLIP (and showed a warning on the console);
711          * anyone know why we stopped doing so?
712          */
713         nr_return_val_if_fail ((pb->area.x1 - pb->area.x0) >= (area->x1 - area->x0), NR_ARENA_ITEM_STATE_INVALID);
714         nr_return_val_if_fail ((pb->area.y1 - pb->area.y0) >= (area->y1 - area->y0), NR_ARENA_ITEM_STATE_INVALID);
716 #ifdef NR_ARENA_ITEM_VERBOSE
717         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);
718 #endif
720         if (item->visible && nr_rect_l_test_intersect (area, &item->bbox)) {
721                 /* Need render that item */
722                 if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->clip) {
723                         return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS(item))->clip (item, area, pb);
724                 }
725         }
727         return item->state;
730 NRArenaItem *
731 nr_arena_item_invoke_pick (NRArenaItem *item, NR::Point p, double delta, unsigned int sticky)
733         nr_return_val_if_fail (item != NULL, NULL);
734         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
736         // Sometimes there's no BBOX in item->state, reason unknown (bug 992817); I made this not an assert to remove the warning
737         if (!(item->state & NR_ARENA_ITEM_STATE_BBOX) || !(item->state & NR_ARENA_ITEM_STATE_PICK))
738                 return NULL;
740         if (!sticky && !(item->visible && item->sensitive)) return NULL;
741         
742         // TODO: rewrite using NR::Rect
743         const double x = p[NR::X];
744         const double y = p[NR::Y];
745         
746         if (((x + delta) >= item->bbox.x0) &&
747             ((x - delta) <  item->bbox.x1) &&
748             ((y + delta) >= item->bbox.y0) &&
749             ((y - delta) <  item->bbox.y1)) {
750                 if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->pick)
751                         return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->pick (item, p, delta, sticky);
752         }
754         return NULL;
757 void
758 nr_arena_item_request_update (NRArenaItem *item, unsigned int reset, unsigned int propagate)
760         nr_return_if_fail (item != NULL);
761         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
762         nr_return_if_fail (!(reset & NR_ARENA_ITEM_STATE_INVALID));
764         if (propagate && !item->propagate) item->propagate = TRUE;
766         if (item->state & reset) {
767                 item->state &= ~reset;
768                 if (item->parent) {
769                         nr_arena_item_request_update (item->parent, reset, FALSE);
770                 } else {
771                         nr_arena_request_update (item->arena, item);
772                 }
773         }
776 void
777 nr_arena_item_request_render (NRArenaItem *item)
779         nr_return_if_fail (item != NULL);
780         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
782         nr_arena_request_render_rect (item->arena, &item->bbox);
785 /* Public */
787 NRArenaItem *
788 nr_arena_item_unparent (NRArenaItem *item)
790         nr_return_val_if_fail (item != NULL, NULL);
791         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
793         nr_arena_item_request_render (item);
795         if (item->parent) {
796                 nr_arena_item_remove_child (item->parent, item);
797         }
799         return NULL;
802 void
803 nr_arena_item_append_child (NRArenaItem *parent, NRArenaItem *child)
805         nr_return_if_fail (parent != NULL);
806         nr_return_if_fail (NR_IS_ARENA_ITEM (parent));
807         nr_return_if_fail (child != NULL);
808         nr_return_if_fail (NR_IS_ARENA_ITEM (child));
809         nr_return_if_fail (parent->arena == child->arena);
810         nr_return_if_fail (child->parent == NULL);
811         nr_return_if_fail (child->prev == NULL);
812         nr_return_if_fail (child->next == NULL);
814         nr_arena_item_add_child (parent, child, nr_arena_item_last_child (parent));
817 void
818 nr_arena_item_set_transform(NRArenaItem *item, NR::Matrix const &transform)
820         NRMatrix const t(transform);
821         nr_arena_item_set_transform(item, &t);
824 void
825 nr_arena_item_set_transform(NRArenaItem *item, NRMatrix const *transform)
827         const NRMatrix *ms, *md;
829         nr_return_if_fail (item != NULL);
830         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
832         if (!transform && !item->transform) return;
834         md = (item->transform) ? item->transform : &NR_MATRIX_IDENTITY;
835         ms = (transform) ? transform : &NR_MATRIX_IDENTITY;
837         if (!NR_MATRIX_DF_TEST_CLOSE (md, ms, NR_EPSILON)) {
838                 nr_arena_item_request_render (item);
839                 if (!transform || nr_matrix_test_identity (transform, NR_EPSILON)) {
840                         /* Set to identity affine */
841                         item->transform = NULL;
842                 } else {
843                         if (!item->transform) item->transform = new (GC::ATOMIC) NRMatrix();
844                         *item->transform = *transform;
845                 }
846                 nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
847         }
850 void
851 nr_arena_item_set_opacity (NRArenaItem *item, double opacity)
853         nr_return_if_fail (item != NULL);
854         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
856         nr_arena_item_request_render (item);
858         item->opacity = (unsigned int) (opacity * 255.9999);
861 void
862 nr_arena_item_set_sensitive (NRArenaItem *item, unsigned int sensitive)
864         nr_return_if_fail (item != NULL);
865         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
867         /* fixme: mess with pick/repick... */
869         item->sensitive = sensitive;
872 void
873 nr_arena_item_set_visible (NRArenaItem *item, unsigned int visible)
875         nr_return_if_fail (item != NULL);
876         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
878         item->visible = visible;
880         nr_arena_item_request_render (item);
883 void
884 nr_arena_item_set_clip (NRArenaItem *item, NRArenaItem *clip)
886         nr_return_if_fail (item != NULL);
887         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
888         nr_return_if_fail (!clip || NR_IS_ARENA_ITEM (clip));
890         if (clip != item->clip) {
891                 nr_arena_item_request_render (item);
892                 if (item->clip) item->clip = nr_arena_item_detach (item, item->clip);
893                 if (clip) item->clip = nr_arena_item_attach (item, clip, NULL, NULL);
894                 nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
895         }
898 void
899 nr_arena_item_set_mask (NRArenaItem *item, NRArenaItem *mask)
901         nr_return_if_fail (item != NULL);
902         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
903         nr_return_if_fail (!mask || NR_IS_ARENA_ITEM (mask));
905         if (mask != item->mask) {
906                 nr_arena_item_request_render (item);
907                 if (item->mask) item->mask = nr_arena_item_detach (item, item->mask);
908                 if (mask) item->mask = nr_arena_item_attach (item, mask, NULL, NULL);
909                 nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
910         }
913 void
914 nr_arena_item_set_order (NRArenaItem *item, int order)
916         NRArenaItem *children, *child, *ref;
917         int pos;
919         nr_return_if_fail (item != NULL);
920         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
922         if (!item->parent) return;
924         children = nr_arena_item_children (item->parent);
926         ref = NULL;
927         pos = 0;
928         for (child = children; child != NULL; child = child->next) {
929                 if (pos >= order) break;
930                 if (child != item) {
931                         ref = child;
932                         pos += 1;
933                 }
934         }
936         nr_arena_item_set_child_position (item->parent, item, ref);
939 /** Returns a background image for use with filter effects. */
940 NRPixBlock *nr_arena_item_get_background (NRArenaItem const *item, int depth)
942   NRPixBlock *pb;
943   if (!item->background_pb) return NULL;
944   if (item->background_new) {
945     pb = new NRPixBlock();
946     nr_pixblock_setup_fast(pb, item->background_pb->mode,
947                            item->background_pb->area.x0,
948                            item->background_pb->area.y0,
949                            item->background_pb->area.x1,
950                            item->background_pb->area.y1, true);
951   } else if (item->parent) {
952     pb = nr_arena_item_get_background(item->parent, depth + 1);
953   } else return NULL;
955   if (depth > 0)
956     nr_blit_pixblock_pixblock(pb, item->background_pb);
958   return pb;
961 /* Helpers */
963 NRArenaItem *
964 nr_arena_item_attach (NRArenaItem *parent, NRArenaItem *child, NRArenaItem *prev, NRArenaItem *next)
966         nr_return_val_if_fail (parent != NULL, NULL);
967         nr_return_val_if_fail (NR_IS_ARENA_ITEM (parent), NULL);
968         nr_return_val_if_fail (child != NULL, NULL);
969         nr_return_val_if_fail (NR_IS_ARENA_ITEM (child), NULL);
970         nr_return_val_if_fail (child->parent == NULL, NULL);
971         nr_return_val_if_fail (child->prev == NULL, NULL);
972         nr_return_val_if_fail (child->next == NULL, NULL);
973         nr_return_val_if_fail (!prev || NR_IS_ARENA_ITEM (prev), NULL);
974         nr_return_val_if_fail (!prev || (prev->parent == parent), NULL);
975         nr_return_val_if_fail (!prev || (prev->next == next), NULL);
976         nr_return_val_if_fail (!next || NR_IS_ARENA_ITEM (next), NULL);
977         nr_return_val_if_fail (!next || (next->parent == parent), NULL);
978         nr_return_val_if_fail (!next || (next->prev == prev), NULL);
980         child->parent = parent;
981         child->prev = prev;
982         child->next = next;
984         if (prev) prev->next = child;
985         if (next) next->prev = child;
987         return child;
990 NRArenaItem *
991 nr_arena_item_detach (NRArenaItem *parent, NRArenaItem *child)
993         NRArenaItem *prev, *next;
995         nr_return_val_if_fail (parent != NULL, NULL);
996         nr_return_val_if_fail (NR_IS_ARENA_ITEM (parent), NULL);
997         nr_return_val_if_fail (child != NULL, NULL);
998         nr_return_val_if_fail (NR_IS_ARENA_ITEM (child), NULL);
999         nr_return_val_if_fail (child->parent == parent, NULL);
1001         prev = child->prev;
1002         next = child->next;
1004         child->parent = NULL;
1005         child->prev = NULL;
1006         child->next = NULL;
1008         if (prev) prev->next = next;
1009         if (next) next->prev = prev;
1011         return next;
1014 /*
1015  *
1016  * caches
1017  *
1018  */
1020 #ifdef arena_item_tile_cache
1021 typedef struct cache_entry {
1022   int             key;
1023   double          score;
1024   NRArenaItem*    owner;
1025   int             th,tv;
1026   int             prev,next;
1027   NRPixBlock      ipb;
1028   bool            hasMask;
1029   NRPixBlock      mpb;
1030 } cache_entry;
1032 int hash_max=2048,hash_fill=1024;
1034 int            *keys=NULL;
1035 int            nbCch=0;
1037 int            nbEnt=0,maxEnt=0;
1038 cache_entry*   entries=NULL;
1040 //#define tile_cache_stats
1041 #ifdef tile_cache_stats
1042 double         hits=0,misses=0;
1043 int            hitMissCount=0;
1044 #endif
1046 int hash_that(NRArenaItem* owner,int th,int tv)
1048   int res=GPOINTER_TO_INT(owner);
1049   res*=17;
1050   res+=th;
1051   res*=59;
1052   res+=tv;
1053   res*=217;
1054   if ( res < 0 ) res=-res;
1055   res%=hash_max;
1056   return res;
1059 bool  test_cache(NRArenaItem* owner,int th,int tv,NRPixBlock &ipb,NRPixBlock &mpb,bool &hasMask)
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   int key=hash_that(owner,th,tv);
1068   if ( keys[key] < 0 ) {
1069 #ifdef tile_cache_stats
1070     misses+=1.0;
1071 #endif
1072     return false;
1073   }
1074   int cur=keys[key];
1075   while ( cur >= 0 && cur < nbEnt ) {
1076     if ( entries[cur].owner == owner && entries[cur].th == th && entries[cur].tv == tv ) {
1077       hasMask=entries[cur].hasMask;
1078       ipb=entries[cur].ipb;
1079       mpb=entries[cur].mpb;
1080 #ifdef tile_cache_stats
1081       hits+=1.0;
1082 #endif
1083       return true;
1084     }
1085     cur=entries[cur].next;
1086   }
1087 #ifdef tile_cache_stats
1088   misses+=1.0;
1089 #endif
1090   return false;
1092 void  remove_one_cache(int no)
1094   if ( no < 0 || no >= nbEnt ) return;
1096   nr_pixblock_release(&entries[no].ipb);
1097   if ( entries[no].hasMask ) nr_pixblock_release(&entries[no].mpb);
1099   if ( entries[no].prev >= 0 ) entries[entries[no].prev].next=entries[no].next;
1100   if ( entries[no].next >= 0 ) entries[entries[no].next].prev=entries[no].prev;
1101   if ( entries[no].prev < 0 ) keys[entries[no].key]=entries[no].next;
1102   entries[no].prev=entries[no].next=entries[no].key=-1;
1104   if ( no == nbEnt-1 ) {
1105     nbEnt--;
1106     return;
1107   }
1108   entries[no]=entries[--nbEnt];
1109   if ( entries[no].prev >= 0 ) entries[entries[no].prev].next=no;
1110   if ( entries[no].next >= 0 ) entries[entries[no].next].prev=no;
1111   if ( entries[no].prev < 0 ) keys[entries[no].key]=no;
1113 void  remove_caches(NRArenaItem* owner)
1115   if ( keys == NULL ) {
1116     hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1117     hash_fill=(hash_max*3)/4;
1118     keys=(int*)malloc(hash_max*sizeof(int));
1119     for (int i=0;i<hash_max;i++) keys[i]=-1;
1120   }
1121   for (int i=nbEnt-1;i>=0;i--) {
1122     if ( entries[i].owner == owner ) {
1123       remove_one_cache(i);
1124     }
1125   }
1127 void  age_cache(void)
1129   for (int i=0;i<nbEnt;i++) entries[i].score*=0.95;
1131 bool  insert_cache(NRArenaItem* owner,int th,int tv,NRPixBlock *ipb,NRPixBlock *mpb,double activity,double duration)
1133   if ( keys == NULL ) {
1134     hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1135     hash_fill=(hash_max*3)/4;
1136     keys=(int*)malloc(hash_max*sizeof(int));
1137     for (int i=0;i<hash_max;i++) keys[i]=-1;
1138   }
1139   for (int i=0;i<nbEnt;i++) entries[i].score*=0.95;
1140 #ifdef tile_cache_stats
1141   hits*=0.95;
1142   misses*=0.95;
1143   hitMissCount++;
1144   if ( hitMissCount > 100 ) {
1145     hitMissCount=0;
1146     printf("hit/miss = %f  used/total=%i/%i\n",(misses>0.001)?hits/misses:100000.0,nbEnt,hash_max); // localizing ok
1147   }
1148 #endif
1149   int    key=hash_that(owner,th,tv);
1150   double nScore=/*activity**/duration;
1152   if ( keys[key] >= 0 ) {
1153     int cur=keys[key];
1154     while ( cur >= 0 && cur < nbEnt ) {
1155       if ( entries[cur].owner == owner && entries[cur].th == th && entries[cur].tv == tv ) {
1156         remove_one_cache(cur);
1157         break;
1158       }
1159       cur=entries[cur].next;
1160     }
1161   }
1163   bool doAdd=false;
1164   if ( nbEnt < hash_fill ) {
1165     doAdd=true;
1166   } else {
1167     double    worstS=entries[0].score;
1168     int       worstE=0;
1169     for (int i=1;i<nbEnt;i++) {
1170       if ( entries[i].score < worstS ) {
1171         worstS=entries[i].score;
1172         worstE=i;
1173       }
1174     }
1175     if ( worstS < nScore ) {
1176       doAdd=true;
1177       remove_one_cache(worstE);
1178     }
1179   }
1180   if ( doAdd == false ) return false;
1181   if ( nbEnt >= maxEnt ) {
1182     maxEnt=2*nbEnt+1;
1183     entries=(cache_entry*)realloc(entries,maxEnt*sizeof(cache_entry));
1184   }
1185   entries[nbEnt].key=key;
1186   entries[nbEnt].score=nScore;
1187   entries[nbEnt].owner=owner;
1188   entries[nbEnt].th=th;
1189   entries[nbEnt].tv=tv;
1190   entries[nbEnt].prev=entries[nbEnt].next=-1;
1191   entries[nbEnt].ipb=*ipb;
1192   if ( mpb ) {
1193     entries[nbEnt].hasMask=true;
1194     entries[nbEnt].mpb=*mpb;
1195   } else {
1196     entries[nbEnt].hasMask=false;
1197   }
1198   entries[nbEnt].next=keys[key];
1199   if ( entries[nbEnt].next >= 0 ) entries[entries[nbEnt].next].prev=nbEnt;
1200   keys[key]=nbEnt;
1202   nbEnt++;
1203   return true;
1205 #endif