Logo Search packages:      
Sourcecode: inkscape version File versions  Download package

nr-arena-item.cpp

#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 <string.h>
#include <assert.h>

#include <libnr/nr-macros.h>
#include <libnr/nr-rect.h>
#include <libnr/nr-matrix.h>
#include <libnr/nr-blit.h>
#include <libnr/nr-pixops.h>
#include "nr-arena.h"
#include "nr-arena-item.h"
//#include "nr-arena-group.h"

#include "../prefs-utils.h"
#include <sys/time.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) {
        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) ) {
      /* 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)) {
      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) {
          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





Generated by  Doxygen 1.6.0   Back to index