summaryrefslogtreecommitdiffstats
path: root/src/display
diff options
context:
space:
mode:
authorKrzysztof Kosi??ski <tweenk.pl@gmail.com>2011-07-28 05:04:08 +0000
committerKrzysztof KosiƄski <tweenk.pl@gmail.com>2011-07-28 05:04:08 +0000
commit905b8a96963f78358abfd109c0c49758c6fe4e9d (patch)
treef36eff88e8cc148264f9ea46df7b525c6a9ea80e /src/display
parentAdd deferred allocation functionality to DrawingSurface (diff)
downloadinkscape-905b8a96963f78358abfd109c0c49758c6fe4e9d.tar.gz
inkscape-905b8a96963f78358abfd109c0c49758c6fe4e9d.zip
Per-item render cache.
Cache some offscreen data to facilitate smoother navigation. (bzr r10347.1.20)
Diffstat (limited to 'src/display')
-rw-r--r--src/display/canvas-arena.cpp125
-rw-r--r--src/display/canvas-arena.h4
-rw-r--r--src/display/drawing-context.cpp17
-rw-r--r--src/display/drawing-context.h2
-rw-r--r--src/display/drawing-surface.cpp137
-rw-r--r--src/display/drawing-surface.h22
-rw-r--r--src/display/nr-arena-item.cpp121
-rw-r--r--src/display/nr-arena-item.h24
-rw-r--r--src/display/nr-arena.cpp11
-rw-r--r--src/display/nr-arena.h4
-rw-r--r--src/display/sp-canvas-item.h2
-rw-r--r--src/display/sp-canvas.cpp27
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