Code

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