summaryrefslogtreecommitdiffstats
path: root/src/display/nr-arena-item.cpp
diff options
context:
space:
mode:
authorMenTaLguY <mental@rydia.net>2006-01-16 02:36:01 +0000
committermental <mental@users.sourceforge.net>2006-01-16 02:36:01 +0000
commit179fa413b047bede6e32109e2ce82437c5fb8d34 (patch)
treea5a6ac2c1708bd02288fbd8edb2ff500ff2e0916 /src/display/nr-arena-item.cpp
downloadinkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.tar.gz
inkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.zip
moving trunk for module inkscape
(bzr r1)
Diffstat (limited to 'src/display/nr-arena-item.cpp')
-rw-r--r--src/display/nr-arena-item.cpp1155
1 files changed, 1155 insertions, 0 deletions
diff --git a/src/display/nr-arena-item.cpp b/src/display/nr-arena-item.cpp
new file mode 100644
index 000000000..ccabe7b28
--- /dev/null
+++ b/src/display/nr-arena-item.cpp
@@ -0,0 +1,1155 @@
+#define __NR_ARENA_ITEM_C__
+
+/*
+ * RGBA display list system for inkscape
+ *
+ * Author:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * Copyright (C) 2001-2002 Lauris Kaplinski
+ * Copyright (C) 2001 Ximian, Inc.
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#define noNR_ARENA_ITEM_VERBOSE
+#define noNR_ARENA_ITEM_DEBUG_CASCADE
+
+
+#include <libnr/nr-blit.h>
+#include <libnr/nr-pixops.h>
+#include "nr-arena.h"
+#include "nr-arena-item.h"
+//#include "nr-arena-group.h"
+
+
+static void nr_arena_item_class_init (NRArenaItemClass *klass);
+static void nr_arena_item_init (NRArenaItem *item);
+static void nr_arena_item_private_finalize (NRObject *object);
+
+#ifdef arena_item_tile_cache
+bool insert_cache(NRArenaItem* owner,int th,int tv,NRPixBlock *ipb,NRPixBlock *mpb,double activity,double duration);
+void remove_caches(NRArenaItem* owner);
+bool test_cache(NRArenaItem* owner,int th,int tv,NRPixBlock &ipb,NRPixBlock &mpb,bool &hasMask);
+#endif
+
+static NRObjectClass *parent_class;
+
+NRType
+nr_arena_item_get_type (void)
+{
+ static NRType type = 0;
+ if (!type) {
+ type = nr_object_register_type (NR_TYPE_OBJECT,
+ "NRArenaItem",
+ sizeof (NRArenaItemClass),
+ sizeof (NRArenaItem),
+ (void (*) (NRObjectClass *)) nr_arena_item_class_init,
+ (void (*) (NRObject *)) nr_arena_item_init);
+ }
+ return type;
+}
+
+static void
+nr_arena_item_class_init (NRArenaItemClass *klass)
+{
+ NRObjectClass *object_class;
+
+ object_class = (NRObjectClass *) klass;
+
+ parent_class = ((NRObjectClass *) klass)->parent;
+
+ object_class->finalize = nr_arena_item_private_finalize;
+ object_class->cpp_ctor = NRObject::invoke_ctor<NRArenaItem>;
+}
+
+NRArenaItem::NRArenaItem() {
+ // clear all reverse-pointing pointers before finalization
+ clearOnceInaccessible(&arena);
+ clearOnceInaccessible(&parent);
+ clearOnceInaccessible(&prev);
+}
+
+static void
+nr_arena_item_init (NRArenaItem *item)
+{
+ item->arena = NULL;
+ item->parent = NULL;
+ item->next = item->prev = NULL;
+
+ item->key = 0;
+
+ item->state = 0;
+ item->sensitive = TRUE;
+ item->visible = TRUE;
+
+ memset(&item->bbox, 0, sizeof(item->bbox));
+ item->transform = NULL;
+ item->opacity = 255;
+ item->render_opacity = FALSE;
+
+#ifdef arena_item_tile_cache
+ item->activity=0.0;
+ item->skipCaching=false;
+#endif
+
+ item->transform = NULL;
+ item->clip = NULL;
+ item->mask = NULL;
+ item->px = NULL;
+ item->data = NULL;
+}
+
+static void
+nr_arena_item_private_finalize (NRObject *object)
+{
+ NRArenaItem *item=static_cast<NRArenaItem *>(object);
+
+#ifdef arena_item_tile_cache
+ remove_caches(item);
+#endif
+
+ if (item->px) {
+ nr_free (item->px);
+ }
+
+ if (item->transform) {
+ nr_free (item->transform);
+ }
+
+ ((NRObjectClass *) (parent_class))->finalize (object);
+}
+
+NRArenaItem *
+nr_arena_item_children (NRArenaItem *item)
+{
+ nr_return_val_if_fail (item != NULL, NULL);
+ nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
+
+ if (NR_ARENA_ITEM_VIRTUAL (item, children))
+ return NR_ARENA_ITEM_VIRTUAL (item, children) (item);
+
+ return NULL;
+}
+
+NRArenaItem *
+nr_arena_item_last_child (NRArenaItem *item)
+{
+ nr_return_val_if_fail (item != NULL, NULL);
+ nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
+
+ if (NR_ARENA_ITEM_VIRTUAL (item, last_child)) {
+ return NR_ARENA_ITEM_VIRTUAL (item, last_child) (item);
+ } else {
+ NRArenaItem *ref;
+ ref = nr_arena_item_children (item);
+ if (ref) while (ref->next) ref = ref->next;
+ return ref;
+ }
+}
+
+void
+nr_arena_item_add_child (NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref)
+{
+ nr_return_if_fail (item != NULL);
+ nr_return_if_fail (NR_IS_ARENA_ITEM (item));
+ nr_return_if_fail (child != NULL);
+ nr_return_if_fail (NR_IS_ARENA_ITEM (child));
+ nr_return_if_fail (child->parent == NULL);
+ nr_return_if_fail (child->prev == NULL);
+ nr_return_if_fail (child->next == NULL);
+ nr_return_if_fail (child->arena == item->arena);
+ nr_return_if_fail (child != ref);
+ nr_return_if_fail (!ref || NR_IS_ARENA_ITEM (ref));
+ nr_return_if_fail (!ref || (ref->parent == item));
+
+ if (NR_ARENA_ITEM_VIRTUAL (item, add_child))
+ NR_ARENA_ITEM_VIRTUAL (item, add_child) (item, child, ref);
+}
+
+void
+nr_arena_item_remove_child (NRArenaItem *item, NRArenaItem *child)
+{
+ nr_return_if_fail (item != NULL);
+ nr_return_if_fail (NR_IS_ARENA_ITEM (item));
+ nr_return_if_fail (child != NULL);
+ nr_return_if_fail (NR_IS_ARENA_ITEM (child));
+ nr_return_if_fail (child->parent == item);
+
+ if (NR_ARENA_ITEM_VIRTUAL (item, remove_child))
+ NR_ARENA_ITEM_VIRTUAL (item, remove_child) (item, child);
+}
+
+void
+nr_arena_item_set_child_position (NRArenaItem *item, NRArenaItem *child, NRArenaItem *ref)
+{
+ nr_return_if_fail (item != NULL);
+ nr_return_if_fail (NR_IS_ARENA_ITEM (item));
+ nr_return_if_fail (child != NULL);
+ nr_return_if_fail (NR_IS_ARENA_ITEM (child));
+ nr_return_if_fail (child->parent == item);
+ nr_return_if_fail (!ref || NR_IS_ARENA_ITEM (ref));
+ nr_return_if_fail (!ref || (ref->parent == item));
+
+ if (NR_ARENA_ITEM_VIRTUAL (item, set_child_position))
+ NR_ARENA_ITEM_VIRTUAL (item, set_child_position) (item, child, ref);
+}
+
+NRArenaItem *
+nr_arena_item_ref (NRArenaItem *item)
+{
+ nr_object_ref ((NRObject *) item);
+
+ return item;
+}
+
+NRArenaItem *
+nr_arena_item_unref (NRArenaItem *item)
+{
+ nr_object_unref ((NRObject *) item);
+
+ return NULL;
+}
+
+unsigned int
+nr_arena_item_invoke_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset)
+{
+ NRGC childgc(gc);
+
+ nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
+ nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NR_ARENA_ITEM_STATE_INVALID);
+ nr_return_val_if_fail (!(state & NR_ARENA_ITEM_STATE_INVALID), NR_ARENA_ITEM_STATE_INVALID);
+
+#ifdef NR_ARENA_ITEM_DEBUG_CASCADE
+ printf ("Update %s:%p %x %x %x\n", nr_type_name_from_instance ((GTypeInstance *) item), item, state, item->state, reset);
+#endif
+
+ /* return if in error */
+ if (item->state & NR_ARENA_ITEM_STATE_INVALID) return item->state;
+ /* Set reset flags according to propagation status */
+ if (item->propagate) {
+ reset |= ~item->state;
+ item->propagate = FALSE;
+ }
+ /* Reset our state */
+ item->state &= ~reset;
+ /* Return if NOP */
+ if (!(~item->state & state)) return item->state;
+ /* Test whether to return immediately */
+ if (area && (item->state & NR_ARENA_ITEM_STATE_BBOX)) {
+ if (!nr_rect_l_test_intersect (area, &item->bbox)) return item->state;
+ }
+
+ /* Reset image cache, if not to be kept */
+ if (!(item->state & NR_ARENA_ITEM_STATE_IMAGE) && (item->px)) {
+ nr_free (item->px);
+ item->px = NULL;
+ }
+#ifdef arena_item_tile_cache
+ remove_caches(item);
+#endif
+
+ /* Set up local gc */
+ childgc = *gc;
+ if (item->transform) {
+ nr_matrix_multiply (&childgc.transform, item->transform, &childgc.transform);
+ }
+
+ /* Invoke the real method */
+ item->state = NR_ARENA_ITEM_VIRTUAL (item, update) (item, area, &childgc, state, reset);
+ if (item->state & NR_ARENA_ITEM_STATE_INVALID) return item->state;
+ /* Clipping */
+ if (item->clip) {
+ unsigned int newstate;
+ newstate = nr_arena_item_invoke_update (item->clip, area, &childgc, state, reset);
+ if (newstate & NR_ARENA_ITEM_STATE_INVALID) {
+ item->state |= NR_ARENA_ITEM_STATE_INVALID;
+ return item->state;
+ }
+ nr_rect_l_intersect (&item->bbox, &item->bbox, &item->clip->bbox);
+ }
+ /* Masking */
+ if (item->mask) {
+ unsigned int newstate;
+ newstate = nr_arena_item_invoke_update (item->mask, area, &childgc, state, reset);
+ if (newstate & NR_ARENA_ITEM_STATE_INVALID) {
+ item->state |= NR_ARENA_ITEM_STATE_INVALID;
+ return item->state;
+ }
+ nr_rect_l_intersect (&item->bbox, &item->bbox, &item->mask->bbox);
+ }
+
+ return item->state;
+}
+
+/**
+ * Render item to pixblock.
+ *
+ * \return Has NR_ARENA_ITEM_STATE_RENDER set on success.
+ */
+
+unsigned int nr_arena_item_invoke_render(NRArenaItem *item, NRRectL const *area, NRPixBlock *pb, unsigned int flags)
+{
+ NRRectL carea;
+ NRPixBlock *dpb;
+ NRPixBlock cpb;
+ unsigned int state;
+
+ nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
+ nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NR_ARENA_ITEM_STATE_INVALID);
+ nr_return_val_if_fail (item->state & NR_ARENA_ITEM_STATE_BBOX, item->state);
+
+#ifdef NR_ARENA_ITEM_VERBOSE
+ printf ("Invoke render %p: %d %d - %d %d\n", item, area->x0, area->y0, area->x1, area->y1);
+#endif
+
+#ifdef arena_item_tile_cache
+ item->activity*=0.5;
+#endif
+
+ /* If we are outside bbox just return successfully */
+ if (!item->visible) return item->state | NR_ARENA_ITEM_STATE_RENDER;
+ nr_rect_l_intersect (&carea, area, &item->bbox);
+ if (nr_rect_l_test_empty (&carea)) return item->state | NR_ARENA_ITEM_STATE_RENDER;
+
+ if (item->px) {
+ /* Has cache pixblock, render this and return */
+ nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P,
+ /* fixme: This probably cannot overflow, because we render only if visible */
+ /* fixme: and pixel cache is there only for small items */
+ /* fixme: But this still needs extra check (Lauris) */
+ item->bbox.x0, item->bbox.y0,
+ item->bbox.x1, item->bbox.y1,
+ item->px, 4 * (item->bbox.x1 - item->bbox.x0), FALSE, FALSE);
+ nr_blit_pixblock_pixblock (pb, &cpb);
+ nr_pixblock_release (&cpb);
+ pb->empty = FALSE;
+ return item->state | NR_ARENA_ITEM_STATE_RENDER;
+ }
+
+ dpb = pb;
+ bool canCache=false;
+#ifdef arena_item_tile_cache
+ bool checkCache=false;
+ int tile_h=0,tile_v=0;
+#endif
+ /* Setup cache if we can */
+ if ((!(flags & NR_ARENA_ITEM_RENDER_NO_CACHE)) &&
+ (carea.x0 <= item->bbox.x0) && (carea.y0 <= item->bbox.y0) &&
+ (carea.x1 >= item->bbox.x1) && (carea.y1 >= item->bbox.y1) &&
+ (((item->bbox.x1 - item->bbox.x0) * (item->bbox.y1 - item->bbox.y0)) <= 4096)) {
+ // Item bbox is fully in renderable area and size is acceptable
+ carea.x0 = item->bbox.x0;
+ carea.y0 = item->bbox.y0;
+ carea.x1 = item->bbox.x1;
+ carea.y1 = item->bbox.y1;
+ item->px = nr_new (unsigned char, 4 * (carea.x1 - carea.x0) * (carea.y1 - carea.y0));
+ nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P,
+ carea.x0, carea.y0, carea.x1, carea.y1,
+ item->px, 4 * (carea.x1 - carea.x0), TRUE, TRUE);
+ dpb = &cpb;
+ // Set nocache flag for downstream rendering
+ flags |= NR_ARENA_ITEM_RENDER_NO_CACHE;
+ } else {
+#ifdef arena_item_tile_cache
+ if ( item->skipCaching ) {
+ } else {
+ int tl=area->x0&(~127);
+ int tt=area->y0&(~127);
+ if ( area->x1 <= tl+128 && area->y1 <= tt+128 ) {
+ checkCache=true;
+ tile_h=tl/128;
+ tile_v=tt/128;
+ int surf=(area->x1-area->x0)*(area->y1-area->y0);
+ if ( surf >= 4096 ) {
+ canCache=true;
+ carea.x0=tl;
+ carea.y0=tt;
+ carea.x1=tl+128;
+ carea.y1=tt+128;
+ }
+ }
+ }
+#endif
+ }
+
+#ifdef arena_item_tile_cache
+ item->activity+=1.0;
+#endif
+
+#ifdef arena_item_tile_cache
+ if ( checkCache ) {
+ NRPixBlock ipb, mpb;
+ bool hasMask;
+ if ( test_cache(item,tile_h,tile_v,ipb,mpb,hasMask) ) {
+ // youpi! c'etait deja cache
+ if ( hasMask ) {
+ nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
+ } else if ( ((item->opacity != 255) && !item->render_opacity) ) {
+ nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
+ } else {
+ nr_blit_pixblock_pixblock (pb, &ipb);
+ }
+ pb->empty = FALSE;
+ return item->state | NR_ARENA_ITEM_STATE_RENDER;
+ }
+ }
+#endif
+ if ( canCache ) {
+#ifdef arena_item_tile_cache
+ // nota: exclusif de dpb != pb, donc pas de cas particulier a la fin
+ NRPixBlock ipb, mpb;
+
+ // struct timeval start_time,end_time;
+ // gettimeofday(&start_time,NULL);
+ GTimeVal start_time,end_time;
+ g_get_current_time (&start_time);
+ int duration=0;
+
+ /* Setup and render item buffer */
+ nr_pixblock_setup_fast (&ipb, NR_PIXBLOCK_MODE_R8G8B8A8P, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
+ state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, &ipb, flags);
+ if (state & NR_ARENA_ITEM_STATE_INVALID) {
+ /* Clean up and return error */
+ nr_pixblock_release (&ipb);
+ if (dpb != pb) nr_pixblock_release (dpb);
+ item->state |= NR_ARENA_ITEM_STATE_INVALID;
+ return item->state;
+ }
+ ipb.empty = FALSE;
+
+ if (item->clip || item->mask) {
+ /* Setup mask pixblock */
+ nr_pixblock_setup_fast (&mpb, NR_PIXBLOCK_MODE_A8, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
+ /* Do clip if needed */
+ if (item->clip) {
+ state = nr_arena_item_invoke_clip (item->clip, &carea, &mpb);
+ if (state & NR_ARENA_ITEM_STATE_INVALID) {
+ /* Clean up and return error */
+ nr_pixblock_release (&mpb);
+ nr_pixblock_release (&ipb);
+ if (dpb != pb) nr_pixblock_release (dpb);
+ item->state |= NR_ARENA_ITEM_STATE_INVALID;
+ return item->state;
+ }
+ mpb.empty = FALSE;
+ }
+ /* Do mask if needed */
+ if (item->mask) {
+ NRPixBlock tpb;
+ /* Set up yet another temporary pixblock */
+ nr_pixblock_setup_fast (&tpb, NR_PIXBLOCK_MODE_R8G8B8A8N, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
+ state = NR_ARENA_ITEM_VIRTUAL (item->mask, render) (item->mask, &carea, &tpb, flags);
+ if (state & NR_ARENA_ITEM_STATE_INVALID) {
+ /* Clean up and return error */
+ nr_pixblock_release (&tpb);
+ nr_pixblock_release (&mpb);
+ nr_pixblock_release (&ipb);
+ if (dpb != pb) nr_pixblock_release (dpb);
+ item->state |= NR_ARENA_ITEM_STATE_INVALID;
+ return item->state;
+ }
+ /* Composite with clip */
+ if (item->clip) {
+ int x, y;
+ for (y = carea.y0; y < carea.y1; y++) {
+ unsigned char *s, *d;
+ s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
+ d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
+ for (x = carea.x0; x < carea.x1; x++) {
+ unsigned int m;
+ m = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
+ d[0] = NR_PREMUL (d[0], m);
+ s += 4;
+ d += 1;
+ }
+ }
+ } else {
+ int x, y;
+ for (y = carea.y0; y < carea.y1; y++) {
+ unsigned char *s, *d;
+ s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
+ d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
+ for (x = carea.x0; x < carea.x1; x++) {
+ unsigned int m;
+ m = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
+ d[0] = m;
+ s += 4;
+ d += 1;
+ }
+ }
+ mpb.empty = FALSE;
+ }
+ nr_pixblock_release (&tpb);
+ }
+ /* Multiply with opacity if needed */
+ if ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE) {
+ int x, y;
+ unsigned int a;
+ a = item->opacity;
+ for (y = carea.y0; y < carea.y1; y++) {
+ unsigned char *d;
+ d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
+ for (x = carea.x0; x < carea.x1; x++) {
+ d[0] = NR_PREMUL (d[0], a);
+ d += 1;
+ }
+ }
+ }
+ /* Compose rendering pixblock int destination */
+ // gettimeofday(&end_time,NULL);
+ g_get_current_time (&end_time);
+ duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
+ if ( !(ipb.empty) ) {
+ nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
+ if ( insert_cache(item,tile_h,tile_v,&ipb,&mpb,item->activity,(double)duration) ) {
+ } else {
+ nr_pixblock_release (&mpb);
+ nr_pixblock_release (&ipb);
+ }
+ dpb->empty = FALSE;
+ } else {
+ nr_pixblock_release (&ipb);
+ }
+ } else if ( ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE) ) {
+ /* Opacity only */
+ // gettimeofday(&end_time,NULL);
+ g_get_current_time (&end_time);
+ duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
+ if ( !(ipb.empty) ) {
+ nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
+ if ( insert_cache(item,tile_h,tile_v,&ipb,NULL,item->activity,(double)duration) ) {
+ } else {
+ nr_pixblock_release (&ipb);
+ }
+ dpb->empty = FALSE;
+ } else {
+ nr_pixblock_release (&ipb);
+ }
+ } else {
+ // gettimeofday(&end_time,NULL);
+ g_get_current_time (&end_time);
+ duration=(end_time.tv_sec-start_time.tv_sec)*1000+(end_time.tv_usec-start_time.tv_usec)/1000;
+ if ( !(ipb.empty) ) {
+ nr_blit_pixblock_pixblock (dpb, &ipb);
+ if ( insert_cache(item,tile_h,tile_v,&ipb,NULL,item->activity,(double)duration) ) {
+ } else {
+ nr_pixblock_release (&ipb);
+ }
+ dpb->empty = FALSE;
+ } else {
+ nr_pixblock_release (&ipb);
+ }
+ }
+#endif
+ } else {
+ /* Determine, whether we need temporary buffer */
+ if (item->clip || item->mask || ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE)) {
+ NRPixBlock ipb, mpb;
+
+ /* Setup and render item buffer */
+ nr_pixblock_setup_fast (&ipb, NR_PIXBLOCK_MODE_R8G8B8A8P, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
+ state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, &ipb, flags);
+ if (state & NR_ARENA_ITEM_STATE_INVALID) {
+ /* Clean up and return error */
+ nr_pixblock_release (&ipb);
+ if (dpb != pb) nr_pixblock_release (dpb);
+ item->state |= NR_ARENA_ITEM_STATE_INVALID;
+ return item->state;
+ }
+ ipb.empty = FALSE;
+
+ if (item->clip || item->mask) {
+ /* Setup mask pixblock */
+ nr_pixblock_setup_fast (&mpb, NR_PIXBLOCK_MODE_A8, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
+ /* Do clip if needed */
+ if (item->clip) {
+ state = nr_arena_item_invoke_clip (item->clip, &carea, &mpb);
+ if (state & NR_ARENA_ITEM_STATE_INVALID) {
+ /* Clean up and return error */
+ nr_pixblock_release (&mpb);
+ nr_pixblock_release (&ipb);
+ if (dpb != pb) nr_pixblock_release (dpb);
+ item->state |= NR_ARENA_ITEM_STATE_INVALID;
+ return item->state;
+ }
+ mpb.empty = FALSE;
+ }
+ /* Do mask if needed */
+ if (item->mask) {
+ NRPixBlock tpb;
+ /* Set up yet another temporary pixblock */
+ nr_pixblock_setup_fast (&tpb, NR_PIXBLOCK_MODE_R8G8B8A8N, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
+ state = NR_ARENA_ITEM_VIRTUAL (item->mask, render) (item->mask, &carea, &tpb, flags);
+ if (state & NR_ARENA_ITEM_STATE_INVALID) {
+ /* Clean up and return error */
+ nr_pixblock_release (&tpb);
+ nr_pixblock_release (&mpb);
+ nr_pixblock_release (&ipb);
+ if (dpb != pb) nr_pixblock_release (dpb);
+ item->state |= NR_ARENA_ITEM_STATE_INVALID;
+ return item->state;
+ }
+ /* Composite with clip */
+ if (item->clip) {
+ int x, y;
+ for (y = carea.y0; y < carea.y1; y++) {
+ unsigned char *s, *d;
+ s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
+ d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
+ for (x = carea.x0; x < carea.x1; x++) {
+ unsigned int m;
+ m = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
+ d[0] = NR_PREMUL (d[0], m);
+ s += 4;
+ d += 1;
+ }
+ }
+ } else {
+ int x, y;
+ for (y = carea.y0; y < carea.y1; y++) {
+ unsigned char *s, *d;
+ s = NR_PIXBLOCK_PX (&tpb) + (y - carea.y0) * tpb.rs;
+ d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
+ for (x = carea.x0; x < carea.x1; x++) {
+ unsigned int m;
+ m = ((s[0] + s[1] + s[2]) * s[3] + 127) / (3 * 255);
+ d[0] = m;
+ s += 4;
+ d += 1;
+ }
+ }
+ mpb.empty = FALSE;
+ }
+ nr_pixblock_release (&tpb);
+ }
+ /* Multiply with opacity if needed */
+ if ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE) {
+ int x, y;
+ unsigned int a;
+ a = item->opacity;
+ for (y = carea.y0; y < carea.y1; y++) {
+ unsigned char *d;
+ d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
+ for (x = carea.x0; x < carea.x1; x++) {
+ d[0] = NR_PREMUL (d[0], a);
+ d += 1;
+ }
+ }
+ }
+ /* Compose rendering pixblock int destination */
+ nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
+ nr_pixblock_release (&mpb);
+ } else {
+ /* Opacity only */
+ nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
+ }
+ nr_pixblock_release (&ipb);
+ dpb->empty = FALSE;
+ } else {
+ /* Just render */
+ state = NR_ARENA_ITEM_VIRTUAL (item, render) (item, &carea, dpb, flags);
+ if (state & NR_ARENA_ITEM_STATE_INVALID) {
+ /* Clean up and return error */
+ if (dpb != pb) nr_pixblock_release (dpb);
+ item->state |= NR_ARENA_ITEM_STATE_INVALID;
+ return item->state;
+ }
+ dpb->empty = FALSE;
+ }
+
+ if (dpb != pb) {
+ /* Have to blit from cache */
+ nr_blit_pixblock_pixblock (pb, dpb);
+ nr_pixblock_release (dpb);
+ pb->empty = FALSE;
+ item->state |= NR_ARENA_ITEM_STATE_IMAGE;
+ }
+ }
+ return item->state | NR_ARENA_ITEM_STATE_RENDER;
+}
+
+unsigned int
+nr_arena_item_invoke_clip (NRArenaItem *item, NRRectL *area, NRPixBlock *pb)
+{
+ nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
+ nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NR_ARENA_ITEM_STATE_INVALID);
+ /* we originally short-circuited if the object state included
+ * NR_ARENA_ITEM_STATE_CLIP (and showed a warning on the console);
+ * anyone know why we stopped doing so?
+ */
+ nr_return_val_if_fail ((pb->area.x1 - pb->area.x0) >= (area->x1 - area->x0), NR_ARENA_ITEM_STATE_INVALID);
+ nr_return_val_if_fail ((pb->area.y1 - pb->area.y0) >= (area->y1 - area->y0), NR_ARENA_ITEM_STATE_INVALID);
+
+#ifdef NR_ARENA_ITEM_VERBOSE
+ printf ("Invoke render %p: %d %d - %d %d\n", item, area->x0, area->y0, area->x1, area->y1);
+#endif
+
+ if (item->visible && nr_rect_l_test_intersect (area, &item->bbox)) {
+ /* Need render that item */
+ if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->clip)
+ return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS(item))->clip (item, area, pb);
+ }
+
+ return item->state;
+}
+
+NRArenaItem *
+nr_arena_item_invoke_pick (NRArenaItem *item, NR::Point p, double delta, unsigned int sticky)
+{
+ nr_return_val_if_fail (item != NULL, NULL);
+ nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
+
+ // Sometimes there's no BBOX in item->state, reason unknown (bug 992817); I made this not an assert to remove the warning
+ if (!(item->state & NR_ARENA_ITEM_STATE_BBOX) || !(item->state & NR_ARENA_ITEM_STATE_PICK))
+ return NULL;
+
+ if (!sticky && !(item->visible && item->sensitive)) return NULL;
+
+ // TODO: rewrite using NR::Rect
+ const double x = p[NR::X];
+ const double y = p[NR::Y];
+
+ if (((x + delta) >= item->bbox.x0) &&
+ ((x - delta) < item->bbox.x1) &&
+ ((y + delta) >= item->bbox.y0) &&
+ ((y - delta) < item->bbox.y1)) {
+ if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->pick)
+ return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->pick (item, p, delta, sticky);
+ }
+
+ return NULL;
+}
+
+void
+nr_arena_item_request_update (NRArenaItem *item, unsigned int reset, unsigned int propagate)
+{
+ nr_return_if_fail (item != NULL);
+ nr_return_if_fail (NR_IS_ARENA_ITEM (item));
+ nr_return_if_fail (!(reset & NR_ARENA_ITEM_STATE_INVALID));
+
+ if (propagate && !item->propagate) item->propagate = TRUE;
+
+ if (item->state & reset) {
+ item->state &= ~reset;
+ if (item->parent) {
+ nr_arena_item_request_update (item->parent, reset, FALSE);
+ } else {
+ nr_arena_request_update (item->arena, item);
+ }
+ }
+}
+
+void
+nr_arena_item_request_render (NRArenaItem *item)
+{
+ nr_return_if_fail (item != NULL);
+ nr_return_if_fail (NR_IS_ARENA_ITEM (item));
+
+ nr_arena_request_render_rect (item->arena, &item->bbox);
+}
+
+/* Public */
+
+NRArenaItem *
+nr_arena_item_unparent (NRArenaItem *item)
+{
+ nr_return_val_if_fail (item != NULL, NULL);
+ nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
+
+ nr_arena_item_request_render (item);
+
+ if (item->parent) {
+ nr_arena_item_remove_child (item->parent, item);
+ }
+
+ return NULL;
+}
+
+void
+nr_arena_item_append_child (NRArenaItem *parent, NRArenaItem *child)
+{
+ nr_return_if_fail (parent != NULL);
+ nr_return_if_fail (NR_IS_ARENA_ITEM (parent));
+ nr_return_if_fail (child != NULL);
+ nr_return_if_fail (NR_IS_ARENA_ITEM (child));
+ nr_return_if_fail (parent->arena == child->arena);
+ nr_return_if_fail (child->parent == NULL);
+ nr_return_if_fail (child->prev == NULL);
+ nr_return_if_fail (child->next == NULL);
+
+ nr_arena_item_add_child (parent, child, nr_arena_item_last_child (parent));
+}
+
+void
+nr_arena_item_set_transform(NRArenaItem *item, NR::Matrix const &transform)
+{
+ NRMatrix const t(transform);
+ nr_arena_item_set_transform(item, &t);
+}
+
+void
+nr_arena_item_set_transform(NRArenaItem *item, NRMatrix const *transform)
+{
+ const NRMatrix *ms, *md;
+
+ nr_return_if_fail (item != NULL);
+ nr_return_if_fail (NR_IS_ARENA_ITEM (item));
+
+ if (!transform && !item->transform) return;
+
+ md = (item->transform) ? item->transform : &NR_MATRIX_IDENTITY;
+ ms = (transform) ? transform : &NR_MATRIX_IDENTITY;
+
+ if (!NR_MATRIX_DF_TEST_CLOSE (md, ms, NR_EPSILON)) {
+ nr_arena_item_request_render (item);
+ if (!transform || nr_matrix_test_identity (transform, NR_EPSILON)) {
+ /* Set to identity affine */
+ if (item->transform) nr_free (item->transform);
+ item->transform = NULL;
+ } else {
+ if (!item->transform) item->transform = nr_new (NRMatrix, 1);
+ *item->transform = *transform;
+ }
+ nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
+ }
+}
+
+void
+nr_arena_item_set_opacity (NRArenaItem *item, double opacity)
+{
+ nr_return_if_fail (item != NULL);
+ nr_return_if_fail (NR_IS_ARENA_ITEM (item));
+
+ nr_arena_item_request_render (item);
+
+ item->opacity = (unsigned int) (opacity * 255.9999);
+}
+
+void
+nr_arena_item_set_sensitive (NRArenaItem *item, unsigned int sensitive)
+{
+ nr_return_if_fail (item != NULL);
+ nr_return_if_fail (NR_IS_ARENA_ITEM (item));
+
+ /* fixme: mess with pick/repick... */
+
+ item->sensitive = sensitive;
+}
+
+void
+nr_arena_item_set_visible (NRArenaItem *item, unsigned int visible)
+{
+ nr_return_if_fail (item != NULL);
+ nr_return_if_fail (NR_IS_ARENA_ITEM (item));
+
+ item->visible = visible;
+
+ nr_arena_item_request_render (item);
+}
+
+void
+nr_arena_item_set_clip (NRArenaItem *item, NRArenaItem *clip)
+{
+ nr_return_if_fail (item != NULL);
+ nr_return_if_fail (NR_IS_ARENA_ITEM (item));
+ nr_return_if_fail (!clip || NR_IS_ARENA_ITEM (clip));
+
+ if (clip != item->clip) {
+ nr_arena_item_request_render (item);
+ if (item->clip) item->clip = nr_arena_item_detach_unref (item, item->clip);
+ if (clip) item->clip = nr_arena_item_attach_ref (item, clip, NULL, NULL);
+ nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
+ }
+}
+
+void
+nr_arena_item_set_mask (NRArenaItem *item, NRArenaItem *mask)
+{
+ nr_return_if_fail (item != NULL);
+ nr_return_if_fail (NR_IS_ARENA_ITEM (item));
+ nr_return_if_fail (!mask || NR_IS_ARENA_ITEM (mask));
+
+ if (mask != item->mask) {
+ nr_arena_item_request_render (item);
+ if (item->mask) item->mask = nr_arena_item_detach_unref (item, item->mask);
+ if (mask) item->mask = nr_arena_item_attach_ref (item, mask, NULL, NULL);
+ nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
+ }
+}
+
+void
+nr_arena_item_set_order (NRArenaItem *item, int order)
+{
+ NRArenaItem *children, *child, *ref;
+ int pos;
+
+ nr_return_if_fail (item != NULL);
+ nr_return_if_fail (NR_IS_ARENA_ITEM (item));
+
+ if (!item->parent) return;
+
+ children = nr_arena_item_children (item->parent);
+
+ ref = NULL;
+ pos = 0;
+ for (child = children; child != NULL; child = child->next) {
+ if (pos >= order) break;
+ if (child != item) {
+ ref = child;
+ pos += 1;
+ }
+ }
+
+ nr_arena_item_set_child_position (item->parent, item, ref);
+}
+
+/* Helpers */
+
+NRArenaItem *
+nr_arena_item_attach_ref (NRArenaItem *parent, NRArenaItem *child, NRArenaItem *prev, NRArenaItem *next)
+{
+ nr_return_val_if_fail (parent != NULL, NULL);
+ nr_return_val_if_fail (NR_IS_ARENA_ITEM (parent), NULL);
+ nr_return_val_if_fail (child != NULL, NULL);
+ nr_return_val_if_fail (NR_IS_ARENA_ITEM (child), NULL);
+ nr_return_val_if_fail (child->parent == NULL, NULL);
+ nr_return_val_if_fail (child->prev == NULL, NULL);
+ nr_return_val_if_fail (child->next == NULL, NULL);
+ nr_return_val_if_fail (!prev || NR_IS_ARENA_ITEM (prev), NULL);
+ nr_return_val_if_fail (!prev || (prev->parent == parent), NULL);
+ nr_return_val_if_fail (!prev || (prev->next == next), NULL);
+ nr_return_val_if_fail (!next || NR_IS_ARENA_ITEM (next), NULL);
+ nr_return_val_if_fail (!next || (next->parent == parent), NULL);
+ nr_return_val_if_fail (!next || (next->prev == prev), NULL);
+
+ child->parent = parent;
+ child->prev = prev;
+ child->next = next;
+
+ if (prev) prev->next = child;
+ if (next) next->prev = child;
+
+ return child;
+}
+
+NRArenaItem *
+nr_arena_item_detach_unref (NRArenaItem *parent, NRArenaItem *child)
+{
+ NRArenaItem *prev, *next;
+
+ nr_return_val_if_fail (parent != NULL, NULL);
+ nr_return_val_if_fail (NR_IS_ARENA_ITEM (parent), NULL);
+ nr_return_val_if_fail (child != NULL, NULL);
+ nr_return_val_if_fail (NR_IS_ARENA_ITEM (child), NULL);
+ nr_return_val_if_fail (child->parent == parent, NULL);
+
+ prev = child->prev;
+ next = child->next;
+
+ child->parent = NULL;
+ child->prev = NULL;
+ child->next = NULL;
+
+ if (prev) prev->next = next;
+ if (next) next->prev = prev;
+
+ return next;
+}
+
+/*
+ *
+ * caches
+ *
+ */
+
+#ifdef arena_item_tile_cache
+typedef struct cache_entry {
+ int key;
+ double score;
+ NRArenaItem* owner;
+ int th,tv;
+ int prev,next;
+ NRPixBlock ipb;
+ bool hasMask;
+ NRPixBlock mpb;
+} cache_entry;
+
+int hash_max=2048,hash_fill=1024;
+
+int *keys=NULL;
+int nbCch=0;
+
+int nbEnt=0,maxEnt=0;
+cache_entry* entries=NULL;
+
+//#define tile_cache_stats
+#ifdef tile_cache_stats
+double hits=0,misses=0;
+int hitMissCount=0;
+#endif
+
+int hash_that(NRArenaItem* owner,int th,int tv)
+{
+ int res=GPOINTER_TO_INT(owner);
+ res*=17;
+ res+=th;
+ res*=59;
+ res+=tv;
+ res*=217;
+ if ( res < 0 ) res=-res;
+ res%=hash_max;
+ return res;
+}
+
+bool test_cache(NRArenaItem* owner,int th,int tv,NRPixBlock &ipb,NRPixBlock &mpb,bool &hasMask)
+{
+ if ( keys == NULL ) {
+ hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
+ hash_fill=(hash_max*3)/4;
+ keys=(int*)malloc(hash_max*sizeof(int));
+ for (int i=0;i<hash_max;i++) keys[i]=-1;
+ }
+ int key=hash_that(owner,th,tv);
+ if ( keys[key] < 0 ) {
+#ifdef tile_cache_stats
+ misses+=1.0;
+#endif
+ return false;
+ }
+ int cur=keys[key];
+ while ( cur >= 0 && cur < nbEnt ) {
+ if ( entries[cur].owner == owner && entries[cur].th == th && entries[cur].tv == tv ) {
+ hasMask=entries[cur].hasMask;
+ ipb=entries[cur].ipb;
+ mpb=entries[cur].mpb;
+#ifdef tile_cache_stats
+ hits+=1.0;
+#endif
+ return true;
+ }
+ cur=entries[cur].next;
+ }
+#ifdef tile_cache_stats
+ misses+=1.0;
+#endif
+ return false;
+}
+void remove_one_cache(int no)
+{
+ if ( no < 0 || no >= nbEnt ) return;
+
+ nr_pixblock_release(&entries[no].ipb);
+ if ( entries[no].hasMask ) nr_pixblock_release(&entries[no].mpb);
+
+ if ( entries[no].prev >= 0 ) entries[entries[no].prev].next=entries[no].next;
+ if ( entries[no].next >= 0 ) entries[entries[no].next].prev=entries[no].prev;
+ if ( entries[no].prev < 0 ) keys[entries[no].key]=entries[no].next;
+ entries[no].prev=entries[no].next=entries[no].key=-1;
+
+ if ( no == nbEnt-1 ) {
+ nbEnt--;
+ return;
+ }
+ entries[no]=entries[--nbEnt];
+ if ( entries[no].prev >= 0 ) entries[entries[no].prev].next=no;
+ if ( entries[no].next >= 0 ) entries[entries[no].next].prev=no;
+ if ( entries[no].prev < 0 ) keys[entries[no].key]=no;
+}
+void remove_caches(NRArenaItem* owner)
+{
+ if ( keys == NULL ) {
+ hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
+ hash_fill=(hash_max*3)/4;
+ keys=(int*)malloc(hash_max*sizeof(int));
+ for (int i=0;i<hash_max;i++) keys[i]=-1;
+ }
+ for (int i=nbEnt-1;i>=0;i--) {
+ if ( entries[i].owner == owner ) {
+ remove_one_cache(i);
+ }
+ }
+}
+void age_cache(void)
+{
+ for (int i=0;i<nbEnt;i++) entries[i].score*=0.95;
+}
+bool insert_cache(NRArenaItem* owner,int th,int tv,NRPixBlock *ipb,NRPixBlock *mpb,double activity,double duration)
+{
+ if ( keys == NULL ) {
+ hash_max = prefs_get_int_attribute ("options.arenatilescachesize", "value", 2048);
+ hash_fill=(hash_max*3)/4;
+ keys=(int*)malloc(hash_max*sizeof(int));
+ for (int i=0;i<hash_max;i++) keys[i]=-1;
+ }
+ for (int i=0;i<nbEnt;i++) entries[i].score*=0.95;
+#ifdef tile_cache_stats
+ hits*=0.95;
+ misses*=0.95;
+ hitMissCount++;
+ if ( hitMissCount > 100 ) {
+ hitMissCount=0;
+ printf("hit/miss = %f used/total=%i/%i\n",(misses>0.001)?hits/misses:100000.0,nbEnt,hash_max); // localizing ok
+ }
+#endif
+ int key=hash_that(owner,th,tv);
+ double nScore=/*activity**/duration;
+
+ if ( keys[key] >= 0 ) {
+ int cur=keys[key];
+ while ( cur >= 0 && cur < nbEnt ) {
+ if ( entries[cur].owner == owner && entries[cur].th == th && entries[cur].tv == tv ) {
+ remove_one_cache(cur);
+ break;
+ }
+ cur=entries[cur].next;
+ }
+ }
+
+ bool doAdd=false;
+ if ( nbEnt < hash_fill ) {
+ doAdd=true;
+ } else {
+ double worstS=entries[0].score;
+ int worstE=0;
+ for (int i=1;i<nbEnt;i++) {
+ if ( entries[i].score < worstS ) {
+ worstS=entries[i].score;
+ worstE=i;
+ }
+ }
+ if ( worstS < nScore ) {
+ doAdd=true;
+ remove_one_cache(worstE);
+ }
+ }
+ if ( doAdd == false ) return false;
+ if ( nbEnt >= maxEnt ) {
+ maxEnt=2*nbEnt+1;
+ entries=(cache_entry*)realloc(entries,maxEnt*sizeof(cache_entry));
+ }
+ entries[nbEnt].key=key;
+ entries[nbEnt].score=nScore;
+ entries[nbEnt].owner=owner;
+ entries[nbEnt].th=th;
+ entries[nbEnt].tv=tv;
+ entries[nbEnt].prev=entries[nbEnt].next=-1;
+ entries[nbEnt].ipb=*ipb;
+ if ( mpb ) {
+ entries[nbEnt].hasMask=true;
+ entries[nbEnt].mpb=*mpb;
+ } else {
+ entries[nbEnt].hasMask=false;
+ }
+ entries[nbEnt].next=keys[key];
+ if ( entries[nbEnt].next >= 0 ) entries[entries[nbEnt].next].prev=nbEnt;
+ keys[key]=nbEnt;
+
+ nbEnt++;
+ return true;
+}
+#endif
+
+
+
+