Code

svg-filters branch merged back to head
[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;
102 static void
103 nr_arena_item_private_finalize (NRObject *object)
105         NRArenaItem *item=static_cast<NRArenaItem *>(object);
107 #ifdef arena_item_tile_cache
108   remove_caches(item);
109 #endif
111         item->px = NULL;
112         item->transform = NULL;
114         ((NRObjectClass *) (parent_class))->finalize (object);
117 NRArenaItem *
118 nr_arena_item_children (NRArenaItem *item)
120         nr_return_val_if_fail (item != NULL, NULL);
121         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
123         if (NR_ARENA_ITEM_VIRTUAL (item, children))
124                 return NR_ARENA_ITEM_VIRTUAL (item, children) (item);
126         return NULL;
129 NRArenaItem *
130 nr_arena_item_last_child (NRArenaItem *item)
132         nr_return_val_if_fail (item != NULL, NULL);
133         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
135         if (NR_ARENA_ITEM_VIRTUAL (item, last_child)) {
136                 return NR_ARENA_ITEM_VIRTUAL (item, last_child) (item);
137         } else {
138                 NRArenaItem *ref;
139                 ref = nr_arena_item_children (item);
140                 if (ref) while (ref->next) ref = ref->next;
141                 return ref;
142         }
145 void
146 nr_arena_item_add_child (NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref)
148         nr_return_if_fail (item != NULL);
149         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
150         nr_return_if_fail (child != NULL);
151         nr_return_if_fail (NR_IS_ARENA_ITEM (child));
152         nr_return_if_fail (child->parent == NULL);
153         nr_return_if_fail (child->prev == NULL);
154         nr_return_if_fail (child->next == NULL);
155         nr_return_if_fail (child->arena == item->arena);
156         nr_return_if_fail (child != ref);
157         nr_return_if_fail (!ref || NR_IS_ARENA_ITEM (ref));
158         nr_return_if_fail (!ref || (ref->parent == item));
160         if (NR_ARENA_ITEM_VIRTUAL (item, add_child))
161                 NR_ARENA_ITEM_VIRTUAL (item, add_child) (item, child, ref);
164 void
165 nr_arena_item_remove_child (NRArenaItem *item, NRArenaItem *child)
167         nr_return_if_fail (item != NULL);
168         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
169         nr_return_if_fail (child != NULL);
170         nr_return_if_fail (NR_IS_ARENA_ITEM (child));
171         nr_return_if_fail (child->parent == item);
173         if (NR_ARENA_ITEM_VIRTUAL (item, remove_child))
174                 NR_ARENA_ITEM_VIRTUAL (item, remove_child) (item, child);
177 void
178 nr_arena_item_set_child_position (NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref)
180         nr_return_if_fail (item != NULL);
181         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
182         nr_return_if_fail (child != NULL);
183         nr_return_if_fail (NR_IS_ARENA_ITEM (child));
184         nr_return_if_fail (child->parent == item);
185         nr_return_if_fail (!ref || NR_IS_ARENA_ITEM (ref));
186         nr_return_if_fail (!ref || (ref->parent == item));
188         if (NR_ARENA_ITEM_VIRTUAL (item, set_child_position))
189                 NR_ARENA_ITEM_VIRTUAL (item, set_child_position) (item, child, ref);
192 NRArenaItem *
193 nr_arena_item_ref (NRArenaItem *item)
195         nr_object_ref ((NRObject *) item);
197         return item;
200 NRArenaItem *
201 nr_arena_item_unref (NRArenaItem *item)
203         nr_object_unref ((NRObject *) item);
205         return NULL;
208 unsigned int
209 nr_arena_item_invoke_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset)
211         NRGC childgc(gc);
213         nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
214         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NR_ARENA_ITEM_STATE_INVALID);
215         nr_return_val_if_fail (!(state & NR_ARENA_ITEM_STATE_INVALID), NR_ARENA_ITEM_STATE_INVALID);
217 #ifdef NR_ARENA_ITEM_DEBUG_CASCADE
218         printf ("Update %s:%p %x %x %x\n", nr_type_name_from_instance ((GTypeInstance *) item), item, state, item->state, reset);
219 #endif
221         /* return if in error */
222         if (item->state & NR_ARENA_ITEM_STATE_INVALID) return item->state;
223         /* Set reset flags according to propagation status */
224         if (item->propagate) {
225                 reset |= ~item->state;
226                 item->propagate = FALSE;
227         }
228         /* Reset our state */
229         item->state &= ~reset;
230         /* Return if NOP */
231         if (!(~item->state & state)) return item->state;
232         /* Test whether to return immediately */
233         if (area && (item->state & NR_ARENA_ITEM_STATE_BBOX)) {
234                 if (!nr_rect_l_test_intersect (area, &item->bbox)) return item->state;
235         }
237         /* Reset image cache, if not to be kept */
238         if (!(item->state & NR_ARENA_ITEM_STATE_IMAGE) && (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         }
250         /* Remember the transformation matrix */
251         item->ctm = childgc.transform;
253         /* Invoke the real method */
254         item->state = NR_ARENA_ITEM_VIRTUAL (item, update) (item, area, &childgc, state, reset);
255         if (item->state & NR_ARENA_ITEM_STATE_INVALID) return item->state;
256         /* Enlarge the bounding box to contain filter effects */
257         if(item->filter) {
258           item->filter->bbox_enlarge(item->bbox);
259         }
260             
261         /* Clipping */
262         if (item->clip) {
263                 unsigned int newstate;
264                 newstate = nr_arena_item_invoke_update (item->clip, area, &childgc, state, reset);
265                 if (newstate & NR_ARENA_ITEM_STATE_INVALID) {
266                         item->state |= NR_ARENA_ITEM_STATE_INVALID;
267                         return item->state;
268                 }
269                 nr_rect_l_intersect (&item->bbox, &item->bbox, &item->clip->bbox);
270         }
271         /* Masking */
272         if (item->mask) {
273                 unsigned int newstate;
274                 newstate = nr_arena_item_invoke_update (item->mask, area, &childgc, state, reset);
275                 if (newstate & NR_ARENA_ITEM_STATE_INVALID) {
276                         item->state |= NR_ARENA_ITEM_STATE_INVALID;
277                         return item->state;
278                 }
279                 nr_rect_l_intersect (&item->bbox, &item->bbox, &item->mask->bbox);
280         }
282         return item->state;
285 /**
286  *    Render item to pixblock.
287  *
288  *    \return Has NR_ARENA_ITEM_STATE_RENDER set on success.
289  */
291 unsigned int nr_arena_item_invoke_render(NRArenaItem *item, NRRectL const *area, NRPixBlock *pb, unsigned int flags)
293         NRRectL carea;
294         NRPixBlock *dpb;
295         NRPixBlock cpb;
296         unsigned int state;
298         nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
299         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NR_ARENA_ITEM_STATE_INVALID);
300         nr_return_val_if_fail (item->state & NR_ARENA_ITEM_STATE_BBOX, item->state);
302 #ifdef NR_ARENA_ITEM_VERBOSE
303         printf ("Invoke render %p: %d %d - %d %d\n", item, area->x0, area->y0, area->x1, area->y1);
304 #endif
306 #ifdef arena_item_tile_cache
307   item->activity*=0.5;
308 #endif
310         /* If we are outside bbox just return successfully */
311         if (!item->visible) return item->state | NR_ARENA_ITEM_STATE_RENDER;
312         nr_rect_l_intersect (&carea, area, &item->bbox);
313         if (nr_rect_l_test_empty (&carea)) return item->state | NR_ARENA_ITEM_STATE_RENDER;
314         if(item->filter) {
315           nr_rect_l_enlarge(&carea, item->filter->get_enlarge(item->ctm));
316           nr_rect_l_intersect(&carea, &carea, &item->bbox);
317         }
319         if (item->px) {
320                 /* Has cache pixblock, render this and return */
321                 nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P,
322                                           /* fixme: This probably cannot overflow, because we render only if visible */
323                                           /* fixme: and pixel cache is there only for small items */
324                                           /* fixme: But this still needs extra check (Lauris) */
325                                           item->bbox.x0, item->bbox.y0,
326                                           item->bbox.x1, item->bbox.y1,
327                                           item->px, 4 * (item->bbox.x1 - item->bbox.x0), FALSE, FALSE);
328                 nr_blit_pixblock_pixblock (pb, &cpb);
329                 nr_pixblock_release (&cpb);
330                 pb->empty = FALSE;
331                 return item->state | NR_ARENA_ITEM_STATE_RENDER;
332         }
334         dpb = pb;
335   bool  canCache=false;
336 #ifdef arena_item_tile_cache  
337   bool checkCache=false;
338   int   tile_h=0,tile_v=0;
339 #endif  
340         /* Setup cache if we can */
341         if ((!(flags & NR_ARENA_ITEM_RENDER_NO_CACHE)) &&
342             (carea.x0 <= item->bbox.x0) && (carea.y0 <= item->bbox.y0) &&
343             (carea.x1 >= item->bbox.x1) && (carea.y1 >= item->bbox.y1) &&
344             (((item->bbox.x1 - item->bbox.x0) * (item->bbox.y1 - item->bbox.y0)) <= 4096)) {
345                 // Item bbox is fully in renderable area and size is acceptable
346                 carea.x0 = item->bbox.x0;
347                 carea.y0 = item->bbox.y0;
348                 carea.x1 = item->bbox.x1;
349                 carea.y1 = item->bbox.y1;
350                 item->px = new (GC::ATOMIC) unsigned char[4 * (carea.x1 - carea.x0) * (carea.y1 - carea.y0)];
351                 nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P,
352                               carea.x0, carea.y0, carea.x1, carea.y1,
353                               item->px, 4 * (carea.x1 - carea.x0), TRUE, TRUE);
354         cpb.visible_area = pb->visible_area; 
355                 dpb = &cpb;
356                 // Set nocache flag for downstream rendering
357                 flags |= NR_ARENA_ITEM_RENDER_NO_CACHE;
358         } else {
359 #ifdef arena_item_tile_cache
360     if ( item->skipCaching ) {
361     } else {
362       int tl=area->x0&(~127);
363       int tt=area->y0&(~127);
364       if ( area->x1 <= tl+128 && area->y1 <= tt+128 ) {
365         checkCache=true;
366         tile_h=tl/128;
367         tile_v=tt/128;
368         int surf=(area->x1-area->x0)*(area->y1-area->y0);
369         if ( surf >= 4096 ) {
370           canCache=true;
371           carea.x0=tl;
372           carea.y0=tt;
373           carea.x1=tl+128;
374           carea.y1=tt+128;
375         }
376       }
377     }
378 #endif
379   }
381 #ifdef arena_item_tile_cache
382   item->activity+=1.0;
383 #endif
385 #ifdef arena_item_tile_cache
386   if ( checkCache ) {
387     NRPixBlock ipb, mpb;
388     bool       hasMask;
389     if ( test_cache(item,tile_h,tile_v,ipb,mpb,hasMask) ) {
390       // youpi! c'etait deja cache
391       if ( hasMask ) {
392         nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
393       } else if ( ((item->opacity != 255) && !item->render_opacity) ) {
394         nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
395       } else {
396         nr_blit_pixblock_pixblock (pb, &ipb);
397       }
398       pb->empty = FALSE;
399       return item->state | NR_ARENA_ITEM_STATE_RENDER;
400     }
401   }
402 #endif
403   if ( canCache ) {
404 #ifdef arena_item_tile_cache
405     // nota: exclusif de dpb != pb, donc pas de cas particulier a la fin
406     NRPixBlock ipb, mpb;
408     // struct timeval start_time,end_time;
409     // gettimeofday(&start_time,NULL);
410     GTimeVal start_time,end_time;
411     g_get_current_time (&start_time);
412     int    duration=0;
414     /* Setup and render item buffer */
415     nr_pixblock_setup_fast (&ipb, NR_PIXBLOCK_MODE_R8G8B8A8P, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
416     ipb.visible_area = pb->visible_area; 
417     state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, &ipb, flags);
418     if (state & NR_ARENA_ITEM_STATE_INVALID) {
419       /* Clean up and return error */
420       nr_pixblock_release (&ipb);
421       if (dpb != pb) nr_pixblock_release (dpb);
422       item->state |= NR_ARENA_ITEM_STATE_INVALID;
423       return item->state;
424     }
425     ipb.empty = FALSE;
427     if (item->clip || item->mask) {
428       /* Setup mask pixblock */
429       nr_pixblock_setup_fast (&mpb, NR_PIXBLOCK_MODE_A8, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
430       mpb.visible_area = pb->visible_area; 
431       /* Do clip if needed */
432       if (item->clip) {
433         state = nr_arena_item_invoke_clip (item->clip, &carea, &mpb);
434         if (state & NR_ARENA_ITEM_STATE_INVALID) {
435           /* Clean up and return error */
436           nr_pixblock_release (&mpb);
437           nr_pixblock_release (&ipb);
438           if (dpb != pb) nr_pixblock_release (dpb);
439           item->state |= NR_ARENA_ITEM_STATE_INVALID;
440           return item->state;
441         }
442         mpb.empty = FALSE;
443       }
444       /* Do mask if needed */
445       if (item->mask) {
446         NRPixBlock tpb;
447         /* Set up yet another temporary pixblock */
448         nr_pixblock_setup_fast (&tpb, NR_PIXBLOCK_MODE_R8G8B8A8N, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
449         tpb.visible_area = pb->visible_area; 
450         state = NR_ARENA_ITEM_VIRTUAL (item->mask, render) (item->mask, &carea, &tpb, flags);
451         if (state & NR_ARENA_ITEM_STATE_INVALID) {
452           /* Clean up and return error */
453           nr_pixblock_release (&tpb);
454           nr_pixblock_release (&mpb);
455           nr_pixblock_release (&ipb);
456           if (dpb != pb) nr_pixblock_release (dpb);
457           item->state |= NR_ARENA_ITEM_STATE_INVALID;
458           return item->state;
459         }
460         /* Composite with clip */
461         if (item->clip) {
462           int x, y;
463           for (y = carea.y0; y < carea.y1; y++) {
464             unsigned char *s, *d;
465             s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
466             d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
467             for (x = carea.x0; x < carea.x1; x++) {
468               unsigned int m;
469               m = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
470               d[0] = NR_PREMUL (d[0], m);
471               s += 4;
472               d += 1;
473             }
474           }
475         } else {
476           int x, y;
477           for (y = carea.y0; y < carea.y1; y++) {
478             unsigned char *s, *d;
479             s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
480             d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
481             for (x = carea.x0; x < carea.x1; x++) {
482               unsigned int m;
483               m = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
484               d[0] = m;
485               s += 4;
486               d += 1;
487             }
488           }
489           mpb.empty = FALSE;
490         }
491         nr_pixblock_release (&tpb);
492       }
493       /* Multiply with opacity if needed */
494       if ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE) {
495         int x, y;
496         unsigned int a;
497         a = item->opacity;
498         for (y = carea.y0; y < carea.y1; y++) {
499           unsigned char *d;
500           d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
501           for (x = carea.x0; x < carea.x1; x++) {
502             d[0] = NR_PREMUL (d[0], a);
503             d += 1;
504           }
505         }
506       }
507       /* Compose rendering pixblock int destination */
508       // gettimeofday(&end_time,NULL);
509       g_get_current_time (&end_time);
510       duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
511       if ( !(ipb.empty) ) {
512         nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
513         if ( insert_cache(item,tile_h,tile_v,&ipb,&mpb,item->activity,(double)duration) ) {
514         } else {
515           nr_pixblock_release (&mpb);
516           nr_pixblock_release (&ipb);
517         }
518         dpb->empty = FALSE;
519       } else {
520         nr_pixblock_release (&ipb);
521       }
522     } else if ( ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE) ) {
523       /* Opacity only */
524       // gettimeofday(&end_time,NULL);
525       g_get_current_time (&end_time);
526       duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
527       if ( !(ipb.empty) ) {
528         nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
529         if ( insert_cache(item,tile_h,tile_v,&ipb,NULL,item->activity,(double)duration) ) {
530         } else {
531           nr_pixblock_release (&ipb);
532         }
533         dpb->empty = FALSE;
534       } else {
535         nr_pixblock_release (&ipb);
536       }
537     } else {
538       // gettimeofday(&end_time,NULL);
539       g_get_current_time (&end_time);
540       duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
541       if ( !(ipb.empty) ) {
542         nr_blit_pixblock_pixblock (dpb, &ipb);
543         if ( insert_cache(item,tile_h,tile_v,&ipb,NULL,item->activity,(double)duration) ) {
544         } else {
545           nr_pixblock_release (&ipb);
546         }
547         dpb->empty = FALSE;
548       } else {
549         nr_pixblock_release (&ipb);
550       }
551     }
552 #endif
553   } else {
554     /* Determine, whether we need temporary buffer */
555     if (item->clip || item->mask || ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE) || item->filter) {
556       NRPixBlock ipb, mpb;
558       /* Setup and render item buffer */
559       nr_pixblock_setup_fast (&ipb, NR_PIXBLOCK_MODE_R8G8B8A8P, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
560       ipb.visible_area = pb->visible_area; 
561       state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, &ipb, flags);
562       if (state & NR_ARENA_ITEM_STATE_INVALID) {
563         /* Clean up and return error */
564         nr_pixblock_release (&ipb);
565         if (dpb != pb) nr_pixblock_release (dpb);
566         item->state |= NR_ARENA_ITEM_STATE_INVALID;
567         return item->state;
568       }
569       ipb.empty = FALSE;
571       /* Run filtering test, if a filter is set for this object */
572       if(item->filter) {
573         item->filter->render(item, &ipb);
574       }
576       if (item->clip || item->mask) {
577         /* Setup mask pixblock */
578         nr_pixblock_setup_fast (&mpb, NR_PIXBLOCK_MODE_A8, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
579         mpb.visible_area = pb->visible_area; 
580         /* Do clip if needed */
581         if (item->clip) {
582           state = nr_arena_item_invoke_clip (item->clip, &carea, &mpb);
583           if (state & NR_ARENA_ITEM_STATE_INVALID) {
584             /* Clean up and return error */
585             nr_pixblock_release (&mpb);
586             nr_pixblock_release (&ipb);
587             if (dpb != pb) nr_pixblock_release (dpb);
588             item->state |= NR_ARENA_ITEM_STATE_INVALID;
589             return item->state;
590           }
591           mpb.empty = FALSE;
592         }
593         /* Do mask if needed */
594         if (item->mask) {
595           NRPixBlock tpb;
596           /* Set up yet another temporary pixblock */
597           nr_pixblock_setup_fast (&tpb, NR_PIXBLOCK_MODE_R8G8B8A8N, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
598           tpb.visible_area = pb->visible_area; 
599           state = NR_ARENA_ITEM_VIRTUAL (item->mask, render) (item->mask, &carea, &tpb, flags);
600           if (state & NR_ARENA_ITEM_STATE_INVALID) {
601             /* Clean up and return error */
602             nr_pixblock_release (&tpb);
603             nr_pixblock_release (&mpb);
604             nr_pixblock_release (&ipb);
605             if (dpb != pb) nr_pixblock_release (dpb);
606             item->state |= NR_ARENA_ITEM_STATE_INVALID;
607             return item->state;
608           }
609           /* Composite with clip */
610           if (item->clip) {
611             int x, y;
612             for (y = carea.y0; y < carea.y1; y++) {
613               unsigned char *s, *d;
614               s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
615               d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
616               for (x = carea.x0; x < carea.x1; x++) {
617                 unsigned int m;
618                 m = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
619                 d[0] = NR_PREMUL (d[0], m);
620                 s += 4;
621                 d += 1;
622               }
623             }
624           } else {
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 = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
633                 d[0] = m;
634                 s += 4;
635                 d += 1;
636               }
637             }
638             mpb.empty = FALSE;
639           }
640           nr_pixblock_release (&tpb);
641         }
642         /* Multiply with opacity if needed */
643         if ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE) {
644           int x, y;
645           unsigned int a;
646           a = item->opacity;
647           for (y = carea.y0; y < carea.y1; y++) {
648             unsigned char *d;
649             d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
650             for (x = carea.x0; x < carea.x1; x++) {
651               d[0] = NR_PREMUL (d[0], a);
652               d += 1;
653             }
654           }
655         }
656         /* Compose rendering pixblock int destination */
657         nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
658         nr_pixblock_release (&mpb);
659       } else {
660         /* Opacity only */
661         nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
662       }
663       nr_pixblock_release (&ipb);
664       dpb->empty = FALSE;
665     } else {
666       /* Just render */
667       state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, dpb, flags);
668       if (state & NR_ARENA_ITEM_STATE_INVALID) {
669         /* Clean up and return error */
670         if (dpb != pb) nr_pixblock_release (dpb);
671         item->state |= NR_ARENA_ITEM_STATE_INVALID;
672         return item->state;
673       }
674       dpb->empty = FALSE;
675     }
677     if (dpb != pb) {
678       /* Have to blit from cache */
679       nr_blit_pixblock_pixblock (pb, dpb);
680       nr_pixblock_release (dpb);
681       pb->empty = FALSE;
682       item->state |= NR_ARENA_ITEM_STATE_IMAGE;
683     }
684   }
685         return item->state | NR_ARENA_ITEM_STATE_RENDER;
688 unsigned int
689 nr_arena_item_invoke_clip (NRArenaItem *item, NRRectL *area, NRPixBlock *pb)
691         nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
692         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NR_ARENA_ITEM_STATE_INVALID);
693         /* we originally short-circuited if the object state included
694          * NR_ARENA_ITEM_STATE_CLIP (and showed a warning on the console);
695          * anyone know why we stopped doing so?
696          */
697         nr_return_val_if_fail ((pb->area.x1 - pb->area.x0) >= (area->x1 - area->x0), NR_ARENA_ITEM_STATE_INVALID);
698         nr_return_val_if_fail ((pb->area.y1 - pb->area.y0) >= (area->y1 - area->y0), NR_ARENA_ITEM_STATE_INVALID);
700 #ifdef NR_ARENA_ITEM_VERBOSE
701         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);
702 #endif
704         if (item->visible && nr_rect_l_test_intersect (area, &item->bbox)) {
705                 /* Need render that item */
706                 if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->clip) {
707                         return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS(item))->clip (item, area, pb);
708                 }
709         }
711         return item->state;
714 NRArenaItem *
715 nr_arena_item_invoke_pick (NRArenaItem *item, NR::Point p, double delta, unsigned int sticky)
717         nr_return_val_if_fail (item != NULL, NULL);
718         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
720         // Sometimes there's no BBOX in item->state, reason unknown (bug 992817); I made this not an assert to remove the warning
721         if (!(item->state & NR_ARENA_ITEM_STATE_BBOX) || !(item->state & NR_ARENA_ITEM_STATE_PICK))
722                 return NULL;
724         if (!sticky && !(item->visible && item->sensitive)) return NULL;
725         
726         // TODO: rewrite using NR::Rect
727         const double x = p[NR::X];
728         const double y = p[NR::Y];
729         
730         if (((x + delta) >= item->bbox.x0) &&
731             ((x - delta) <  item->bbox.x1) &&
732             ((y + delta) >= item->bbox.y0) &&
733             ((y - delta) <  item->bbox.y1)) {
734                 if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->pick)
735                         return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->pick (item, p, delta, sticky);
736         }
738         return NULL;
741 void
742 nr_arena_item_request_update (NRArenaItem *item, unsigned int reset, unsigned int propagate)
744         nr_return_if_fail (item != NULL);
745         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
746         nr_return_if_fail (!(reset & NR_ARENA_ITEM_STATE_INVALID));
748         if (propagate && !item->propagate) item->propagate = TRUE;
750         if (item->state & reset) {
751                 item->state &= ~reset;
752                 if (item->parent) {
753                         nr_arena_item_request_update (item->parent, reset, FALSE);
754                 } else {
755                         nr_arena_request_update (item->arena, item);
756                 }
757         }
760 void
761 nr_arena_item_request_render (NRArenaItem *item)
763         nr_return_if_fail (item != NULL);
764         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
766         nr_arena_request_render_rect (item->arena, &item->bbox);
769 /* Public */
771 NRArenaItem *
772 nr_arena_item_unparent (NRArenaItem *item)
774         nr_return_val_if_fail (item != NULL, NULL);
775         nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
777         nr_arena_item_request_render (item);
779         if (item->parent) {
780                 nr_arena_item_remove_child (item->parent, item);
781         }
783         return NULL;
786 void
787 nr_arena_item_append_child (NRArenaItem *parent, NRArenaItem *child)
789         nr_return_if_fail (parent != NULL);
790         nr_return_if_fail (NR_IS_ARENA_ITEM (parent));
791         nr_return_if_fail (child != NULL);
792         nr_return_if_fail (NR_IS_ARENA_ITEM (child));
793         nr_return_if_fail (parent->arena == child->arena);
794         nr_return_if_fail (child->parent == NULL);
795         nr_return_if_fail (child->prev == NULL);
796         nr_return_if_fail (child->next == NULL);
798         nr_arena_item_add_child (parent, child, nr_arena_item_last_child (parent));
801 void
802 nr_arena_item_set_transform(NRArenaItem *item, NR::Matrix const &transform)
804         NRMatrix const t(transform);
805         nr_arena_item_set_transform(item, &t);
808 void
809 nr_arena_item_set_transform(NRArenaItem *item, NRMatrix const *transform)
811         const NRMatrix *ms, *md;
813         nr_return_if_fail (item != NULL);
814         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
816         if (!transform && !item->transform) return;
818         md = (item->transform) ? item->transform : &NR_MATRIX_IDENTITY;
819         ms = (transform) ? transform : &NR_MATRIX_IDENTITY;
821         if (!NR_MATRIX_DF_TEST_CLOSE (md, ms, NR_EPSILON)) {
822                 nr_arena_item_request_render (item);
823                 if (!transform || nr_matrix_test_identity (transform, NR_EPSILON)) {
824                         /* Set to identity affine */
825                         item->transform = NULL;
826                 } else {
827                         if (!item->transform) item->transform = new (GC::ATOMIC) NRMatrix();
828                         *item->transform = *transform;
829                 }
830                 nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
831         }
834 void
835 nr_arena_item_set_opacity (NRArenaItem *item, double opacity)
837         nr_return_if_fail (item != NULL);
838         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
840         nr_arena_item_request_render (item);
842         item->opacity = (unsigned int) (opacity * 255.9999);
845 void
846 nr_arena_item_set_sensitive (NRArenaItem *item, unsigned int sensitive)
848         nr_return_if_fail (item != NULL);
849         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
851         /* fixme: mess with pick/repick... */
853         item->sensitive = sensitive;
856 void
857 nr_arena_item_set_visible (NRArenaItem *item, unsigned int visible)
859         nr_return_if_fail (item != NULL);
860         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
862         item->visible = visible;
864         nr_arena_item_request_render (item);
867 void
868 nr_arena_item_set_clip (NRArenaItem *item, NRArenaItem *clip)
870         nr_return_if_fail (item != NULL);
871         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
872         nr_return_if_fail (!clip || NR_IS_ARENA_ITEM (clip));
874         if (clip != item->clip) {
875                 nr_arena_item_request_render (item);
876                 if (item->clip) item->clip = nr_arena_item_detach (item, item->clip);
877                 if (clip) item->clip = nr_arena_item_attach (item, clip, NULL, NULL);
878                 nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
879         }
882 void
883 nr_arena_item_set_mask (NRArenaItem *item, NRArenaItem *mask)
885         nr_return_if_fail (item != NULL);
886         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
887         nr_return_if_fail (!mask || NR_IS_ARENA_ITEM (mask));
889         if (mask != item->mask) {
890                 nr_arena_item_request_render (item);
891                 if (item->mask) item->mask = nr_arena_item_detach (item, item->mask);
892                 if (mask) item->mask = nr_arena_item_attach (item, mask, NULL, NULL);
893                 nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
894         }
897 void
898 nr_arena_item_set_order (NRArenaItem *item, int order)
900         NRArenaItem *children, *child, *ref;
901         int pos;
903         nr_return_if_fail (item != NULL);
904         nr_return_if_fail (NR_IS_ARENA_ITEM (item));
906         if (!item->parent) return;
908         children = nr_arena_item_children (item->parent);
910         ref = NULL;
911         pos = 0;
912         for (child = children; child != NULL; child = child->next) {
913                 if (pos >= order) break;
914                 if (child != item) {
915                         ref = child;
916                         pos += 1;
917                 }
918         }
920         nr_arena_item_set_child_position (item->parent, item, ref);
923 /* Helpers */
925 NRArenaItem *
926 nr_arena_item_attach (NRArenaItem *parent, NRArenaItem *child, NRArenaItem *prev, NRArenaItem *next)
928         nr_return_val_if_fail (parent != NULL, NULL);
929         nr_return_val_if_fail (NR_IS_ARENA_ITEM (parent), NULL);
930         nr_return_val_if_fail (child != NULL, NULL);
931         nr_return_val_if_fail (NR_IS_ARENA_ITEM (child), NULL);
932         nr_return_val_if_fail (child->parent == NULL, NULL);
933         nr_return_val_if_fail (child->prev == NULL, NULL);
934         nr_return_val_if_fail (child->next == NULL, NULL);
935         nr_return_val_if_fail (!prev || NR_IS_ARENA_ITEM (prev), NULL);
936         nr_return_val_if_fail (!prev || (prev->parent == parent), NULL);
937         nr_return_val_if_fail (!prev || (prev->next == next), NULL);
938         nr_return_val_if_fail (!next || NR_IS_ARENA_ITEM (next), NULL);
939         nr_return_val_if_fail (!next || (next->parent == parent), NULL);
940         nr_return_val_if_fail (!next || (next->prev == prev), NULL);
942         child->parent = parent;
943         child->prev = prev;
944         child->next = next;
946         if (prev) prev->next = child;
947         if (next) next->prev = child;
949         return child;
952 NRArenaItem *
953 nr_arena_item_detach (NRArenaItem *parent, NRArenaItem *child)
955         NRArenaItem *prev, *next;
957         nr_return_val_if_fail (parent != NULL, NULL);
958         nr_return_val_if_fail (NR_IS_ARENA_ITEM (parent), NULL);
959         nr_return_val_if_fail (child != NULL, NULL);
960         nr_return_val_if_fail (NR_IS_ARENA_ITEM (child), NULL);
961         nr_return_val_if_fail (child->parent == parent, NULL);
963         prev = child->prev;
964         next = child->next;
966         child->parent = NULL;
967         child->prev = NULL;
968         child->next = NULL;
970         if (prev) prev->next = next;
971         if (next) next->prev = prev;
973         return next;
976 /*
977  *
978  * caches
979  *
980  */
982 #ifdef arena_item_tile_cache
983 typedef struct cache_entry {
984   int             key;
985   double          score;
986   NRArenaItem*    owner;
987   int             th,tv;
988   int             prev,next;
989   NRPixBlock      ipb;
990   bool            hasMask;
991   NRPixBlock      mpb;
992 } cache_entry;
994 int hash_max=2048,hash_fill=1024;
996 int            *keys=NULL;
997 int            nbCch=0;
999 int            nbEnt=0,maxEnt=0;
1000 cache_entry*   entries=NULL;
1002 //#define tile_cache_stats
1003 #ifdef tile_cache_stats
1004 double         hits=0,misses=0;
1005 int            hitMissCount=0;
1006 #endif
1008 int hash_that(NRArenaItem* owner,int th,int tv)
1010   int res=GPOINTER_TO_INT(owner);
1011   res*=17;
1012   res+=th;
1013   res*=59;
1014   res+=tv;
1015   res*=217;
1016   if ( res < 0 ) res=-res;
1017   res%=hash_max;
1018   return res;
1021 bool  test_cache(NRArenaItem* owner,int th,int tv,NRPixBlock &ipb,NRPixBlock &mpb,bool &hasMask)
1023   if ( keys == NULL ) {
1024     hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1025     hash_fill=(hash_max*3)/4;
1026     keys=(int*)malloc(hash_max*sizeof(int));
1027     for (int i=0;i<hash_max;i++) keys[i]=-1;
1028   }
1029   int key=hash_that(owner,th,tv);
1030   if ( keys[key] < 0 ) {
1031 #ifdef tile_cache_stats
1032     misses+=1.0;
1033 #endif
1034     return false;
1035   }
1036   int cur=keys[key];
1037   while ( cur >= 0 && cur < nbEnt ) {
1038     if ( entries[cur].owner == owner && entries[cur].th == th && entries[cur].tv == tv ) {
1039       hasMask=entries[cur].hasMask;
1040       ipb=entries[cur].ipb;
1041       mpb=entries[cur].mpb;
1042 #ifdef tile_cache_stats
1043       hits+=1.0;
1044 #endif
1045       return true;
1046     }
1047     cur=entries[cur].next;
1048   }
1049 #ifdef tile_cache_stats
1050   misses+=1.0;
1051 #endif
1052   return false;
1054 void  remove_one_cache(int no)
1056   if ( no < 0 || no >= nbEnt ) return;
1058   nr_pixblock_release(&entries[no].ipb);
1059   if ( entries[no].hasMask ) nr_pixblock_release(&entries[no].mpb);
1061   if ( entries[no].prev >= 0 ) entries[entries[no].prev].next=entries[no].next;
1062   if ( entries[no].next >= 0 ) entries[entries[no].next].prev=entries[no].prev;
1063   if ( entries[no].prev < 0 ) keys[entries[no].key]=entries[no].next;
1064   entries[no].prev=entries[no].next=entries[no].key=-1;
1066   if ( no == nbEnt-1 ) {
1067     nbEnt--;
1068     return;
1069   }
1070   entries[no]=entries[--nbEnt];
1071   if ( entries[no].prev >= 0 ) entries[entries[no].prev].next=no;
1072   if ( entries[no].next >= 0 ) entries[entries[no].next].prev=no;
1073   if ( entries[no].prev < 0 ) keys[entries[no].key]=no;
1075 void  remove_caches(NRArenaItem* owner)
1077   if ( keys == NULL ) {
1078     hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1079     hash_fill=(hash_max*3)/4;
1080     keys=(int*)malloc(hash_max*sizeof(int));
1081     for (int i=0;i<hash_max;i++) keys[i]=-1;
1082   }
1083   for (int i=nbEnt-1;i>=0;i--) {
1084     if ( entries[i].owner == owner ) {
1085       remove_one_cache(i);
1086     }
1087   }
1089 void  age_cache(void)
1091   for (int i=0;i<nbEnt;i++) entries[i].score*=0.95;
1093 bool  insert_cache(NRArenaItem* owner,int th,int tv,NRPixBlock *ipb,NRPixBlock *mpb,double activity,double duration)
1095   if ( keys == NULL ) {
1096     hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1097     hash_fill=(hash_max*3)/4;
1098     keys=(int*)malloc(hash_max*sizeof(int));
1099     for (int i=0;i<hash_max;i++) keys[i]=-1;
1100   }
1101   for (int i=0;i<nbEnt;i++) entries[i].score*=0.95;
1102 #ifdef tile_cache_stats
1103   hits*=0.95;
1104   misses*=0.95;
1105   hitMissCount++;
1106   if ( hitMissCount > 100 ) {
1107     hitMissCount=0;
1108     printf("hit/miss = %f  used/total=%i/%i\n",(misses>0.001)?hits/misses:100000.0,nbEnt,hash_max); // localizing ok
1109   }
1110 #endif
1111   int    key=hash_that(owner,th,tv);
1112   double nScore=/*activity**/duration;
1114   if ( keys[key] >= 0 ) {
1115     int cur=keys[key];
1116     while ( cur >= 0 && cur < nbEnt ) {
1117       if ( entries[cur].owner == owner && entries[cur].th == th && entries[cur].tv == tv ) {
1118         remove_one_cache(cur);
1119         break;
1120       }
1121       cur=entries[cur].next;
1122     }
1123   }
1125   bool doAdd=false;
1126   if ( nbEnt < hash_fill ) {
1127     doAdd=true;
1128   } else {
1129     double    worstS=entries[0].score;
1130     int       worstE=0;
1131     for (int i=1;i<nbEnt;i++) {
1132       if ( entries[i].score < worstS ) {
1133         worstS=entries[i].score;
1134         worstE=i;
1135       }
1136     }
1137     if ( worstS < nScore ) {
1138       doAdd=true;
1139       remove_one_cache(worstE);
1140     }
1141   }
1142   if ( doAdd == false ) return false;
1143   if ( nbEnt >= maxEnt ) {
1144     maxEnt=2*nbEnt+1;
1145     entries=(cache_entry*)realloc(entries,maxEnt*sizeof(cache_entry));
1146   }
1147   entries[nbEnt].key=key;
1148   entries[nbEnt].score=nScore;
1149   entries[nbEnt].owner=owner;
1150   entries[nbEnt].th=th;
1151   entries[nbEnt].tv=tv;
1152   entries[nbEnt].prev=entries[nbEnt].next=-1;
1153   entries[nbEnt].ipb=*ipb;
1154   if ( mpb ) {
1155     entries[nbEnt].hasMask=true;
1156     entries[nbEnt].mpb=*mpb;
1157   } else {
1158     entries[nbEnt].hasMask=false;
1159   }
1160   entries[nbEnt].next=keys[key];
1161   if ( entries[nbEnt].next >= 0 ) entries[entries[nbEnt].next].prev=nbEnt;
1162   keys[key]=nbEnt;
1164   nbEnt++;
1165   return true;
1167 #endif