diff options
| -rw-r--r-- | src/display/canvas-arena.cpp | 125 | ||||
| -rw-r--r-- | src/display/canvas-arena.h | 4 | ||||
| -rw-r--r-- | src/display/drawing-context.cpp | 17 | ||||
| -rw-r--r-- | src/display/drawing-context.h | 2 | ||||
| -rw-r--r-- | src/display/drawing-surface.cpp | 137 | ||||
| -rw-r--r-- | src/display/drawing-surface.h | 22 | ||||
| -rw-r--r-- | src/display/nr-arena-item.cpp | 121 | ||||
| -rw-r--r-- | src/display/nr-arena-item.h | 24 | ||||
| -rw-r--r-- | src/display/nr-arena.cpp | 11 | ||||
| -rw-r--r-- | src/display/nr-arena.h | 4 | ||||
| -rw-r--r-- | src/display/sp-canvas-item.h | 2 | ||||
| -rw-r--r-- | src/display/sp-canvas.cpp | 27 |
12 files changed, 320 insertions, 176 deletions
diff --git a/src/display/canvas-arena.cpp b/src/display/canvas-arena.cpp index 1d5cfe826..0f653a258 100644 --- a/src/display/canvas-arena.cpp +++ b/src/display/canvas-arena.cpp @@ -33,10 +33,8 @@ static void sp_canvas_arena_destroy(GtkObject *object); static void sp_canvas_arena_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags); static void sp_canvas_arena_render (SPCanvasItem *item, SPCanvasBuf *buf); -static void sp_canvas_arena_render_cache (SPCanvasItem *item, Geom::IntRect const &area); -static void sp_canvas_arena_dirty_cache (SPCanvasArena *arena, NRRectL *area); static double sp_canvas_arena_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item); -static void sp_canvas_arena_visible_area_changed (SPCanvasItem *item, Geom::IntRect const &old_area, Geom::IntRect const &new_area); +static void sp_canvas_arena_viewbox_changed (SPCanvasItem *item, Geom::IntRect const &new_area); static gint sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event); static gint sp_canvas_arena_send_event (SPCanvasArena *arena, GdkEvent *event); @@ -98,7 +96,7 @@ sp_canvas_arena_class_init (SPCanvasArenaClass *klass) item_class->render = sp_canvas_arena_render; item_class->point = sp_canvas_arena_point; item_class->event = sp_canvas_arena_event; - item_class->visible_area_changed = sp_canvas_arena_visible_area_changed; + item_class->viewbox_changed = sp_canvas_arena_viewbox_changed; } static void @@ -110,11 +108,9 @@ sp_canvas_arena_init (SPCanvasArena *arena) arena->arena->canvasarena = arena; arena->root = NRArenaGroup::create(arena->arena); nr_arena_group_set_transparent (NR_ARENA_GROUP (arena->root), TRUE); + nr_arena_item_set_cache(arena->root, true); arena->active = NULL; - arena->cache = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1); - arena->cache_area = Geom::IntRect::from_xywh(0,0,1,1); - arena->dirty = cairo_region_create(); nr_active_object_add_listener ((NRActiveObject *) arena->arena, (NRObjectEventVector *) &carenaev, sizeof (carenaev), arena); } @@ -140,11 +136,6 @@ sp_canvas_arena_destroy (GtkObject *object) nr_object_unref ((NRObject *) arena->arena); arena->arena = NULL; } - if (arena->cache) { - cairo_surface_destroy(arena->cache); - arena->cache = NULL; - } - cairo_region_destroy(arena->dirty); if (GTK_OBJECT_CLASS (parent_class)->destroy) (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); @@ -209,42 +200,8 @@ sp_canvas_arena_render (SPCanvasItem *item, SPCanvasBuf *buf) Geom::OptIntRect r = buf->rect; if (!r || r->hasZeroArea()) return; - - cairo_rectangle_int_t crect; - crect.x = r->left(); - crect.y = r->top(); - crect.width = r->width(); - crect.height = r->height(); - if (cairo_region_contains_rectangle(arena->dirty, &crect) != CAIRO_REGION_OVERLAP_OUT) { - sp_canvas_arena_render_cache(item, *r); - cairo_region_subtract_rectangle(arena->dirty, &crect); - } - - cairo_save(buf->ct); - cairo_translate(buf->ct, -r->left(), -r->top()); - cairo_set_source_surface(buf->ct, arena->cache, arena->cache_area.left(), arena->cache_area.top()); - cairo_paint(buf->ct); - cairo_restore(buf->ct); -} - -static void sp_canvas_arena_render_cache (SPCanvasItem *item, Geom::IntRect const &area) -{ - SPCanvasArena *arena = SP_CANVAS_ARENA (item); - - Geom::OptIntRect r = Geom::intersect(arena->cache_area, area); - if (!r || r->hasZeroArea()) return; // nothing to do - - Inkscape::DrawingSurface cache(arena->cache, arena->cache_area.min()); - Inkscape::DrawingContext ct(cache); - ct.rectangle(area); - ct.clip(); - - { Inkscape::DrawingContext::Save save(ct); - ct.setSource(0,0,0,0); - ct.setOperator(CAIRO_OPERATOR_SOURCE); - ct.paint(); - } + Inkscape::DrawingContext ct(buf->ct, r->min()); nr_arena_item_invoke_update (arena->root, Geom::IntRect::infinite(), &arena->gc, NR_ARENA_ITEM_STATE_BBOX | NR_ARENA_ITEM_STATE_RENDER, @@ -252,17 +209,6 @@ static void sp_canvas_arena_render_cache (SPCanvasItem *item, Geom::IntRect cons nr_arena_item_invoke_render (ct, arena->root, *r, 0); } -static void -sp_canvas_arena_dirty_cache (SPCanvasArena *arena, NRRectL *area) -{ - cairo_rectangle_int_t rect; - rect.x = area->x0; - rect.y = area->y0; - rect.width = area->x1 - area->x0; - rect.height = area->y1 - area->y0; - cairo_region_union_rectangle(arena->dirty, &rect); -} - static double sp_canvas_arena_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item) { @@ -285,64 +231,14 @@ sp_canvas_arena_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_ } static void -sp_canvas_arena_visible_area_changed (SPCanvasItem *item, Geom::IntRect const &old_area, Geom::IntRect const &new_area) +sp_canvas_arena_viewbox_changed (SPCanvasItem *item, Geom::IntRect const &new_area) { SPCanvasArena *arena = SP_CANVAS_ARENA(item); - - cairo_surface_t *new_cache = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, - new_area.width(), new_area.height()); - cairo_t *ct = cairo_create(new_cache); - cairo_set_source_surface(ct, arena->cache, old_area.left() - new_area.left(), old_area.top() - new_area.top()); - cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE); - cairo_paint(ct); - cairo_destroy(ct); - cairo_surface_destroy(arena->cache); - arena->cache = new_cache; - arena->cache_area = new_area; - - cairo_rectangle_int_t crect; - crect.x = new_area.left(); - crect.y = new_area.top(); - crect.width = new_area.width(); - crect.height = new_area.height(); - cairo_region_intersect_rectangle(arena->dirty, &crect); - - // invalidate newly exposed areas - /* - * +----------------------+ - * | top strip | - * +-------+------+-------+ - * | | | | - * | left | old | right | - * | strip | area | strip | - * | | | | - * +-------+------+-------+ - * | bottom strip | - * +----------------------+ - */ - - // top strip - if (new_area.top() < old_area.top()) { - NRRectL top_strip(new_area.left(), new_area.top(), new_area.right(), old_area.top()); - sp_canvas_arena_dirty_cache(arena, &top_strip); - } - // left strip - if (new_area.left() < old_area.left()) { - NRRectL left_strip(new_area.left(), std::max(new_area.top(), old_area.top()), - old_area.left(), std::min(new_area.bottom(), old_area.bottom())); - sp_canvas_arena_dirty_cache(arena, &left_strip); - } - // right strip - if (new_area.right() > old_area.right()) { - NRRectL right_strip(old_area.right(), std::max(new_area.top(), old_area.top()), - new_area.right(), std::min(new_area.bottom(), old_area.bottom())); - sp_canvas_arena_dirty_cache(arena, &right_strip); - } - // bottom strip - if (new_area.bottom() > old_area.bottom()) { - NRRectL bottom_strip(new_area.left(), old_area.bottom(), new_area.right(), new_area.bottom()); - sp_canvas_arena_dirty_cache(arena, &bottom_strip); - } + // make the cache limit larger than screen to facilitate smooth scrolling + Geom::IntRect expanded = new_area; + Geom::IntPoint expansion(new_area.width()/2, new_area.height()/2); + expanded.expandBy(expansion); + nr_arena_set_cache_limit(arena->arena, expanded); } static gint @@ -445,7 +341,6 @@ static void sp_canvas_arena_request_render (NRArena */*arena*/, NRRectL *area, void *data) { if (!area) return; - sp_canvas_arena_dirty_cache (SP_CANVAS_ARENA(data), area); sp_canvas_request_redraw (SP_CANVAS_ITEM (data)->canvas, area->x0, area->y0, area->x1, area->y1); } diff --git a/src/display/canvas-arena.h b/src/display/canvas-arena.h index 220976da0..4cfeccb5a 100644 --- a/src/display/canvas-arena.h +++ b/src/display/canvas-arena.h @@ -45,10 +45,6 @@ struct _SPCanvasArena { /* fixme: */ NRArenaItem *picked; gdouble delta; - - Geom::IntRect cache_area; - cairo_surface_t *cache; - cairo_region_t *dirty; }; struct _SPCanvasArenaClass { diff --git a/src/display/drawing-context.cpp b/src/display/drawing-context.cpp index 8f37bb693..3c0c2163b 100644 --- a/src/display/drawing-context.cpp +++ b/src/display/drawing-context.cpp @@ -55,10 +55,23 @@ void DrawingContext::Save::save(DrawingContext &ct) * for drawing entire SPObjects when exporting. */ +DrawingContext::DrawingContext(cairo_t *ct, Geom::Point const &origin) + : _ct(ct) + , _surface(new DrawingSurface(cairo_get_group_target(ct), origin)) + , _delete_surface(true) + , _restore_context(true) +{ + _surface->_has_context = true; + cairo_reference(_ct); + cairo_save(_ct); + cairo_translate(_ct, -origin[Geom::X], -origin[Geom::Y]); +} + DrawingContext::DrawingContext(cairo_surface_t *surface, Geom::Point const &origin) : _ct(NULL) , _surface(new DrawingSurface(surface, origin)) , _delete_surface(true) + , _restore_context(false) { _surface->_has_context = true; _ct = _surface->createRawContext(); @@ -68,10 +81,14 @@ DrawingContext::DrawingContext(DrawingSurface &s) : _ct(s.createRawContext()) , _surface(&s) , _delete_surface(false) + , _restore_context(false) {} DrawingContext::~DrawingContext() { + if (_restore_context) { + cairo_restore(_ct); + } cairo_destroy(_ct); _surface->_has_context = false; if (_delete_surface) { diff --git a/src/display/drawing-context.h b/src/display/drawing-context.h index c0ea81874..8d2e7d68a 100644 --- a/src/display/drawing-context.h +++ b/src/display/drawing-context.h @@ -38,6 +38,7 @@ public: DrawingContext *_ct; }; + DrawingContext(cairo_t *ct, Geom::Point const &origin); DrawingContext(cairo_surface_t *surface, Geom::Point const &origin); DrawingContext(DrawingSurface &s); ~DrawingContext(); @@ -103,6 +104,7 @@ private: cairo_t *_ct; DrawingSurface *_surface; bool _delete_surface; + bool _restore_context; friend class DrawingSurface; }; diff --git a/src/display/drawing-surface.cpp b/src/display/drawing-surface.cpp index 41ff14167..28bdc1f3c 100644 --- a/src/display/drawing-surface.cpp +++ b/src/display/drawing-surface.cpp @@ -10,6 +10,7 @@ */ #include "display/drawing-surface.h" +#include "display/drawing-context.h" #include "display/cairo-utils.h" namespace Inkscape { @@ -43,18 +44,6 @@ DrawingSurface::DrawingSurface(Geom::IntRect const &area) , _pixels(area.dimensions()) {} -/** @brief Creates a surface with the given logical extents. - * When a drawing context is created for this surface, its pixels - * will cover the area under the given rectangle. If the rectangle - * has non-integer width, there will be slightly more than 1 pixel - * per logical unit. */ -DrawingSurface::DrawingSurface(Geom::Rect const &area) - : _surface(NULL) - , _origin(area.min()) - , _scale(ceil(area.width()) / area.width(), ceil(area.height()) / area.height()) - , _pixels(area.dimensions().ceil()) -{} - /** @brief Creates a surface with the given logical and physical extents. * When a drawing context is created for this surface, its pixels * will cover the area under the given rectangle. IT will contain @@ -164,6 +153,130 @@ DrawingSurface::createRawContext() return ct; } +Geom::IntRect +DrawingSurface::pixelArea() const +{ + Geom::IntRect ret = Geom::IntRect::from_xywh(_origin.round(), _pixels); + return ret; +} + +////////////////////////////////////////////////////////////////////////////// + +DrawingCache::DrawingCache(Geom::IntRect const &area) + : DrawingSurface(area) + , _clean_region(cairo_region_create()) +{} + +DrawingCache::~DrawingCache() +{ + cairo_region_destroy(_clean_region); +} + +void +DrawingCache::markDirty(Geom::IntRect const &area) +{ + cairo_rectangle_int_t dirty = _convertRect(area); + cairo_region_subtract_rectangle(_clean_region, &dirty); +} +void +DrawingCache::markClean(Geom::IntRect const &area) +{ + Geom::OptIntRect r = Geom::intersect(area, pixelArea()); + if (!r) return; + cairo_rectangle_int_t clean = _convertRect(*r); + cairo_region_union_rectangle(_clean_region, &clean); +} +bool +DrawingCache::isClean(Geom::IntRect const &area) const +{ + cairo_rectangle_int_t test = _convertRect(area); + if (cairo_region_contains_rectangle(_clean_region, &test) == CAIRO_REGION_OVERLAP_IN) { + return true; + } else { + return false; + } +} +void +DrawingCache::resizeAndTransform(Geom::IntRect const &new_area, Geom::Affine const &trans) +{ + Geom::IntRect old_area = pixelArea(); + bool is_identity = false; + bool is_integer_translation = false; + if (trans.isIdentity()) { + is_identity = true; + if (new_area == old_area) return; + } + if (!is_identity && trans.isTranslation()) { + Geom::IntPoint t = trans.translation().round(); + if (Geom::are_near(Geom::Point(t), trans.translation())) { + // integer translation or identity with change of area + is_integer_translation = true; + cairo_region_translate(_clean_region, t[X], t[Y]); + if (old_area + t == new_area) { + // if the areas match, the only thing to do + // is to ensure that the clean area is not too large + cairo_rectangle_int_t limit = _convertRect(new_area); + cairo_region_intersect_rectangle(_clean_region, &limit); + _origin += t; + return; + } + } + } + // otherwise, we need to transform the cache + Geom::IntPoint old_origin = old_area.min(); + cairo_surface_t *old_surface = _surface; + _surface = NULL; + _pixels = new_area.dimensions(); + _origin = new_area.min(); + + cairo_t *ct = createRawContext(); + if (!is_identity) { + ink_cairo_transform(ct, trans); + } + cairo_set_source_surface(ct, old_surface, old_origin[X], old_origin[Y]); + cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE); + cairo_paint(ct); + + cairo_surface_destroy(old_surface); + cairo_destroy(ct); + + if (!is_identity && !is_integer_translation) { + // dirty everything + cairo_region_destroy(_clean_region); + _clean_region = cairo_region_create(); + } else { + cairo_rectangle_int_t limit = _convertRect(new_area); + cairo_region_intersect_rectangle(_clean_region, &limit); + } +} + +/** @brief Paints the clean area from cache and returns the remaining part */ +bool +DrawingCache::paintFromCache(DrawingContext &ct, Geom::IntRect const &area) +{ + if (!isClean(area)) + return false; + + Inkscape::DrawingContext::Save save(ct); + ct.rectangle(area); + ct.clip(); + ct.setSource(this); + ct.paint(); + + return true; +} + +cairo_rectangle_int_t +DrawingCache::_convertRect(Geom::IntRect const &area) +{ + cairo_rectangle_int_t ret; + ret.x = area.left(); + ret.y = area.top(); + ret.width = area.width(); + ret.height = area.height(); + return ret; +} + } // end namespace Inkscape /* diff --git a/src/display/drawing-surface.h b/src/display/drawing-surface.h index e26bc28fa..f279d771b 100644 --- a/src/display/drawing-surface.h +++ b/src/display/drawing-surface.h @@ -26,7 +26,6 @@ class DrawingSurface { public: explicit DrawingSurface(Geom::IntRect const &area); - explicit DrawingSurface(Geom::Rect const &area); DrawingSurface(Geom::Rect const &logbox, Geom::IntPoint const &pixdims); DrawingSurface(cairo_surface_t *surface, Geom::Point const &origin); virtual ~DrawingSurface(); @@ -44,6 +43,8 @@ public: cairo_t *createRawContext(); protected: + Geom::IntRect pixelArea() const; + cairo_surface_t *_surface; Geom::Point _origin; Geom::Scale _scale; @@ -53,16 +54,23 @@ protected: friend class DrawingContext; }; -class PixbufSurface +class DrawingCache : public DrawingSurface { public: - explicit PixbufSurface(GdkPixbuf *pb, Geom::Point const &origin = Geom::Point(0,0)); - ~PixbufSurface(); -protected: - GdkPixbuf *pb; + explicit DrawingCache(Geom::IntRect const &area); + ~DrawingCache(); - friend class DrawingContext; + void markDirty(Geom::IntRect const &area = Geom::IntRect::infinite()); + void markClean(Geom::IntRect const &area = Geom::IntRect::infinite()); + bool isClean(Geom::IntRect const &area) const; + void resizeAndTransform(Geom::IntRect const &new_area, Geom::Affine const &trans); + bool paintFromCache(DrawingContext &ct, Geom::IntRect const &area); + +protected: + cairo_region_t *_clean_region; +private: + static cairo_rectangle_int_t _convertRect(Geom::IntRect const &r); }; } // end namespace Inkscape diff --git a/src/display/nr-arena-item.cpp b/src/display/nr-arena-item.cpp index c1ffefa1d..264b8ab10 100644 --- a/src/display/nr-arena-item.cpp +++ b/src/display/nr-arena-item.cpp @@ -90,14 +90,14 @@ nr_arena_item_init (NRArenaItem *item) item->ctm.setIdentity(); item->opacity = 255; item->render_opacity = FALSE; + item->render_cache = FALSE; item->transform = NULL; item->clip = NULL; item->mask = NULL; - item->px = NULL; + item->cache = NULL; item->data = NULL; item->filter = NULL; - item->background_pb = NULL; item->background_new = false; } @@ -106,13 +106,13 @@ nr_arena_item_private_finalize (NRObject *object) { NRArenaItem *item = static_cast < NRArenaItem * >(object); - item->px = NULL; item->transform = NULL; if (item->clip) nr_arena_item_detach(item, item->clip); if (item->mask) nr_arena_item_detach(item, item->mask); + delete item->cache; ((NRObjectClass *) (parent_class))->finalize (object); } @@ -246,21 +246,18 @@ nr_arena_item_invoke_update (NRArenaItem *item, Geom::IntRect const &area, NRGC return item->state; /* Test whether to return immediately */ if (item->state & NR_ARENA_ITEM_STATE_BBOX) { + // we have up-to-date bbox if (!area.intersects(outline ? item->bbox : item->drawbox)) return item->state; } - /* Reset image cache, if not to be kept */ - if (!(item->state & NR_ARENA_ITEM_STATE_IMAGE) && (item->px)) { - item->px = NULL; - } - /* Set up local gc */ childgc = *gc; if (item->transform) { childgc.transform = (*item->transform) * childgc.transform; } /* Remember the transformation matrix */ + Geom::Affine ctm_change = item->ctm.inverse() * childgc.transform; item->ctm = childgc.transform; /* Invoke the real method */ @@ -307,10 +304,31 @@ nr_arena_item_invoke_update (NRArenaItem *item, Geom::IntRect const &area, NRGC } } + // update cache if enabled + if (item->render_cache) { + Geom::OptIntRect cl = item->arena->cache_limit; + cl.intersectWith(item->drawbox); + if (cl) { + if (item->cache) { + // this takes care of invalidation on transform + item->cache->resizeAndTransform(*cl, ctm_change); + } else { + item->cache = new Inkscape::DrawingCache(*cl); + // the cache is initially dirty + } + } else { + // disable cache for this item - not visible + delete item->cache; + item->cache = NULL; + } + } + // now that we know drawbox, dirty the corresponding rect on canvas: if (!NR_IS_ARENA_GROUP(item) || (item->filter && filter)) { // unless filtered, groups do not need to render by themselves, only their members - nr_arena_item_request_render (item); + if (state & ~NR_ARENA_ITEM_STATE_CACHE) { + nr_arena_item_request_render (item); + } } return item->state; @@ -379,12 +397,19 @@ nr_arena_item_invoke_render (Inkscape::DrawingContext &ct, NRArenaItem *item, Ge return item->state | NR_ARENA_ITEM_STATE_RENDER; } - + // carea is the bounding box for intermediate rendering. - // NOTE: carea might be larger than area, because of filter effects. Geom::OptIntRect carea = Geom::intersect(area, item->drawbox); if (!carea) return item->state | NR_ARENA_ITEM_STATE_RENDER; + + // render from cache + if (item->render_cache && item->cache) { + if(item->cache->paintFromCache(ct, *carea)) + return item->state | NR_ARENA_ITEM_STATE_RENDER; + } + + // expand carea to contain the dependent area of filters. if (item->filter && filter) { item->filter->area_enlarge(*carea, item); carea.intersectWith(item->drawbox); @@ -422,12 +447,40 @@ nr_arena_item_invoke_render (Inkscape::DrawingContext &ct, NRArenaItem *item, Ge // short-circuit the simple case. if (!needs_intermediate_rendering) { - state = NR_ARENA_ITEM_VIRTUAL (item, render) (ct, item, *carea, flags); - if (state & NR_ARENA_ITEM_STATE_INVALID) { - item->state |= NR_ARENA_ITEM_STATE_INVALID; - return item->state; + if (item->render_cache && item->cache) { + Inkscape::DrawingContext cachect(*item->cache); + cachect.rectangle(area); + cachect.clip(); + + { // 1. clear the corresponding part of cache + Inkscape::DrawingContext::Save save(cachect); + cachect.setSource(0,0,0,0); + cachect.setOperator(CAIRO_OPERATOR_SOURCE); + cachect.paint(); + } + // 2. render to cache + state = NR_ARENA_ITEM_VIRTUAL (item, render) (cachect, item, *carea, flags); + if (state & NR_ARENA_ITEM_STATE_INVALID) { + item->state |= NR_ARENA_ITEM_STATE_INVALID; + return item->state; + } + // 3. copy from cache to output + Inkscape::DrawingContext::Save save(ct); + ct.rectangle(*carea); + ct.clip(); + ct.setSource(item->cache); + ct.paint(); + // 4. mark as clean + item->cache->markClean(area); + return item->state | NR_ARENA_ITEM_STATE_RENDER; + } else { + state = NR_ARENA_ITEM_VIRTUAL (item, render) (ct, item, *carea, flags); + if (state & NR_ARENA_ITEM_STATE_INVALID) { + item->state |= NR_ARENA_ITEM_STATE_INVALID; + return item->state; + } + return item->state | NR_ARENA_ITEM_STATE_RENDER; } - return item->state | NR_ARENA_ITEM_STATE_RENDER; } DrawingSurface intermediate(*carea); @@ -490,7 +543,16 @@ nr_arena_item_invoke_render (Inkscape::DrawingContext &ct, NRArenaItem *item, Ge ict.setOperator(CAIRO_OPERATOR_IN); ict.paint(); - // 6. Paint the completed rendering onto the base context + // 6. Paint the completed rendering onto the base context (or into cache) + if (item->render_cache && item->cache) { + DrawingContext cachect(*item->cache); + cachect.rectangle(area); + cachect.clip(); + cachect.setOperator(CAIRO_OPERATOR_SOURCE); + cachect.setSource(&intermediate); + cachect.paint(); + item->cache->markClean(area); + } ct.setSource(&intermediate); ct.paint(); ct.setSource(0,0,0,0); @@ -601,7 +663,17 @@ nr_arena_item_request_render (NRArenaItem *item) nr_return_if_fail (NR_IS_ARENA_ITEM (item)); bool outline = (item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE); - nr_arena_request_render_rect (item->arena, outline ? item->bbox : item->drawbox); + Geom::OptIntRect dirty = outline ? item->bbox : item->drawbox; + if (!dirty) return; + + // dirty the caches of all parents + for (NRArenaItem *i = item; i; i = i->parent) { + if (i->render_cache && i->cache) { + i->cache->markDirty(*dirty); + } + } + + nr_arena_request_render_rect (item->arena, dirty); } /* Public */ @@ -773,6 +845,19 @@ nr_arena_item_set_item_bbox (NRArenaItem *item, Geom::OptRect const &bbox) item->item_bbox = bbox; } +void +nr_arena_item_set_cache (NRArenaItem *item, bool cache) +{ + if (cache) { + item->render_cache = TRUE; + item->arena->cached_items.insert(item); + } else { + item->render_cache = FALSE; + item->arena->cached_items.erase(item); + } + nr_arena_item_request_update(item, NR_ARENA_ITEM_STATE_ALL, FALSE); +} + /** Returns a background image for use with filter effects. */ NRPixBlock *nr_arena_item_get_background(NRArenaItem const * /*item*/) { diff --git a/src/display/nr-arena-item.h b/src/display/nr-arena-item.h index 4b43e4da8..2c00c0bf3 100644 --- a/src/display/nr-arena-item.h +++ b/src/display/nr-arena-item.h @@ -24,6 +24,7 @@ namespace Inkscape { class DrawingContext; +class DrawingCache; namespace Filters { class Filter; } } @@ -57,15 +58,17 @@ class Filter; #define NR_ARENA_ITEM_STATE_MASK (1 << 6) #define NR_ARENA_ITEM_STATE_PICK (1 << 7) #define NR_ARENA_ITEM_STATE_IMAGE (1 << 8) +#define NR_ARENA_ITEM_STATE_CACHE (1 << 9) #define NR_ARENA_ITEM_STATE_NONE 0x0000 -#define NR_ARENA_ITEM_STATE_ALL 0x01fe +#define NR_ARENA_ITEM_STATE_ALL 0x03fe #define NR_ARENA_ITEM_STATE(i,s) (NR_ARENA_ITEM (i)->state & (s)) #define NR_ARENA_ITEM_SET_STATE(i,s) (NR_ARENA_ITEM (i)->state |= (s)) #define NR_ARENA_ITEM_UNSET_STATE(i,s) (NR_ARENA_ITEM (i)->state &= ~(s)) #define NR_ARENA_ITEM_RENDER_NO_CACHE (1 << 0) +#define NR_ARENA_ITEM_RENDER_CACHE (1 << 1) struct NRGC { NRGC(NRGC const *p) : parent(p) {} @@ -81,14 +84,15 @@ struct NRArenaItem : public NRObject { Inkscape::GC::soft_ptr<NRArenaItem> prev; /* Item state */ - unsigned int state : 16; - unsigned int propagate : 1; - unsigned int sensitive : 1; - unsigned int visible : 1; - /* Whether items renders opacity itself */ - unsigned int render_opacity : 1; + unsigned state : 16; /* Opacity itself */ - unsigned int opacity : 8; + unsigned opacity : 8; + unsigned propagate : 1; + unsigned sensitive : 1; + unsigned visible : 1; + /* Whether items renders opacity itself */ + unsigned render_opacity : 1; + unsigned render_cache : 1; unsigned int key; ///< Some SPItems can have more than one NRArenaItem, ///this value is a hack used to distinguish between them @@ -101,11 +105,10 @@ struct NRArenaItem : public NRObject { NRArenaItem *clip; ///< Clipping path NRArenaItem *mask; ///< Mask Inkscape::Filters::Filter *filter; ///< Filter - unsigned char *px; ///< Render cache; unused + Inkscape::DrawingCache *cache; ///< Render cache void *data; ///< Anonymous data member - this is used to associate SPItems with arena items - NRPixBlock *background_pb; ///< Background for filters; unused bool background_new; void init(NRArena *arena) { @@ -173,6 +176,7 @@ void nr_arena_item_set_clip (NRArenaItem *item, NRArenaItem *clip); void nr_arena_item_set_mask (NRArenaItem *item, NRArenaItem *mask); void nr_arena_item_set_order (NRArenaItem *item, int order); void nr_arena_item_set_item_bbox (NRArenaItem *item, Geom::OptRect const &bbox); +void nr_arena_item_set_cache (NRArenaItem *item, bool cache); NRPixBlock *nr_arena_item_get_background (NRArenaItem const *item); diff --git a/src/display/nr-arena.cpp b/src/display/nr-arena.cpp index 5747de26c..735d44e9e 100644 --- a/src/display/nr-arena.cpp +++ b/src/display/nr-arena.cpp @@ -151,6 +151,17 @@ nr_arena_set_renderoffscreen (NRArena *arena) } +void +nr_arena_set_cache_limit (NRArena *arena, Geom::OptIntRect const &cache_limit) +{ + arena->cache_limit = cache_limit; + for (std::set<NRArenaItem *>::iterator i = arena->cached_items.begin(); + i != arena->cached_items.end(); ++i) + { + nr_arena_item_request_update(*i, NR_ARENA_ITEM_STATE_CACHE, FALSE); + } +} + #define FLOAT_TO_UINT8(f) (int(f*255)) #define RGBA_R(v) ((v) >> 24) #define RGBA_G(v) (((v) >> 16) & 0xff) diff --git a/src/display/nr-arena.h b/src/display/nr-arena.h index 49d133f9f..5d078e19d 100644 --- a/src/display/nr-arena.h +++ b/src/display/nr-arena.h @@ -27,6 +27,7 @@ G_END_DECLS #define NR_ARENA(o) (NR_CHECK_INSTANCE_CAST ((o), NR_TYPE_ARENA, NRArena)) #define NR_IS_ARENA(o) (NR_CHECK_INSTANCE_TYPE ((o), NR_TYPE_ARENA)) +#include <set> #include <2geom/rect.h> #include <libnr/nr-forward.h> #include <libnr/nr-object.h> @@ -53,6 +54,8 @@ struct NRArena : public NRActiveObject { Inkscape::ColorRenderMode colorrendermode; int blurquality; // will be updated during update from preferences int filterquality; // will be updated during update from preferences + Geom::OptIntRect cache_limit; + std::set<NRArenaItem *> cached_items; guint32 outlinecolor; SPCanvasArena *canvasarena; // may be NULL is this arena is not the screen but used for export etc. @@ -64,6 +67,7 @@ struct NRArenaClass : public NRActiveObjectClass { void nr_arena_request_update (NRArena *arena, NRArenaItem *item); void nr_arena_request_render_rect (NRArena *arena, Geom::OptIntRect const &area); void nr_arena_set_renderoffscreen (NRArena *arena); +void nr_arena_set_cache_limit (NRArena *arena, Geom::OptIntRect const &cache_limit); void nr_arena_separate_color_plates(guint32* rgba); diff --git a/src/display/sp-canvas-item.h b/src/display/sp-canvas-item.h index 4c731e56b..415c36566 100644 --- a/src/display/sp-canvas-item.h +++ b/src/display/sp-canvas-item.h @@ -64,7 +64,7 @@ struct _SPCanvasItemClass : public GtkObjectClass { double (* point) (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item); int (* event) (SPCanvasItem *item, GdkEvent *event); - void (* visible_area_changed) (SPCanvasItem *item, Geom::IntRect const &old_area, Geom::IntRect const &new_area); + void (* viewbox_changed) (SPCanvasItem *item, Geom::IntRect const &new_area); }; SPCanvasItem *sp_canvas_item_new(SPCanvasGroup *parent, GType type, const gchar *first_arg_name, ...); diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp index 71f608118..7d6727ff3 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -689,7 +689,7 @@ static void sp_canvas_group_destroy (GtkObject *object); static void sp_canvas_group_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags); static double sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item); static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf); -static void sp_canvas_group_visible_area_changed (SPCanvasItem *item, Geom::IntRect const &old_area, Geom::IntRect const &new_area); +static void sp_canvas_group_viewbox_changed (SPCanvasItem *item, Geom::IntRect const &new_area); static SPCanvasItemClass *group_parent_class; @@ -733,7 +733,7 @@ sp_canvas_group_class_init (SPCanvasGroupClass *klass) item_class->update = sp_canvas_group_update; item_class->render = sp_canvas_group_render; item_class->point = sp_canvas_group_point; - item_class->visible_area_changed = sp_canvas_group_visible_area_changed; + item_class->viewbox_changed = sp_canvas_group_viewbox_changed; } /** @@ -878,15 +878,15 @@ sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf) } static void -sp_canvas_group_visible_area_changed (SPCanvasItem *item, Geom::IntRect const &old_area, Geom::IntRect const &new_area) +sp_canvas_group_viewbox_changed (SPCanvasItem *item, Geom::IntRect const &new_area) { SPCanvasGroup *group = SP_CANVAS_GROUP (item); for (GList *list = group->items; list; list = list->next) { SPCanvasItem *child = (SPCanvasItem *)list->data; if (child->flags & SP_CANVAS_ITEM_VISIBLE) { - if (SP_CANVAS_ITEM_GET_CLASS (child)->visible_area_changed) - SP_CANVAS_ITEM_GET_CLASS (child)->visible_area_changed (child, old_area, new_area); + if (SP_CANVAS_ITEM_GET_CLASS (child)->viewbox_changed) + SP_CANVAS_ITEM_GET_CLASS (child)->viewbox_changed (child, new_area); } } } @@ -1234,8 +1234,8 @@ sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation) /* Schedule redraw of new region */ sp_canvas_resize_tiles(canvas,canvas->x0,canvas->y0,canvas->x0+allocation->width,canvas->y0+allocation->height); - if (SP_CANVAS_ITEM_GET_CLASS (canvas->root)->visible_area_changed) - SP_CANVAS_ITEM_GET_CLASS (canvas->root)->visible_area_changed (canvas->root, old_area, new_area); + if (SP_CANVAS_ITEM_GET_CLASS (canvas->root)->viewbox_changed) + SP_CANVAS_ITEM_GET_CLASS (canvas->root)->viewbox_changed (canvas->root, new_area); if (allocation->width > widget->allocation.width) { sp_canvas_request_redraw (canvas, @@ -1655,6 +1655,15 @@ static void sp_canvas_paint_single_buffer(SPCanvas *canvas, int x0, int y0, int buf.is_empty = true; //buf.ct = gdk_cairo_create(widget->window); + /* + cairo_t *xctt = gdk_cairo_create(widget->window); + cairo_translate(xctt, x0 - canvas->x0, y0 - canvas->y0); + cairo_set_source_rgb(xctt, 1,0,0); + cairo_rectangle(xctt, 0, 0, x1-x0, y1-y0); + cairo_fill(xctt); + cairo_destroy(xctt); + //*/ + // create temporary surface int w = x1 - x0; int h = y1 - y0; @@ -2168,8 +2177,8 @@ sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, canvas->y0 = iy; sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height); - if (SP_CANVAS_ITEM_GET_CLASS (canvas->root)->visible_area_changed) - SP_CANVAS_ITEM_GET_CLASS (canvas->root)->visible_area_changed (canvas->root, old_area, new_area); + if (SP_CANVAS_ITEM_GET_CLASS (canvas->root)->viewbox_changed) + SP_CANVAS_ITEM_GET_CLASS (canvas->root)->viewbox_changed (canvas->root, new_area); if (!clear) { // scrolling without zoom; redraw only the newly exposed areas |
