Code

indentation; declarations to first uses; deal with the possible failure to create...
[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,
37               NRPixBlock *mpb, double activity, double duration);
38 void remove_caches (NRArenaItem *owner);
39 bool test_cache (NRArenaItem *owner, int th, int tv, NRPixBlock & ipb,
40             NRPixBlock &mpb, bool &hasMask);
41 #endif
43 static NRObjectClass *parent_class;
45 NRType 
46 nr_arena_item_get_type (void)
47 {
48     static NRType type = 0;
49     if (!type) {
50         type = nr_object_register_type (NR_TYPE_OBJECT,
51                                         "NRArenaItem",
52                                         sizeof (NRArenaItemClass),
53                                         sizeof (NRArenaItem),
54                                         (void (*)(NRObjectClass *))
55                                         nr_arena_item_class_init,
56                                         (void (*)(NRObject *))
57                                         nr_arena_item_init);
58     }
59     return type;
60 }
62 static void
63 nr_arena_item_class_init (NRArenaItemClass *klass)
64 {
65     NRObjectClass *object_class;
67     object_class = (NRObjectClass *) klass;
69     parent_class = ((NRObjectClass *) klass)->parent;
71     object_class->finalize = nr_arena_item_private_finalize;
72     object_class->cpp_ctor = NRObject::invoke_ctor < NRArenaItem >;
73 }
75 static void
76 nr_arena_item_init (NRArenaItem *item)
77 {
78     item->arena = NULL;
79     item->parent = NULL;
80     item->next = item->prev = NULL;
82     item->key = 0;
84     item->state = 0;
85     item->sensitive = TRUE;
86     item->visible = TRUE;
88     memset (&item->bbox, 0, sizeof (item->bbox));
89     item->transform = NULL;
90     item->opacity = 255;
91     item->render_opacity = FALSE;
93 #ifdef arena_item_tile_cache
94     item->activity = 0.0;
95     item->skipCaching = false;
96 #endif
98     item->transform = NULL;
99     item->clip = NULL;
100     item->mask = NULL;
101     item->px = NULL;
102     item->data = NULL;
103     item->filter = NULL;
104     item->background_pb = NULL;
105     item->background_new = false;
108 static void
109 nr_arena_item_private_finalize (NRObject *object)
111     NRArenaItem *item = static_cast < NRArenaItem * >(object);
113 #ifdef arena_item_tile_cache
114     remove_caches (item);
115 #endif
117     item->px = NULL;
118     item->transform = NULL;
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 = nr_arena_item_children (item);
145         if (ref)
146             while (ref->next)
147                 ref = ref->next;
148         return ref;
149     }
152 void
153 nr_arena_item_add_child (NRArenaItem *item, NRArenaItem *child,
154                          NRArenaItem *ref)
156     nr_return_if_fail (item != NULL);
157     nr_return_if_fail (NR_IS_ARENA_ITEM (item));
158     nr_return_if_fail (child != NULL);
159     nr_return_if_fail (NR_IS_ARENA_ITEM (child));
160     nr_return_if_fail (child->parent == NULL);
161     nr_return_if_fail (child->prev == NULL);
162     nr_return_if_fail (child->next == NULL);
163     nr_return_if_fail (child->arena == item->arena);
164     nr_return_if_fail (child != ref);
165     nr_return_if_fail (!ref || NR_IS_ARENA_ITEM (ref));
166     nr_return_if_fail (!ref || (ref->parent == item));
168     if (NR_ARENA_ITEM_VIRTUAL (item, add_child))
169         NR_ARENA_ITEM_VIRTUAL (item, add_child) (item, child, ref);
172 void
173 nr_arena_item_remove_child (NRArenaItem *item, NRArenaItem *child)
175     nr_return_if_fail (item != NULL);
176     nr_return_if_fail (NR_IS_ARENA_ITEM (item));
177     nr_return_if_fail (child != NULL);
178     nr_return_if_fail (NR_IS_ARENA_ITEM (child));
179     nr_return_if_fail (child->parent == item);
181     if (NR_ARENA_ITEM_VIRTUAL (item, remove_child))
182         NR_ARENA_ITEM_VIRTUAL (item, remove_child) (item, child);
185 void
186 nr_arena_item_set_child_position (NRArenaItem *item, NRArenaItem *child,
187                                   NRArenaItem *ref)
189     nr_return_if_fail (item != NULL);
190     nr_return_if_fail (NR_IS_ARENA_ITEM (item));
191     nr_return_if_fail (child != NULL);
192     nr_return_if_fail (NR_IS_ARENA_ITEM (child));
193     nr_return_if_fail (child->parent == item);
194     nr_return_if_fail (!ref || NR_IS_ARENA_ITEM (ref));
195     nr_return_if_fail (!ref || (ref->parent == item));
197     if (NR_ARENA_ITEM_VIRTUAL (item, set_child_position))
198         NR_ARENA_ITEM_VIRTUAL (item, set_child_position) (item, child, ref);
201 NRArenaItem *
202 nr_arena_item_ref (NRArenaItem *item)
204     nr_object_ref ((NRObject *) item);
206     return item;
209 NRArenaItem *
210 nr_arena_item_unref (NRArenaItem *item)
212     nr_object_unref ((NRObject *) item);
214     return NULL;
217 unsigned int
218 nr_arena_item_invoke_update (NRArenaItem *item, NRRectL *area, NRGC *gc,
219                              unsigned int state, unsigned int reset)
221     NRGC childgc (gc);
223     nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
224     nr_return_val_if_fail (NR_IS_ARENA_ITEM (item),
225                            NR_ARENA_ITEM_STATE_INVALID);
226     nr_return_val_if_fail (!(state & NR_ARENA_ITEM_STATE_INVALID),
227                            NR_ARENA_ITEM_STATE_INVALID);
229 #ifdef NR_ARENA_ITEM_DEBUG_CASCADE
230     printf ("Update %s:%p %x %x %x\n",
231             nr_type_name_from_instance ((GTypeInstance *) item), item, state,
232             item->state, reset);
233 #endif
235     /* return if in error */
236     if (item->state & NR_ARENA_ITEM_STATE_INVALID)
237         return item->state;
238     /* Set reset flags according to propagation status */
239     if (item->propagate) {
240         reset |= ~item->state;
241         item->propagate = FALSE;
242     }
243     /* Reset our state */
244     item->state &= ~reset;
245     /* Return if NOP */
246     if (!(~item->state & state))
247         return item->state;
248     /* Test whether to return immediately */
249     if (area && (item->state & NR_ARENA_ITEM_STATE_BBOX)) {
250         if (!nr_rect_l_test_intersect (area, &item->bbox))
251             return item->state;
252     }
254     /* Reset image cache, if not to be kept */
255     if (!(item->state & NR_ARENA_ITEM_STATE_IMAGE) && (item->px)) {
256         item->px = NULL;
257     }
258 #ifdef arena_item_tile_cache
259     remove_caches (item);
260 #endif
262     /* Set up local gc */
263     childgc = *gc;
264     if (item->transform) {
265         nr_matrix_multiply (&childgc.transform, item->transform,
266                             &childgc.transform);
267     }
268     /* Remember the transformation matrix */
269     item->ctm = childgc.transform;
271     /* Invoke the real method */
272     item->state =
273         NR_ARENA_ITEM_VIRTUAL (item, update) (item, area, &childgc, state,
274                                               reset);
275     if (item->state & NR_ARENA_ITEM_STATE_INVALID)
276         return item->state;
277     /* Enlarge the bounding box to contain filter effects */
278     if (item->filter) {
279         item->filter->bbox_enlarge (item->bbox);
280     }
282     /* Clipping */
283     if (item->clip) {
284         unsigned int newstate = nr_arena_item_invoke_update (item->clip, area, &childgc, state, reset);
285         if (newstate & NR_ARENA_ITEM_STATE_INVALID) {
286             item->state |= NR_ARENA_ITEM_STATE_INVALID;
287             return item->state;
288         }
289         nr_rect_l_intersect (&item->bbox, &item->bbox, &item->clip->bbox);
290     }
291     /* Masking */
292     if (item->mask) {
293         unsigned int newstate = nr_arena_item_invoke_update (item->mask, area, &childgc, state, reset);
294         if (newstate & NR_ARENA_ITEM_STATE_INVALID) {
295             item->state |= NR_ARENA_ITEM_STATE_INVALID;
296             return item->state;
297         }
298         nr_rect_l_intersect (&item->bbox, &item->bbox, &item->mask->bbox);
299     }
301     return item->state;
304 /**
305  *    Render item to pixblock.
306  *
307  *    \return Has NR_ARENA_ITEM_STATE_RENDER set on success.
308  */
310 unsigned int
311 nr_arena_item_invoke_render (NRArenaItem *item, NRRectL const *area,
312                              NRPixBlock *pb, unsigned int flags)
314    bool outline = (item->arena->rendermode == RENDERMODE_OUTLINE);
316     nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
317     nr_return_val_if_fail (NR_IS_ARENA_ITEM (item),
318                            NR_ARENA_ITEM_STATE_INVALID);
319     nr_return_val_if_fail (item->state & NR_ARENA_ITEM_STATE_BBOX,
320                            item->state);
322 #ifdef NR_ARENA_ITEM_VERBOSE
323     printf ("Invoke render %p: %d %d - %d %d\n", item, area->x0, area->y0,
324             area->x1, area->y1);
325 #endif
327 #ifdef arena_item_tile_cache
328     item->activity *= 0.5;
329 #endif
331     /* If we are outside bbox just return successfully */
332     if (!item->visible)
333         return item->state | NR_ARENA_ITEM_STATE_RENDER;
335     NRRectL carea;
336     nr_rect_l_intersect (&carea, area, &item->bbox);
337     if (nr_rect_l_test_empty (&carea))
338         return item->state | NR_ARENA_ITEM_STATE_RENDER;
339     if (item->filter && !outline) {
340         nr_rect_l_enlarge (&carea, item->filter->get_enlarge (item->ctm));
341         nr_rect_l_intersect (&carea, &carea, &item->bbox);
342     }
344     NRPixBlock cpb;
345     if (item->px) {
346         /* Has cache pixblock, render this and return */
347         nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P,
348                                   /* fixme: This probably cannot overflow, because we render only if visible */
349                                   /* fixme: and pixel cache is there only for small items */
350                                   /* fixme: But this still needs extra check (Lauris) */
351                                   item->bbox.x0, item->bbox.y0,
352                                   item->bbox.x1, item->bbox.y1,
353                                   item->px,
354                                   4 * (item->bbox.x1 - item->bbox.x0), FALSE,
355                                   FALSE);
356         nr_blit_pixblock_pixblock (pb, &cpb);
357         nr_pixblock_release (&cpb);
358         pb->empty = FALSE;
359         return item->state | NR_ARENA_ITEM_STATE_RENDER;
360     }
362     NRPixBlock *dpb = pb;
363     bool canCache = false;
364 #ifdef arena_item_tile_cache
365     bool checkCache = false;
366     int tile_h = 0, tile_v = 0;
367 #endif
368     /* Setup cache if we can */
369     if ((!(flags & NR_ARENA_ITEM_RENDER_NO_CACHE)) &&
370         (carea.x0 <= item->bbox.x0) && (carea.y0 <= item->bbox.y0) &&
371         (carea.x1 >= item->bbox.x1) && (carea.y1 >= item->bbox.y1) &&
372         (((item->bbox.x1 - item->bbox.x0) * (item->bbox.y1 -
373                                              item->bbox.y0)) <= 4096)) {
374         // Item bbox is fully in renderable area and size is acceptable
375         carea.x0 = item->bbox.x0;
376         carea.y0 = item->bbox.y0;
377         carea.x1 = item->bbox.x1;
378         carea.y1 = item->bbox.y1;
379         item->px =
380             new (GC::ATOMIC) unsigned char[4 * (carea.x1 - carea.x0) *
381                                            (carea.y1 - carea.y0)];
382         nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P, carea.x0,
383                                   carea.y0, carea.x1, carea.y1, item->px,
384                                   4 * (carea.x1 - carea.x0), TRUE, TRUE);
385         cpb.visible_area = pb->visible_area;
386         dpb = &cpb;
387         // Set nocache flag for downstream rendering
388         flags |= NR_ARENA_ITEM_RENDER_NO_CACHE;
389     } else {
390 #ifdef arena_item_tile_cache
391         if (item->skipCaching) {
392         } else {
393             int
394                 tl = area->x0 & (~127);
395             int
396                 tt = area->y0 & (~127);
397             if (area->x1 <= tl + 128 && area->y1 <= tt + 128) {
398                 checkCache = true;
399                 tile_h = tl / 128;
400                 tile_v = tt / 128;
401                 int
402                     surf = (area->x1 - area->x0) * (area->y1 - area->y0);
403                 if (surf >= 4096) {
404                     canCache = true;
405                     carea.x0 = tl;
406                     carea.y0 = tt;
407                     carea.x1 = tl + 128;
408                     carea.y1 = tt + 128;
409                 }
410             }
411         }
412 #endif
413     }
415 #ifdef arena_item_tile_cache
416     item->activity += 1.0;
417 #endif
419 #ifdef arena_item_tile_cache
420     if (checkCache) {
421         NRPixBlock
422             ipb,
423             mpb;
424         bool
425             hasMask;
426         if (test_cache (item, tile_h, tile_v, ipb, mpb, hasMask)) {
427             // youpi! c'etait deja cache
428             if (hasMask) {
429                 nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
430             } else if (((item->opacity != 255) && !item->render_opacity)) {
431                 nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
432             } else {
433                 nr_blit_pixblock_pixblock (pb, &ipb);
434             }
435             pb->empty = FALSE;
436             return item->state | NR_ARENA_ITEM_STATE_RENDER;
437         }
438     }
439 #endif
440     if (canCache) {
441 #ifdef arena_item_tile_cache
442         // nota: exclusif de dpb != pb, donc pas de cas particulier a la fin
444         // struct timeval start_time,end_time;
445         // gettimeofday(&start_time,NULL);
446         GTimeVal start_time, end_time;
447         g_get_current_time (&start_time);
448         int duration = 0;
450         /* Setup and render item buffer */
451         NRPixBlock ipb;
452         nr_pixblock_setup_fast (&ipb, NR_PIXBLOCK_MODE_R8G8B8A8P, carea.x0,
453                                 carea.y0, carea.x1, carea.y1, TRUE);
455         // if memory allocation failed, abort render
456         if (ipb.data.px == NULL) {
457             nr_pixblock_release (&ipb);
458             return (item->state);
459         }
461         ipb.visible_area = pb->visible_area;
462         {
463             unsigned int state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, &ipb, flags);
464             if (state & NR_ARENA_ITEM_STATE_INVALID) {
465                 /* Clean up and return error */
466                 nr_pixblock_release (&ipb);
467                 if (dpb != pb)
468                     nr_pixblock_release (dpb);
469                 item->state |= NR_ARENA_ITEM_STATE_INVALID;
470                 return item->state;
471             }
472         }
473         ipb.empty = FALSE;
475         if (item->clip || item->mask) {
476             /* Setup mask pixblock */
477             NRPixBlock mpb;
478             nr_pixblock_setup_fast (&mpb, NR_PIXBLOCK_MODE_A8, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
480             if (mpb.data.px != NULL) { // if memory allocation was successful
482             mpb.visible_area = pb->visible_area;
483             /* Do clip if needed */
484             if (item->clip) {
485                 unsigned int state = nr_arena_item_invoke_clip (item->clip, &carea, &mpb);
486                 if (state & NR_ARENA_ITEM_STATE_INVALID) {
487                     /* Clean up and return error */
488                     nr_pixblock_release (&mpb);
489                     nr_pixblock_release (&ipb);
490                     if (dpb != pb)
491                         nr_pixblock_release (dpb);
492                     item->state |= NR_ARENA_ITEM_STATE_INVALID;
493                     return item->state;
494                 }
495                 mpb.empty = FALSE;
496             }
497             /* Do mask if needed */
498             if (item->mask) {
499                 NRPixBlock tpb;
500                 /* Set up yet another temporary pixblock */
501                 nr_pixblock_setup_fast (&tpb, NR_PIXBLOCK_MODE_R8G8B8A8N,
502                                         carea.x0, carea.y0, carea.x1,
503                                         carea.y1, TRUE);
505                 if (tpb.data.px != NULL) { // if memory allocation was successful
507                 tpb.visible_area = pb->visible_area;
508                 unsigned int state = NR_ARENA_ITEM_VIRTUAL (item->mask, render) (item->mask, &carea, &tpb, flags);
509                 if (state & NR_ARENA_ITEM_STATE_INVALID) {
510                     /* Clean up and return error */
511                     nr_pixblock_release (&tpb);
512                     nr_pixblock_release (&mpb);
513                     nr_pixblock_release (&ipb);
514                     if (dpb != pb)
515                         nr_pixblock_release (dpb);
516                     item->state |= NR_ARENA_ITEM_STATE_INVALID;
517                     return item->state;
518                 }
519                 /* Composite with clip */
520                 if (item->clip) {
521                     int x, y;
522                     for (y = carea.y0; y < carea.y1; y++) {
523                         unsigned char *s, *d;
524                         s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
525                         d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
526                         for (x = carea.x0; x < carea.x1; x++) {
527                             unsigned int m;
528                             m = ((s[0] + s[1] + s[2]) * s[3] +
529                                  127) / (3 * 255);
530                             d[0] = NR_PREMUL (d[0], m);
531                             s += 4;
532                             d += 1;
533                         }
534                     }
535                 } else {
536                     int x, y;
537                     for (y = carea.y0; y < carea.y1; y++) {
538                         unsigned char *s, *d;
539                         s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
540                         d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
541                         for (x = carea.x0; x < carea.x1; x++) {
542                             unsigned int m;
543                             m = ((s[0] + s[1] + s[2]) * s[3] +
544                                  127) / (3 * 255);
545                             d[0] = m;
546                             s += 4;
547                             d += 1;
548                         }
549                     }
550                     mpb.empty = FALSE;
551                 }
552                 }
553                 nr_pixblock_release (&tpb);
554             }
555             /* Multiply with opacity if needed */
556             if ((item->opacity != 255) && !item->render_opacity && !outline) {
557                 int x, y;
558                 unsigned int a;
559                 a = item->opacity;
560                 for (y = carea.y0; y < carea.y1; y++) {
561                     unsigned char *d;
562                     d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
563                     for (x = carea.x0; x < carea.x1; x++) {
564                         d[0] = NR_PREMUL (d[0], a);
565                         d += 1;
566                     }
567                 }
568             }
569             /* Compose rendering pixblock int destination */
570             // gettimeofday(&end_time,NULL);
571             g_get_current_time (&end_time);
572             duration =
573                 (end_time.tv_sec - start_time.tv_sec) * 1000 +
574                 (end_time.tv_usec - start_time.tv_usec) / 1000;
575             if (!(ipb.empty)) {
576                 nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
577                 if (insert_cache
578                     (item, tile_h, tile_v, &ipb, &mpb, item->activity,
579                      (double) duration)) {
580                 } else {
581                     nr_pixblock_release (&mpb);
582                     nr_pixblock_release (&ipb);
583                 }
584                 dpb->empty = FALSE;
585             } else {
586                 nr_pixblock_release (&ipb);
587             }
588             }
589         } else
590             if (((item->opacity != 255) && !item->render_opacity
591                  && !outline)) {
592             /* Opacity only */
593             // gettimeofday(&end_time,NULL);
594             g_get_current_time (&end_time);
595             duration =
596                 (end_time.tv_sec - start_time.tv_sec) * 1000 +
597                 (end_time.tv_usec - start_time.tv_usec) / 1000;
598             if (!(ipb.empty)) {
599                 nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
600                 if (insert_cache
601                     (item, tile_h, tile_v, &ipb, NULL, item->activity,
602                      (double) duration)) {
603                 } else {
604                     nr_pixblock_release (&ipb);
605                 }
606                 dpb->empty = FALSE;
607             } else {
608                 nr_pixblock_release (&ipb);
609             }
610         } else {
611             // gettimeofday(&end_time,NULL);
612             g_get_current_time (&end_time);
613             duration =
614                 (end_time.tv_sec - start_time.tv_sec) * 1000 +
615                 (end_time.tv_usec - start_time.tv_usec) / 1000;
616             if (!(ipb.empty)) {
617                 nr_blit_pixblock_pixblock (dpb, &ipb);
618                 if (insert_cache
619                     (item, tile_h, tile_v, &ipb, NULL, item->activity,
620                      (double) duration)) {
621                 } else {
622                     nr_pixblock_release (&ipb);
623                 }
624                 dpb->empty = FALSE;
625             } else {
626                 nr_pixblock_release (&ipb);
627             }
628         }
629 #endif
630     } else {
631         /* Determine, whether we need temporary buffer */
632         if (item->clip || item->mask
633             || ((item->opacity != 255) && !item->render_opacity && !outline)
634             || (item->filter && !outline) || item->background_new
635             || (item->parent && item->parent->background_pb)) {
637             /* Setup and render item buffer */
638             NRPixBlock ipb;
639             nr_pixblock_setup_fast (&ipb, NR_PIXBLOCK_MODE_R8G8B8A8P,
640                                     carea.x0, carea.y0, carea.x1, carea.y1,
641                                     TRUE);
643             //  if memory allocation failed, abort render
644             if (ipb.data.px == NULL) {
645                 nr_pixblock_release (&ipb);
646                 return (item->state);
647             }
649             /* If background access is used, save the pixblock address.
650              * This address is set to NULL at the end of this block */
651             if (item->background_new
652                 || (item->parent && item->parent->background_pb)) {
653                 item->background_pb = &ipb;
654             }
655             ipb.visible_area = pb->visible_area;
656             unsigned int state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, &ipb, flags);
657             if (state & NR_ARENA_ITEM_STATE_INVALID) {
658                 /* Clean up and return error */
659                 nr_pixblock_release (&ipb);
660                 if (dpb != pb)
661                     nr_pixblock_release (dpb);
662                 item->state |= NR_ARENA_ITEM_STATE_INVALID;
663                 return item->state;
664             }
665             ipb.empty = FALSE;
667             /* Run filtering, if a filter is set for this object */
668             if (item->filter && !outline) {
669                 item->filter->render (item, &ipb);
670             }
672             if (item->clip || item->mask) {
673                 /* Setup mask pixblock */
674                 NRPixBlock mpb;
675                 nr_pixblock_setup_fast (&mpb, NR_PIXBLOCK_MODE_A8, carea.x0,
676                                         carea.y0, carea.x1, carea.y1, TRUE);
678                 if (mpb.data.px != NULL) { // if memory allocation was successful
680                 mpb.visible_area = pb->visible_area;
681                 /* Do clip if needed */
682                 if (item->clip) {
683                     state = nr_arena_item_invoke_clip (item->clip, &carea, &mpb);
684                     if (state & NR_ARENA_ITEM_STATE_INVALID) {
685                         /* Clean up and return error */
686                         nr_pixblock_release (&mpb);
687                         nr_pixblock_release (&ipb);
688                         if (dpb != pb)
689                             nr_pixblock_release (dpb);
690                         item->state |= NR_ARENA_ITEM_STATE_INVALID;
691                         return item->state;
692                     }
693                     mpb.empty = FALSE;
694                 }
695                 /* Do mask if needed */
696                 if (item->mask) {
697                     NRPixBlock tpb;
698                     /* Set up yet another temporary pixblock */
699                     nr_pixblock_setup_fast (&tpb, NR_PIXBLOCK_MODE_R8G8B8A8N,
700                                             carea.x0, carea.y0, carea.x1,
701                                             carea.y1, TRUE);
703                     if (tpb.data.px != NULL) { // if memory allocation was successful
705                     tpb.visible_area = pb->visible_area;
706                     unsigned int state = NR_ARENA_ITEM_VIRTUAL (item->mask, render) (item->mask, &carea, &tpb, flags);
707                     if (state & NR_ARENA_ITEM_STATE_INVALID) {
708                         /* Clean up and return error */
709                         nr_pixblock_release (&tpb);
710                         nr_pixblock_release (&mpb);
711                         nr_pixblock_release (&ipb);
712                         if (dpb != pb)
713                             nr_pixblock_release (dpb);
714                         item->state |= NR_ARENA_ITEM_STATE_INVALID;
715                         return item->state;
716                     }
717                     /* Composite with clip */
718                     if (item->clip) {
719                         int x, y;
720                         for (y = carea.y0; y < carea.y1; y++) {
721                             unsigned char *s, *d;
722                             s = NR_PIXBLOCK_PX (&tpb) + (y -
723                                                          carea.y0) * tpb.rs;
724                             d = NR_PIXBLOCK_PX (&mpb) + (y -
725                                                          carea.y0) * mpb.rs;
726                             for (x = carea.x0; x < carea.x1; x++) {
727                                 unsigned int m;
728                                 m = NR_PREMUL_112 (s[0] + s[1] + s[2], s[3]);
729                                 d[0] =
730                                     FAST_DIV_ROUND < 3 * 255 * 255 >
731                                     (NR_PREMUL_123 (d[0], m));
732                                 s += 4;
733                                 d += 1;
734                             }
735                         }
736                     } else {
737                         int x, y;
738                         for (y = carea.y0; y < carea.y1; y++) {
739                             unsigned char *s, *d;
740                             s = NR_PIXBLOCK_PX (&tpb) + (y -
741                                                          carea.y0) * tpb.rs;
742                             d = NR_PIXBLOCK_PX (&mpb) + (y -
743                                                          carea.y0) * mpb.rs;
744                             for (x = carea.x0; x < carea.x1; x++) {
745                                 unsigned int m;
746                                 m = NR_PREMUL_112 (s[0] + s[1] + s[2], s[3]);
747                                 d[0] = FAST_DIV_ROUND < 3 * 255 > (m);
748                                 s += 4;
749                                 d += 1;
750                             }
751                         }
752                         mpb.empty = FALSE;
753                     }
754                     }
755                     nr_pixblock_release (&tpb);
756                 }
757                 /* Multiply with opacity if needed */
758                 if ((item->opacity != 255) && !item->render_opacity
759                     && !outline) {
760                     int x, y;
761                     unsigned int a;
762                     a = item->opacity;
763                     for (y = carea.y0; y < carea.y1; y++) {
764                         unsigned char *d;
765                         d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
766                         for (x = carea.x0; x < carea.x1; x++) {
767                             d[0] = NR_PREMUL_111 (d[0], a);
768                             d += 1;
769                         }
770                     }
771                 }
772                 /* Compose rendering pixblock int destination */
773                 nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
774                 }
775                 nr_pixblock_release (&mpb);
776                 /* This pointer wouldn't be valid outside this block, so clear it */
777                 item->background_pb = NULL;
778             } else {
779                 /* Opacity only */
780                 nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
781             }
782             nr_pixblock_release (&ipb);
783             dpb->empty = FALSE;
784         } else {
785             /* Just render */
786             unsigned int state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, dpb, flags);
787             if (state & NR_ARENA_ITEM_STATE_INVALID) {
788                 /* Clean up and return error */
789                 if (dpb != pb)
790                     nr_pixblock_release (dpb);
791                 item->state |= NR_ARENA_ITEM_STATE_INVALID;
792                 return item->state;
793             }
794             dpb->empty = FALSE;
795         }
797         if (dpb != pb) {
798             /* Have to blit from cache */
799             nr_blit_pixblock_pixblock (pb, dpb);
800             nr_pixblock_release (dpb);
801             pb->empty = FALSE;
802             item->state |= NR_ARENA_ITEM_STATE_IMAGE;
803         }
804     }
805     return item->state | NR_ARENA_ITEM_STATE_RENDER;
808 unsigned int
809 nr_arena_item_invoke_clip (NRArenaItem *item, NRRectL *area, NRPixBlock *pb)
811     nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
812     nr_return_val_if_fail (NR_IS_ARENA_ITEM (item),
813                            NR_ARENA_ITEM_STATE_INVALID);
814     /* we originally short-circuited if the object state included
815      * NR_ARENA_ITEM_STATE_CLIP (and showed a warning on the console);
816      * anyone know why we stopped doing so?
817      */
818     nr_return_val_if_fail ((pb->area.x1 - pb->area.x0) >=
819                            (area->x1 - area->x0),
820                            NR_ARENA_ITEM_STATE_INVALID);
821     nr_return_val_if_fail ((pb->area.y1 - pb->area.y0) >=
822                            (area->y1 - area->y0),
823                            NR_ARENA_ITEM_STATE_INVALID);
825 #ifdef NR_ARENA_ITEM_VERBOSE
826     printf ("Invoke clip by %p: %d %d - %d %d, item bbox %d %d - %d %d\n",
827             item, area->x0, area->y0, area->x1, area->y1, (&item->bbox)->x0,
828             (&item->bbox)->y0, (&item->bbox)->x1, (&item->bbox)->y1);
829 #endif
831     if (item->visible && nr_rect_l_test_intersect (area, &item->bbox)) {
832         /* Need render that item */
833         if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->clip) {
834             return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->
835                 clip (item, area, pb);
836         }
837     }
839     return item->state;
842 NRArenaItem *
843 nr_arena_item_invoke_pick (NRArenaItem *item, NR::Point p, double delta,
844                            unsigned int sticky)
846     nr_return_val_if_fail (item != NULL, NULL);
847     nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
849     // Sometimes there's no BBOX in item->state, reason unknown (bug 992817); I made this not an assert to remove the warning
850     if (!(item->state & NR_ARENA_ITEM_STATE_BBOX)
851         || !(item->state & NR_ARENA_ITEM_STATE_PICK))
852         return NULL;
854     if (!sticky && !(item->visible && item->sensitive))
855         return NULL;
857     // TODO: rewrite using NR::Rect
858     const double x = p[NR::X];
859     const double y = p[NR::Y];
861     if (((x + delta) >= item->bbox.x0) &&
862         ((x - delta) < item->bbox.x1) &&
863         ((y + delta) >= item->bbox.y0) && ((y - delta) < item->bbox.y1)) {
864         if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->pick)
865             return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->
866                 pick (item, p, delta, sticky);
867     }
869     return NULL;
872 void
873 nr_arena_item_request_update (NRArenaItem *item, unsigned int reset,
874                               unsigned int propagate)
876     nr_return_if_fail (item != NULL);
877     nr_return_if_fail (NR_IS_ARENA_ITEM (item));
878     nr_return_if_fail (!(reset & NR_ARENA_ITEM_STATE_INVALID));
880     if (propagate && !item->propagate)
881         item->propagate = TRUE;
883     if (item->state & reset) {
884         item->state &= ~reset;
885         if (item->parent) {
886             nr_arena_item_request_update (item->parent, reset, FALSE);
887         } else {
888             nr_arena_request_update (item->arena, item);
889         }
890     }
893 void
894 nr_arena_item_request_render (NRArenaItem *item)
896     nr_return_if_fail (item != NULL);
897     nr_return_if_fail (NR_IS_ARENA_ITEM (item));
899     nr_arena_request_render_rect (item->arena, &item->bbox);
902 /* Public */
904 NRArenaItem *
905 nr_arena_item_unparent (NRArenaItem *item)
907     nr_return_val_if_fail (item != NULL, NULL);
908     nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
910     nr_arena_item_request_render (item);
912     if (item->parent) {
913         nr_arena_item_remove_child (item->parent, item);
914     }
916     return NULL;
919 void
920 nr_arena_item_append_child (NRArenaItem *parent, NRArenaItem *child)
922     nr_return_if_fail (parent != NULL);
923     nr_return_if_fail (NR_IS_ARENA_ITEM (parent));
924     nr_return_if_fail (child != NULL);
925     nr_return_if_fail (NR_IS_ARENA_ITEM (child));
926     nr_return_if_fail (parent->arena == child->arena);
927     nr_return_if_fail (child->parent == NULL);
928     nr_return_if_fail (child->prev == NULL);
929     nr_return_if_fail (child->next == NULL);
931     nr_arena_item_add_child (parent, child, nr_arena_item_last_child (parent));
934 void
935 nr_arena_item_set_transform (NRArenaItem *item, NR::Matrix const &transform)
937     NRMatrix const t (transform);
938     nr_arena_item_set_transform (item, &t);
941 void
942 nr_arena_item_set_transform (NRArenaItem *item, NRMatrix const *transform)
944     nr_return_if_fail (item != NULL);
945     nr_return_if_fail (NR_IS_ARENA_ITEM (item));
947     if (!transform && !item->transform)
948         return;
950     const NRMatrix *md = (item->transform) ? item->transform : &NR_MATRIX_IDENTITY;
951     const NRMatrix *ms = (transform) ? transform : &NR_MATRIX_IDENTITY;
953     if (!NR_MATRIX_DF_TEST_CLOSE (md, ms, NR_EPSILON)) {
954         nr_arena_item_request_render (item);
955         if (!transform || nr_matrix_test_identity (transform, NR_EPSILON)) {
956             /* Set to identity affine */
957             item->transform = NULL;
958         } else {
959             if (!item->transform)
960                 item->transform = new (GC::ATOMIC) NRMatrix ();
961             *item->transform = *transform;
962         }
963         nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
964     }
967 void
968 nr_arena_item_set_opacity (NRArenaItem *item, double opacity)
970     nr_return_if_fail (item != NULL);
971     nr_return_if_fail (NR_IS_ARENA_ITEM (item));
973     nr_arena_item_request_render (item);
975     item->opacity = (unsigned int) (opacity * 255.9999);
978 void
979 nr_arena_item_set_sensitive (NRArenaItem *item, unsigned int sensitive)
981     nr_return_if_fail (item != NULL);
982     nr_return_if_fail (NR_IS_ARENA_ITEM (item));
984     /* fixme: mess with pick/repick... */
986     item->sensitive = sensitive;
989 void
990 nr_arena_item_set_visible (NRArenaItem *item, unsigned int visible)
992     nr_return_if_fail (item != NULL);
993     nr_return_if_fail (NR_IS_ARENA_ITEM (item));
995     item->visible = visible;
997     nr_arena_item_request_render (item);
1000 void
1001 nr_arena_item_set_clip (NRArenaItem *item, NRArenaItem *clip)
1003     nr_return_if_fail (item != NULL);
1004     nr_return_if_fail (NR_IS_ARENA_ITEM (item));
1005     nr_return_if_fail (!clip || NR_IS_ARENA_ITEM (clip));
1007     if (clip != item->clip) {
1008         nr_arena_item_request_render (item);
1009         if (item->clip)
1010             item->clip = nr_arena_item_detach (item, item->clip);
1011         if (clip)
1012             item->clip = nr_arena_item_attach (item, clip, NULL, NULL);
1013         nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
1014     }
1017 void
1018 nr_arena_item_set_mask (NRArenaItem *item, NRArenaItem *mask)
1020     nr_return_if_fail (item != NULL);
1021     nr_return_if_fail (NR_IS_ARENA_ITEM (item));
1022     nr_return_if_fail (!mask || NR_IS_ARENA_ITEM (mask));
1024     if (mask != item->mask) {
1025         nr_arena_item_request_render (item);
1026         if (item->mask)
1027             item->mask = nr_arena_item_detach (item, item->mask);
1028         if (mask)
1029             item->mask = nr_arena_item_attach (item, mask, NULL, NULL);
1030         nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
1031     }
1034 void
1035 nr_arena_item_set_order (NRArenaItem *item, int order)
1037     nr_return_if_fail (item != NULL);
1038     nr_return_if_fail (NR_IS_ARENA_ITEM (item));
1040     if (!item->parent)
1041         return;
1043     NRArenaItem *children = nr_arena_item_children (item->parent);
1045     NRArenaItem *ref = NULL;
1046     int pos = 0;
1047     for (NRArenaItem *child = children; child != NULL; child = child->next) {
1048         if (pos >= order)
1049             break;
1050         if (child != item) {
1051             ref = child;
1052             pos += 1;
1053         }
1054     }
1056     nr_arena_item_set_child_position (item->parent, item, ref);
1059 /** Returns a background image for use with filter effects. */
1060 NRPixBlock *
1061 nr_arena_item_get_background (NRArenaItem const *item, int depth)
1063     NRPixBlock *pb;
1064     if (!item->background_pb)
1065         return NULL;
1066     if (item->background_new) {
1067         pb = new NRPixBlock ();
1068         nr_pixblock_setup_fast (pb, item->background_pb->mode,
1069                                 item->background_pb->area.x0,
1070                                 item->background_pb->area.y0,
1071                                 item->background_pb->area.x1,
1072                                 item->background_pb->area.y1, true);
1073         if (pb->data.px == NULL) // allocation failed
1074             return NULL;
1075     } else if (item->parent) {
1076         pb = nr_arena_item_get_background (item->parent, depth + 1);
1077     } else
1078         return NULL;
1080     if (depth > 0)
1081         nr_blit_pixblock_pixblock (pb, item->background_pb);
1083     return pb;
1086 /* Helpers */
1088 NRArenaItem *
1089 nr_arena_item_attach (NRArenaItem *parent, NRArenaItem *child,
1090                       NRArenaItem *prev, NRArenaItem *next)
1092     nr_return_val_if_fail (parent != NULL, NULL);
1093     nr_return_val_if_fail (NR_IS_ARENA_ITEM (parent), NULL);
1094     nr_return_val_if_fail (child != NULL, NULL);
1095     nr_return_val_if_fail (NR_IS_ARENA_ITEM (child), NULL);
1096     nr_return_val_if_fail (child->parent == NULL, NULL);
1097     nr_return_val_if_fail (child->prev == NULL, NULL);
1098     nr_return_val_if_fail (child->next == NULL, NULL);
1099     nr_return_val_if_fail (!prev || NR_IS_ARENA_ITEM (prev), NULL);
1100     nr_return_val_if_fail (!prev || (prev->parent == parent), NULL);
1101     nr_return_val_if_fail (!prev || (prev->next == next), NULL);
1102     nr_return_val_if_fail (!next || NR_IS_ARENA_ITEM (next), NULL);
1103     nr_return_val_if_fail (!next || (next->parent == parent), NULL);
1104     nr_return_val_if_fail (!next || (next->prev == prev), NULL);
1106     child->parent = parent;
1107     child->prev = prev;
1108     child->next = next;
1110     if (prev)
1111         prev->next = child;
1112     if (next)
1113         next->prev = child;
1115     return child;
1118 NRArenaItem *
1119 nr_arena_item_detach (NRArenaItem *parent, NRArenaItem *child)
1121     nr_return_val_if_fail (parent != NULL, NULL);
1122     nr_return_val_if_fail (NR_IS_ARENA_ITEM (parent), NULL);
1123     nr_return_val_if_fail (child != NULL, NULL);
1124     nr_return_val_if_fail (NR_IS_ARENA_ITEM (child), NULL);
1125     nr_return_val_if_fail (child->parent == parent, NULL);
1127     NRArenaItem *prev = child->prev;
1128     NRArenaItem *next = child->next;
1130     child->parent = NULL;
1131     child->prev = NULL;
1132     child->next = NULL;
1134     if (prev)
1135         prev->next = next;
1136     if (next)
1137         next->prev = prev;
1139     return next;
1142 /*
1143  *
1144  * caches
1145  *
1146  */
1148 #ifdef arena_item_tile_cache
1149 typedef struct cache_entry {
1150     int key;
1151     double score;
1152     NRArenaItem *owner;
1153     int th, tv;
1154     int prev, next;
1155     NRPixBlock ipb;
1156     bool hasMask;
1157     NRPixBlock mpb;
1158 } cache_entry;
1160 int hash_max = 2048, hash_fill = 1024;
1162 int *keys = NULL;
1163 int nbCch = 0;
1165 int nbEnt = 0, maxEnt = 0;
1166 cache_entry *entries = NULL;
1168 //#define tile_cache_stats
1169 #ifdef tile_cache_stats
1170 double hits = 0, misses = 0;
1171 int hitMissCount = 0;
1172 #endif
1174 int
1175 hash_that (NRArenaItem *owner, int th, int tv)
1177     int res = GPOINTER_TO_INT (owner);
1178     res *= 17;
1179     res += th;
1180     res *= 59;
1181     res += tv;
1182     res *= 217;
1183     if (res < 0)
1184         res = -res;
1185     res %= hash_max;
1186     return res;
1189 bool
1190 test_cache (NRArenaItem *owner, int th, int tv, NRPixBlock & ipb,
1191             NRPixBlock & mpb, bool & hasMask)
1193     if (keys == NULL) {
1194         hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1195         hash_fill = (hash_max * 3) / 4;
1196         keys = (int *) malloc (hash_max * sizeof (int));
1197         for (int i = 0; i < hash_max; i++)
1198             keys[i] = -1;
1199     }
1200     int key = hash_that (owner, th, tv);
1201     if (keys[key] < 0) {
1202 #ifdef tile_cache_stats
1203         misses += 1.0;
1204 #endif
1205         return false;
1206     }
1207     int cur = keys[key];
1208     while (cur >= 0 && cur < nbEnt) {
1209         if (entries[cur].owner == owner && entries[cur].th == th
1210             && entries[cur].tv == tv) {
1211             hasMask = entries[cur].hasMask;
1212             ipb = entries[cur].ipb;
1213             mpb = entries[cur].mpb;
1214 #ifdef tile_cache_stats
1215             hits += 1.0;
1216 #endif
1217             return true;
1218         }
1219         cur = entries[cur].next;
1220     }
1221 #ifdef tile_cache_stats
1222     misses += 1.0;
1223 #endif
1224     return false;
1227 void
1228 remove_one_cache (int no)
1230     if (no < 0 || no >= nbEnt)
1231         return;
1233     nr_pixblock_release (&entries[no].ipb);
1234     if (entries[no].hasMask)
1235         nr_pixblock_release (&entries[no].mpb);
1237     if (entries[no].prev >= 0)
1238         entries[entries[no].prev].next = entries[no].next;
1239     if (entries[no].next >= 0)
1240         entries[entries[no].next].prev = entries[no].prev;
1241     if (entries[no].prev < 0)
1242         keys[entries[no].key] = entries[no].next;
1243     entries[no].prev = entries[no].next = entries[no].key = -1;
1245     if (no == nbEnt - 1) {
1246         nbEnt--;
1247         return;
1248     }
1249     entries[no] = entries[--nbEnt];
1250     if (entries[no].prev >= 0)
1251         entries[entries[no].prev].next = no;
1252     if (entries[no].next >= 0)
1253         entries[entries[no].next].prev = no;
1254     if (entries[no].prev < 0)
1255         keys[entries[no].key] = no;
1258 void
1259 remove_caches (NRArenaItem *owner)
1261     if (keys == NULL) {
1262         hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1263         hash_fill = (hash_max * 3) / 4;
1264         keys = (int *) malloc (hash_max * sizeof (int));
1265         for (int i = 0; i < hash_max; i++)
1266             keys[i] = -1;
1267     }
1268     for (int i = nbEnt - 1; i >= 0; i--) {
1269         if (entries[i].owner == owner) {
1270             remove_one_cache (i);
1271         }
1272     }
1274 void
1275 age_cache (void)
1277     for (int i = 0; i < nbEnt; i++)
1278         entries[i].score *= 0.95;
1281 bool
1282 insert_cache (NRArenaItem *owner, int th, int tv, NRPixBlock *ipb,
1283               NRPixBlock *mpb, double activity, double duration)
1285     if (keys == NULL) {
1286         hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
1287         hash_fill = (hash_max * 3) / 4;
1288         keys = (int *) malloc (hash_max * sizeof (int));
1289         for (int i = 0; i < hash_max; i++)
1290             keys[i] = -1;
1291     }
1292     for (int i = 0; i < nbEnt; i++)
1293         entries[i].score *= 0.95;
1294 #ifdef tile_cache_stats
1295     hits *= 0.95;
1296     misses *= 0.95;
1297     hitMissCount++;
1298     if (hitMissCount > 100) {
1299         hitMissCount = 0;
1300         printf ("hit/miss = %f  used/total=%i/%i\n", (misses > 0.001) ? hits / misses : 100000.0, nbEnt, hash_max);     // localizing ok
1301     }
1302 #endif
1303     int key = hash_that (owner, th, tv);
1304     double nScore = /*activity* */ duration;
1306     if (keys[key] >= 0) {
1307         int cur = keys[key];
1308         while (cur >= 0 && cur < nbEnt) {
1309             if (entries[cur].owner == owner && entries[cur].th == th
1310                 && entries[cur].tv == tv) {
1311                 remove_one_cache (cur);
1312                 break;
1313             }
1314             cur = entries[cur].next;
1315         }
1316     }
1318     bool doAdd = false;
1319     if (nbEnt < hash_fill) {
1320         doAdd = true;
1321     } else {
1322         double worstS = entries[0].score;
1323         int worstE = 0;
1324         for (int i = 1; i < nbEnt; i++) {
1325             if (entries[i].score < worstS) {
1326                 worstS = entries[i].score;
1327                 worstE = i;
1328             }
1329         }
1330         if (worstS < nScore) {
1331             doAdd = true;
1332             remove_one_cache (worstE);
1333         }
1334     }
1335     if (doAdd == false)
1336         return false;
1337     if (nbEnt >= maxEnt) {
1338         maxEnt = 2 * nbEnt + 1;
1339         entries = (cache_entry *) realloc (entries, maxEnt * sizeof (cache_entry));
1340     }
1341     entries[nbEnt].key = key;
1342     entries[nbEnt].score = nScore;
1343     entries[nbEnt].owner = owner;
1344     entries[nbEnt].th = th;
1345     entries[nbEnt].tv = tv;
1346     entries[nbEnt].prev = entries[nbEnt].next = -1;
1347     entries[nbEnt].ipb = *ipb;
1348     if (mpb) {
1349         entries[nbEnt].hasMask = true;
1350         entries[nbEnt].mpb = *mpb;
1351     } else {
1352         entries[nbEnt].hasMask = false;
1353     }
1354     entries[nbEnt].next = keys[key];
1355     if (entries[nbEnt].next >= 0)
1356         entries[entries[nbEnt].next].prev = nbEnt;
1357     keys[key] = nbEnt;
1359     nbEnt++;
1360     return true;
1362 #endif
1364 /*
1365   Local Variables:
1366   mode:c++
1367   c-file-style:"stroustrup"
1368   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1369   indent-tabs-mode:nil
1370   fill-column:99
1371   End:
1372 */
1373 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :