diff options
| author | Krzysztof Kosi??ski <tweenk.pl@gmail.com> | 2011-07-09 00:52:06 +0000 |
|---|---|---|
| committer | Krzysztof KosiĆski <tweenk.pl@gmail.com> | 2011-07-09 00:52:06 +0000 |
| commit | a3e406b2ceafb8fb6b44db0a7816a083d7b3c8ee (patch) | |
| tree | 7e9a749edf47feed38908b5dad896f79f791c4e2 /src/display | |
| parent | Merge from trunk (diff) | |
| download | inkscape-a3e406b2ceafb8fb6b44db0a7816a083d7b3c8ee.tar.gz inkscape-a3e406b2ceafb8fb6b44db0a7816a083d7b3c8ee.zip | |
Add SPCanvasArena caching layer. Currently breaks for clipped groups
that contain filtered objects (Cairo clipping bug?)
(bzr r10347.1.6)
Diffstat (limited to 'src/display')
| -rw-r--r-- | src/display/canvas-arena.cpp | 154 | ||||
| -rw-r--r-- | src/display/canvas-arena.h | 4 | ||||
| -rw-r--r-- | src/display/nr-arena-item.cpp | 4 | ||||
| -rw-r--r-- | src/display/sp-canvas-item.h | 4 | ||||
| -rw-r--r-- | src/display/sp-canvas.cpp | 30 |
5 files changed, 173 insertions, 23 deletions
diff --git a/src/display/canvas-arena.cpp b/src/display/canvas-arena.cpp index 6930e4d7c..dd4a4ed5c 100644 --- a/src/display/canvas-arena.cpp +++ b/src/display/canvas-arena.cpp @@ -31,7 +31,10 @@ 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 gint sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event); static gint sp_canvas_arena_send_event (SPCanvasArena *arena, GdkEvent *event); @@ -93,6 +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; } static void @@ -106,6 +110,9 @@ sp_canvas_arena_init (SPCanvasArena *arena) nr_arena_group_set_transparent (NR_ARENA_GROUP (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); } @@ -131,6 +138,11 @@ 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); @@ -187,33 +199,74 @@ sp_canvas_arena_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned static void sp_canvas_arena_render (SPCanvasItem *item, SPCanvasBuf *buf) { - gint bw, bh; - SPCanvasArena *arena = SP_CANVAS_ARENA (item); //SPCanvas *canvas = item->canvas; - nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, - NR_ARENA_ITEM_STATE_BBOX | NR_ARENA_ITEM_STATE_RENDER, - NR_ARENA_ITEM_STATE_NONE); + //nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, + // NR_ARENA_ITEM_STATE_BBOX | NR_ARENA_ITEM_STATE_RENDER, + // NR_ARENA_ITEM_STATE_NONE); + + 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); + } - sp_canvas_prepare_buffer(buf); + cairo_save(buf->ct); + cairo_translate(buf->ct, -r->left(), -r->top()); + //cairo_rectangle(buf->ct, r->left(), r->top(), r->width(), r->height()); + //cairo_clip(buf->ct); + cairo_set_source_surface(buf->ct, arena->cache, arena->cache_area.left(), arena->cache_area.top()); + cairo_paint(buf->ct); + //nr_arena_item_invoke_render (buf->ct, arena->root, &area, NULL, 0); + cairo_restore(buf->ct); +} - bw = buf->rect.x1 - buf->rect.x0; - bh = buf->rect.y1 - buf->rect.y0; - if ((bw < 1) || (bh < 1)) return; +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 + + cairo_t *ct = cairo_create(arena->cache); + cairo_translate(ct, -arena->cache_area.left(), -arena->cache_area.top()); + + // clear area to paint + cairo_rectangle(ct, area.left(), area.top(), area.width(), area.height()); + cairo_clip(ct); + cairo_save(ct); + cairo_set_source_rgba(ct, 0,0,0,0); + cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE); + cairo_paint(ct); + cairo_restore(ct); + + NRRectL nr_area(r); - NRRectL area; + nr_arena_item_invoke_update (arena->root, NULL, &arena->gc, + NR_ARENA_ITEM_STATE_BBOX | NR_ARENA_ITEM_STATE_RENDER, + NR_ARENA_ITEM_STATE_NONE); + nr_arena_item_invoke_render (ct, arena->root, &nr_area, NULL, 0); - area.x0 = buf->rect.x0; - area.y0 = buf->rect.y0; - area.x1 = buf->rect.x1; - area.y1 = buf->rect.y1; + cairo_destroy(ct); +} - sp_canvas_prepare_buffer(buf); - cairo_save(buf->ct); - cairo_translate(buf->ct, -area.x0, -area.y0); - nr_arena_item_invoke_render (buf->ct, arena->root, &area, NULL, 0); - cairo_restore(buf->ct); +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 @@ -237,6 +290,67 @@ sp_canvas_arena_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_ return 1e18; } +static void +sp_canvas_arena_visible_area_changed (SPCanvasItem *item, Geom::IntRect const &old_area, 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); + } +} + static gint sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event) { @@ -336,6 +450,8 @@ sp_canvas_arena_request_update (NRArena */*arena*/, NRArenaItem */*item*/, void 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 4cfeccb5a..220976da0 100644 --- a/src/display/canvas-arena.h +++ b/src/display/canvas-arena.h @@ -45,6 +45,10 @@ 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/nr-arena-item.cpp b/src/display/nr-arena-item.cpp index 526882921..9c7af1077 100644 --- a/src/display/nr-arena-item.cpp +++ b/src/display/nr-arena-item.cpp @@ -21,6 +21,7 @@ #include "display/cairo-utils.h" #include "display/cairo-templates.h" +#include "display/canvas-arena.h" #include "nr-arena.h" #include "nr-arena-item.h" #include "gc-core.h" @@ -482,7 +483,8 @@ nr_arena_item_invoke_render (cairo_t *ct, NRArenaItem *item, NRRectL const *area // apply filter if (item->filter && filter) { - item->filter->render(item, ct, area, this_ct, &carea); + NRRectL bgarea(item->arena->canvasarena->cache_area); + item->filter->render(item, ct, &bgarea, this_ct, &carea); } if (needs_intermediate_rendering) { diff --git a/src/display/sp-canvas-item.h b/src/display/sp-canvas-item.h index 9dbec547e..4c731e56b 100644 --- a/src/display/sp-canvas-item.h +++ b/src/display/sp-canvas-item.h @@ -24,8 +24,7 @@ #include <glib-object.h> #include <gtk/gtk.h> #include <gdk/gdk.h> - -#include "2geom/rect.h" +#include <2geom/rect.h> G_BEGIN_DECLS @@ -65,6 +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); }; 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 23c6a430e..29729ef6c 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -691,6 +691,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 SPCanvasItemClass *group_parent_class; @@ -734,6 +735,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; } /** @@ -877,6 +879,20 @@ 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) +{ + 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); + } + } +} + /** * Adds an item to a canvas group. */ @@ -1218,8 +1234,16 @@ sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { SPCanvas *canvas = SP_CANVAS (widget); + Geom::IntRect old_area = Geom::IntRect::from_xywh(canvas->x0, canvas->y0, + widget->allocation.width, widget->allocation.height); + Geom::IntRect new_area = Geom::IntRect::from_xywh(canvas->x0, canvas->y0, + allocation->width, allocation->height); + /* 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 (allocation->width > widget->allocation.width) { sp_canvas_request_redraw (canvas, canvas->x0 + widget->allocation.width, @@ -2152,12 +2176,17 @@ sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the int dy = iy - canvas->y0; // canvas w.r.t its previous position + Geom::IntRect old_area = canvas->getViewboxIntegers(); + Geom::IntRect new_area = old_area + Geom::IntPoint(dx, dy); + canvas->dx0 = cx; // here the 'd' stands for double, not delta! canvas->dy0 = cy; canvas->x0 = ix; 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 (!clear) { // scrolling without zoom; redraw only the newly exposed areas @@ -2170,7 +2199,6 @@ sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, } else { // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw } - } /** |
