diff options
| author | Krzysztof Kosi??ski <tweenk.pl@gmail.com> | 2011-07-14 19:42:57 +0000 |
|---|---|---|
| committer | Krzysztof KosiĆski <tweenk.pl@gmail.com> | 2011-07-14 19:42:57 +0000 |
| commit | efbf9755460d4c4b7a3d9d43dd753afcc8a28865 (patch) | |
| tree | 41a259211da187e29f9983821b4cdfea221b6ad6 /src/display | |
| parent | Fix crashes in print preview (diff) | |
| parent | Make cms_key in SPDesktopWidget a regular ustring rather than a pointer (diff) | |
| download | inkscape-efbf9755460d4c4b7a3d9d43dd753afcc8a28865.tar.gz inkscape-efbf9755460d4c4b7a3d9d43dd753afcc8a28865.zip | |
Merge SPCanvasArena caching layer work
(bzr r10451)
Diffstat (limited to 'src/display')
25 files changed, 444 insertions, 448 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/canvas-axonomgrid.cpp b/src/display/canvas-axonomgrid.cpp index dbf7b424d..ec2d35f69 100644 --- a/src/display/canvas-axonomgrid.cpp +++ b/src/display/canvas-axonomgrid.cpp @@ -33,6 +33,7 @@ #include "svg/svg-color.h" #include "util/mathfns.h" #include "xml/node-event-vector.h" +#include "round.h" #define SAFE_SETPIXEL //undefine this when it is certain that setpixel is never called with invalid params @@ -549,13 +550,13 @@ CanvasAxonomGrid::Render (SPCanvasBuf *buf) // x-axis always goes from topleft to bottomright. (0,0) - (1,1) gdouble const xintercept_y_bc = (buf_tl_gc[Geom::X] * tan_angle[X]) - buf_tl_gc[Geom::Y] ; gdouble const xstart_y_sc = ( xintercept_y_bc - floor(xintercept_y_bc/lyw)*lyw ) + buf->rect.y0; - gint const xlinestart = (gint) Inkscape::round( (xstart_y_sc - buf->rect.x0*tan_angle[X] -ow[Geom::Y]) / lyw ); + gint const xlinestart = round( (xstart_y_sc - buf->rect.x0*tan_angle[X] -ow[Geom::Y]) / lyw ); gint xlinenum = xlinestart; // lines starting on left side. for (y = xstart_y_sc; y < buf->rect.y1; y += lyw, xlinenum++) { gint const x0 = buf->rect.x0; - gint const y0 = (gint) Inkscape::round(y); - gint const x1 = x0 + (gint) Inkscape::round( (buf->rect.y1 - y) / tan_angle[X] ); + gint const y0 = round(y); + gint const x1 = x0 + round( (buf->rect.y1 - y) / tan_angle[X] ); gint const y1 = buf->rect.y1; if (!scaled && (xlinenum % empspacing) != 0) { @@ -570,8 +571,8 @@ CanvasAxonomGrid::Render (SPCanvasBuf *buf) for (x = xstart_x_sc; x < buf->rect.x1; x += lxw_x, xlinenum--) { gint const y0 = buf->rect.y0; gint const y1 = buf->rect.y1; - gint const x0 = (gint) Inkscape::round(x); - gint const x1 = x0 + (gint) Inkscape::round( (y1 - y0) / tan_angle[X] ); + gint const x0 = round(x); + gint const x1 = x0 + round( (y1 - y0) / tan_angle[X] ); if (!scaled && (xlinenum % empspacing) != 0) { sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, color); @@ -582,10 +583,10 @@ CanvasAxonomGrid::Render (SPCanvasBuf *buf) // y-axis lines (vertical) gdouble const ystart_x_sc = floor (buf_tl_gc[Geom::X] / spacing_ylines) * spacing_ylines + ow[Geom::X]; - gint const ylinestart = (gint) Inkscape::round((ystart_x_sc - ow[Geom::X]) / spacing_ylines); + gint const ylinestart = round((ystart_x_sc - ow[Geom::X]) / spacing_ylines); gint ylinenum = ylinestart; for (x = ystart_x_sc; x < buf->rect.x1; x += spacing_ylines, ylinenum++) { - gint const x0 = (gint) Inkscape::round(x); + gint const x0 = round(x); if (!scaled && (ylinenum % empspacing) != 0) { sp_grid_vline (buf, x0, buf->rect.y0, buf->rect.y1 - 1, color); @@ -597,13 +598,13 @@ CanvasAxonomGrid::Render (SPCanvasBuf *buf) // z-axis always goes from bottomleft to topright. (0,1) - (1,0) gdouble const zintercept_y_bc = (buf_tl_gc[Geom::X] * -tan_angle[Z]) - buf_tl_gc[Geom::Y] ; gdouble const zstart_y_sc = ( zintercept_y_bc - floor(zintercept_y_bc/lyw)*lyw ) + buf->rect.y0; - gint const zlinestart = (gint) Inkscape::round( (zstart_y_sc + buf->rect.x0*tan_angle[Z] - ow[Geom::Y]) / lyw ); + gint const zlinestart = round( (zstart_y_sc + buf->rect.x0*tan_angle[Z] - ow[Geom::Y]) / lyw ); gint zlinenum = zlinestart; // lines starting from left side for (y = zstart_y_sc; y < buf->rect.y1; y += lyw, zlinenum++) { gint const x0 = buf->rect.x0; - gint const y0 = (gint) Inkscape::round(y); - gint const x1 = x0 + (gint) Inkscape::round( (y - buf->rect.y0 ) / tan_angle[Z] ); + gint const y0 = round(y); + gint const x1 = x0 + round( (y - buf->rect.y0 ) / tan_angle[Z] ); gint const y1 = buf->rect.y0; if (!scaled && (zlinenum % empspacing) != 0) { @@ -617,8 +618,8 @@ CanvasAxonomGrid::Render (SPCanvasBuf *buf) for (x = zstart_x_sc; x < buf->rect.x1; x += lxw_z, zlinenum++) { gint const y0 = buf->rect.y1; gint const y1 = buf->rect.y0; - gint const x0 = (gint) Inkscape::round(x); - gint const x1 = x0 + (gint) Inkscape::round( (buf->rect.y1 - buf->rect.y0) / tan_angle[Z] ); + gint const x0 = round(x); + gint const x1 = x0 + round( (buf->rect.y1 - buf->rect.y0) / tan_angle[Z] ); if (!scaled && (zlinenum % empspacing) != 0) { sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, color); diff --git a/src/display/canvas-grid.cpp b/src/display/canvas-grid.cpp index e1673c8ef..aa38a14c9 100644 --- a/src/display/canvas-grid.cpp +++ b/src/display/canvas-grid.cpp @@ -920,9 +920,9 @@ void CanvasXYGrid::Render (SPCanvasBuf *buf) { gdouble const sxg = floor ((buf->rect.x0 - ow[Geom::X]) / sw[Geom::X]) * sw[Geom::X] + ow[Geom::X]; - gint const xlinestart = (gint) Inkscape::round((sxg - ow[Geom::X]) / sw[Geom::X]); + gint const xlinestart = round((sxg - ow[Geom::X]) / sw[Geom::X]); gdouble const syg = floor ((buf->rect.y0 - ow[Geom::Y]) / sw[Geom::Y]) * sw[Geom::Y] + ow[Geom::Y]; - gint const ylinestart = (gint) Inkscape::round((syg - ow[Geom::Y]) / sw[Geom::Y]); + gint const ylinestart = round((syg - ow[Geom::Y]) / sw[Geom::Y]); //set correct coloring, depending preference (when zoomed out, always major coloring or minor coloring) Inkscape::Preferences *prefs = Inkscape::Preferences::get(); @@ -943,7 +943,7 @@ CanvasXYGrid::Render (SPCanvasBuf *buf) gint ylinenum; gdouble y; for (y = syg, ylinenum = ylinestart; y < buf->rect.y1; y += sw[Geom::Y], ylinenum++) { - gint const y0 = (gint) Inkscape::round(y); + gint const y0 = round(y); if (!scaled[Geom::Y] && (ylinenum % empspacing) != 0) { grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, color); } else { @@ -954,7 +954,7 @@ CanvasXYGrid::Render (SPCanvasBuf *buf) gint xlinenum; gdouble x; for (x = sxg, xlinenum = xlinestart; x < buf->rect.x1; x += sw[Geom::X], xlinenum++) { - gint const ix = (gint) Inkscape::round(x); + gint const ix = round(x); if (!scaled[Geom::X] && (xlinenum % empspacing) != 0) { grid_vline (buf, ix, buf->rect.y0, buf->rect.y1, color); } else { @@ -965,12 +965,12 @@ CanvasXYGrid::Render (SPCanvasBuf *buf) gint ylinenum; gdouble y; for (y = syg, ylinenum = ylinestart; y < buf->rect.y1; y += sw[Geom::Y], ylinenum++) { - gint const iy = (gint) Inkscape::round(y); + gint const iy = round(y); gint xlinenum; gdouble x; for (x = sxg, xlinenum = xlinestart; x < buf->rect.x1; x += sw[Geom::X], xlinenum++) { - gint const ix = (gint) Inkscape::round(x); + gint const ix = round(x); if ( (!scaled[Geom::X] && (xlinenum % empspacing) != 0) || (!scaled[Geom::Y] && (ylinenum % empspacing) != 0) || ((scaled[Geom::X] || scaled[Geom::Y]) && no_emp_when_zoomed_out) ) diff --git a/src/display/guideline.cpp b/src/display/guideline.cpp index c761fa74e..c1c3e7740 100644 --- a/src/display/guideline.cpp +++ b/src/display/guideline.cpp @@ -112,8 +112,8 @@ static void sp_guideline_render(SPCanvasItem *item, SPCanvasBuf *buf) cairo_set_line_cap(buf->ct, CAIRO_LINE_CAP_SQUARE); cairo_set_font_size(buf->ct, 10); - int px = (int) Inkscape::round(gl->point_on_line[Geom::X]); - int py = (int) Inkscape::round(gl->point_on_line[Geom::Y]); + int px = round(gl->point_on_line[Geom::X]); + int py = round(gl->point_on_line[Geom::Y]); if (gl->label) { cairo_save(buf->ct); @@ -126,12 +126,12 @@ static void sp_guideline_render(SPCanvasItem *item, SPCanvasBuf *buf) } if (gl->is_vertical()) { - int position = (int) Inkscape::round(gl->point_on_line[Geom::X]); + int position = round(gl->point_on_line[Geom::X]); cairo_move_to(buf->ct, position + 0.5, buf->rect.y0 + 0.5); cairo_line_to(buf->ct, position + 0.5, buf->rect.y1 - 0.5); cairo_stroke(buf->ct); } else if (gl->is_horizontal()) { - int position = (int) Inkscape::round(gl->point_on_line[Geom::Y]); + int position = round(gl->point_on_line[Geom::Y]); cairo_move_to(buf->ct, buf->rect.x0 + 0.5, position + 0.5); cairo_line_to(buf->ct, buf->rect.x1 - 0.5, position + 0.5); cairo_stroke(buf->ct); @@ -193,9 +193,9 @@ static void sp_guideline_update(SPCanvasItem *item, Geom::Affine const &affine, sp_canvas_item_request_update(SP_CANVAS_ITEM (gl->origin)); if (gl->is_horizontal()) { - sp_canvas_update_bbox (item, -1000000, (int) Inkscape::round(gl->point_on_line[Geom::Y] - 16), 1000000, (int) Inkscape::round(gl->point_on_line[Geom::Y] + 1)); + sp_canvas_update_bbox (item, -1000000, round(gl->point_on_line[Geom::Y] - 16), 1000000, round(gl->point_on_line[Geom::Y] + 1)); } else if (gl->is_vertical()) { - sp_canvas_update_bbox (item, (int) Inkscape::round(gl->point_on_line[Geom::X]), -1000000, (int) Inkscape::round(gl->point_on_line[Geom::X] + 16), 1000000); + sp_canvas_update_bbox (item, round(gl->point_on_line[Geom::X]), -1000000, round(gl->point_on_line[Geom::X] + 16), 1000000); } else { //TODO: labels in angled guidelines are not showing up for some reason. sp_canvas_update_bbox (item, -1000000, -1000000, 1000000, 1000000); diff --git a/src/display/nr-arena-glyphs.cpp b/src/display/nr-arena-glyphs.cpp index 089d6de40..d09f66a2f 100644 --- a/src/display/nr-arena-glyphs.cpp +++ b/src/display/nr-arena-glyphs.cpp @@ -135,10 +135,10 @@ nr_arena_glyphs_update(NRArenaItem *item, NRRectL */*area*/, NRGC *gc, guint /*s } if (b) { - item->bbox.x0 = static_cast<NR::ICoord>(floor(b->left())); - item->bbox.y0 = static_cast<NR::ICoord>(floor(b->top())); - item->bbox.x1 = static_cast<NR::ICoord>(ceil (b->right())); - item->bbox.y1 = static_cast<NR::ICoord>(ceil (b->bottom())); + item->bbox.x0 = floor(b->left()); + item->bbox.y0 = floor(b->top()); + item->bbox.x1 = ceil (b->right()); + item->bbox.y1 = ceil (b->bottom()); } else { item->bbox.x0 = 0; item->bbox.y0 = 0; @@ -282,7 +282,8 @@ nr_arena_glyphs_group_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint s } -static unsigned int nr_arena_glyphs_group_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock * /*pb*/, unsigned int /*flags*/) +static unsigned int +nr_arena_glyphs_group_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock * /*pb*/, unsigned int /*flags*/) { NRArenaItem *child = 0; @@ -352,20 +353,34 @@ static unsigned int nr_arena_glyphs_group_render(cairo_t *ct, NRArenaItem *item, return item->state; } -static unsigned int nr_arena_glyphs_group_clip(cairo_t * /*ct*/, NRArenaItem *item, NRRectL * /*area*/) +static unsigned int nr_arena_glyphs_group_clip(cairo_t *ct, NRArenaItem *item, NRRectL * /*area*/) { - //NRArenaGroup *group = NR_ARENA_GROUP(item); + NRArenaGroup *ggroup = NR_ARENA_GLYPHS_GROUP(item); - guint ret = item->state; + cairo_save(ct); + // handle clip-rule + if (ggroup->style) { + if (ggroup->style->clip_rule.computed == SP_WIND_RULE_EVENODD) { + cairo_set_fill_rule(ct, CAIRO_FILL_RULE_EVEN_ODD); + } else { + cairo_set_fill_rule(ct, CAIRO_FILL_RULE_WINDING); + } + } + ink_cairo_transform(ct, ggroup->ctm); + + for (NRArenaItem *child = ggroup->children; child != NULL; child = child->next) { + NRArenaGlyphs *g = NR_ARENA_GLYPHS(child); + Geom::PathVector const &pathv = *g->font->PathVector(g->glyph); - // Render children fill mask - /* - for (NRArenaItem *child = group->children; child != NULL; child = child->next) { - ret = nr_arena_glyphs_fill_mask(NR_ARENA_GLYPHS(child), area, pb); - if (!(ret & NR_ARENA_ITEM_STATE_RENDER)) return ret; - }*/ + cairo_save(ct); + ink_cairo_transform(ct, g->g_transform); + feed_pathvector_to_cairo(ct, pathv); + cairo_restore(ct); + } + cairo_fill(ct); + cairo_restore(ct); - return ret; + return item->state; } static NRArenaItem * diff --git a/src/display/nr-arena-group.cpp b/src/display/nr-arena-group.cpp index 97f92d02d..1d552fbc2 100644 --- a/src/display/nr-arena-group.cpp +++ b/src/display/nr-arena-group.cpp @@ -12,9 +12,12 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include "display/canvas-bpath.h" +#include "display/nr-arena.h" #include "display/nr-arena-group.h" #include "display/nr-filter.h" #include "display/nr-filter-types.h" +#include "display/rendermode.h" #include "style.h" #include "sp-filter.h" #include "sp-filter-reference.h" @@ -163,10 +166,9 @@ static unsigned int nr_arena_group_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int state, unsigned int reset) { unsigned int newstate; - NRArenaGroup *group = NR_ARENA_GROUP (item); - unsigned int beststate = NR_ARENA_ITEM_STATE_ALL; + bool outline = (item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE); for (NRArenaItem *child = group->children; child != NULL; child = child->next) { NRGC cgc(gc); @@ -176,10 +178,10 @@ nr_arena_group_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int } if (beststate & NR_ARENA_ITEM_STATE_BBOX) { - nr_rect_l_set_empty (&item->bbox); + item->bbox = NR_RECT_L_EMPTY; for (NRArenaItem *child = group->children; child != NULL; child = child->next) { if (child->visible) - nr_rect_l_union (&item->bbox, &item->bbox, &child->drawbox); + nr_rect_l_union (&item->bbox, &item->bbox, outline ? &child->bbox : &child->drawbox); } } @@ -234,10 +236,8 @@ static unsigned int nr_arena_group_clip (cairo_t *ct, NRArenaItem *item, NRRectL *area) { NRArenaGroup *group = NR_ARENA_GROUP (item); - unsigned int ret = item->state; - /* Just compose children into parent buffer */ for (NRArenaItem *child = group->children; child != NULL; child = child->next) { ret = nr_arena_item_invoke_clip (ct, child, area); if (ret & NR_ARENA_ITEM_STATE_INVALID) break; diff --git a/src/display/nr-arena-image.cpp b/src/display/nr-arena-image.cpp index 36d733eb8..a943a6214 100644 --- a/src/display/nr-arena-image.cpp +++ b/src/display/nr-arena-image.cpp @@ -301,7 +301,7 @@ nr_arena_image_rect (NRArenaImage *image) Geom::Point p(image->ox, image->oy); Geom::Point wh(vw, vh); Geom::Rect view(p, p+wh); - Geom::OptRect res = Geom::intersect(r, view); + Geom::OptRect res = r & view; r = res ? *res : r; } diff --git a/src/display/nr-arena-item.cpp b/src/display/nr-arena-item.cpp index 526882921..9ca5a7463 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" @@ -214,6 +215,7 @@ nr_arena_item_invoke_update (NRArenaItem *item, NRRectL *area, NRGC *gc, { NRGC childgc (gc); bool filter = (item->arena->rendermode == Inkscape::RENDERMODE_NORMAL); + bool outline = (item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE); nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID); nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), @@ -242,7 +244,7 @@ nr_arena_item_invoke_update (NRArenaItem *item, NRRectL *area, NRGC *gc, return item->state; /* Test whether to return immediately */ if (area && (item->state & NR_ARENA_ITEM_STATE_BBOX)) { - if (!nr_rect_l_test_intersect_ptr(area, &item->drawbox)) + if (!nr_rect_l_test_intersect_ptr(area, outline ? &item->bbox : &item->drawbox)) return item->state; } @@ -275,8 +277,6 @@ nr_arena_item_invoke_update (NRArenaItem *item, NRRectL *area, NRGC *gc, } else { memcpy(&item->drawbox, &item->bbox, sizeof(item->bbox)); } - // fixme: to fix the display glitches, in outline mode bbox must be a combination of - // full item bbox and its clip and mask (after we have the API to get these) /* Clipping */ if (item->clip) { @@ -288,8 +288,12 @@ nr_arena_item_invoke_update (NRArenaItem *item, NRRectL *area, NRGC *gc, item->state |= NR_ARENA_ITEM_STATE_INVALID; return item->state; } - // for clipping, we need geometric bbox - nr_rect_l_intersect (&item->drawbox, &item->drawbox, &item->clip->bbox); + if (outline) { + nr_rect_l_union(&item->bbox, &item->bbox, &item->clip->bbox); + } else { + // for clipping, we need geometric bbox + nr_rect_l_intersect (&item->drawbox, &item->drawbox, &item->clip->bbox); + } } /* Masking */ if (item->mask) { @@ -298,8 +302,12 @@ nr_arena_item_invoke_update (NRArenaItem *item, NRRectL *area, NRGC *gc, item->state |= NR_ARENA_ITEM_STATE_INVALID; return item->state; } - // for masking, we need full drawbox of mask - nr_rect_l_intersect (&item->drawbox, &item->drawbox, &item->mask->drawbox); + if (outline) { + nr_rect_l_union(&item->bbox, &item->bbox, &item->mask->bbox); + } else { + // for masking, we need full drawbox of mask + nr_rect_l_intersect (&item->drawbox, &item->drawbox, &item->mask->drawbox); + } } // now that we know drawbox, dirty the corresponding rect on canvas: @@ -349,18 +357,14 @@ nr_arena_item_invoke_render (cairo_t *ct, NRArenaItem *item, NRRectL const *area if (!item->visible) 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. - NRRectL carea; - nr_rect_l_intersect (&carea, area, &item->drawbox); - if (nr_rect_l_test_empty(carea)) - return item->state | NR_ARENA_ITEM_STATE_RENDER; - if (item->filter && filter) { - item->filter->area_enlarge (carea, item); - nr_rect_l_intersect (&carea, &carea, &item->drawbox); - } - if (outline) { + // intersect with bbox rather than drawbox, as we want to render things outside + // of the clipping path as well + NRRectL carea; + nr_rect_l_intersect (&carea, area, &item->bbox); + if (nr_rect_l_test_empty(carea)) + return item->state | NR_ARENA_ITEM_STATE_RENDER; + // No caching in outline mode for now; investigate if it really gives any advantage with cairo. // Also no attempts to clip anything; just render everything: item, clip, mask // First, render the object itself @@ -388,121 +392,143 @@ nr_arena_item_invoke_render (cairo_t *ct, NRArenaItem *item, NRRectL const *area 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. + NRRectL carea; + nr_rect_l_intersect (&carea, area, &item->drawbox); + if (nr_rect_l_test_empty(carea)) + return item->state | NR_ARENA_ITEM_STATE_RENDER; + if (item->filter && filter) { + item->filter->area_enlarge (carea, item); + nr_rect_l_intersect (&carea, &carea, &item->drawbox); + } using namespace Inkscape; - // clipping and masks - unsigned int state; - - cairo_t *this_ct = ct; - NRRectL *this_area = const_cast<NRRectL*>(area); + unsigned state; + unsigned retstate; + // determine whether this shape needs intermediate rendering. bool needs_intermediate_rendering = false; bool &nir = needs_intermediate_rendering; bool needs_opacity = (item->opacity != 255 && !item->render_opacity); // this item needs an intermediate rendering if: - nir |= (item->mask != NULL); // 1. it has a mask - nir |= (item->filter != NULL && filter); // 2. it has a filter - nir |= needs_opacity; // 3. it is non-opaque + nir |= (item->clip != NULL); // 1. it has a clipping path + nir |= (item->mask != NULL); // 2. it has a mask + nir |= (item->filter != NULL && filter); // 3. it has a filter + nir |= needs_opacity; // 4. it is non-opaque double opacity = static_cast<double>(item->opacity) / 255.0; - if (needs_intermediate_rendering) { - cairo_surface_t *intermediate = cairo_surface_create_similar( - cairo_get_target(ct), CAIRO_CONTENT_COLOR_ALPHA, - carea.x1 - carea.x0, carea.y1 - carea.y0); - this_ct = cairo_create(intermediate); - cairo_translate(this_ct, -carea.x0, -carea.y0); - this_area = &carea; - cairo_surface_destroy(intermediate); // the surface will be held in memory by this_ct - } else { - cairo_reference(this_ct); - } + /* How the rendering is done. + * + * Clipping, masking and opacity are done by rendering them to a surface + * and then compositing the object's rendering onto it with the IN operator. + * The object itself is rendered to a group. + * + * Opacity is done by rendering the clipping path with an alpha + * value corresponding to the opacity. If there is no clipping path, + * the entire intermediate surface is painted with alpha corresponding + * to the opacity value. + */ - // The pipeline needs to be different for filters. - // First we render the item into an intermediate surface. Then the filter rotates - // the surface to user coordinates (if necessary) and runs the rendering. - // Once that's done we retrieve the result, rotating it back to screen coords. - // Clipping and masking happens after the filter result is ready. - if (item->filter && filter) { + // short-circuit the simple case. + if (!needs_intermediate_rendering) { + state = NR_ARENA_ITEM_VIRTUAL (item, render) (ct, item, &carea, pb, 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; } - Cairo::Context cct(this_ct, true); - Cairo::Context base_ct(ct); - Cairo::RefPtr<Cairo::Pattern> mask; - CairoSave clipsave(ct); // RAII for save / restore - CairoGroup maskgroup(this_ct); // RAII for push_group / pop_group - CairoGroup drawgroup(this_ct); - CairoGroup maskopacitygroup(this_ct); - - // always clip the base context, not the one on the intermediate surface - // this is because filters must be done before clipping + cairo_surface_t *intermediate = cairo_surface_create_similar( + cairo_get_group_target(ct), CAIRO_CONTENT_COLOR_ALPHA, + carea.x1 - carea.x0, carea.y1 - carea.y0); + cairo_t *ict = cairo_create(intermediate); + cairo_translate(ict, -carea.x0, -carea.y0); + + // 1. Render clipping path with alpha = opacity. + cairo_set_source_rgba(ict, 0,0,0,opacity); + // Since clip can be combined with opacity, the result could be incorrect + // for overlapping clip children. To fix this we use the SOURCE operator + // instead of the default OVER. + cairo_set_operator(ict, CAIRO_OPERATOR_SOURCE); if (item->clip) { - clipsave.save(); - state = nr_arena_item_invoke_clip(ct, item->clip, const_cast<NRRectL*>(area)); + state = nr_arena_item_invoke_clip(ict, item->clip, const_cast<NRRectL*>(area)); if (state & NR_ARENA_ITEM_STATE_INVALID) { - item->state |= NR_ARENA_ITEM_STATE_INVALID; - return item->state; + retstate = (item->state |= NR_ARENA_ITEM_STATE_INVALID); + goto cleanup; } - base_ct.clip(); + } else { + // if there is no clipping path, fill the entire surface with alpha = opacity. + cairo_paint(ict); } + // reset back to default + cairo_set_operator(ict, CAIRO_OPERATOR_OVER); - // render mask on the intermediate context and store it + // 2. Render the mask if present and compose it with the clipping path + opacity. if (item->mask) { - maskgroup.push_with_content(CAIRO_CONTENT_COLOR_ALPHA); - // handle opacity of a masked object by composing it with the mask - if (needs_opacity) { - maskopacitygroup.push(); - } - state = NR_ARENA_ITEM_VIRTUAL (item->mask, render) (this_ct, item->mask, this_area, pb, flags); + cairo_push_group(ict); + state = NR_ARENA_ITEM_VIRTUAL (item->mask, render) (ict, item->mask, &carea, NULL, flags); if (state & NR_ARENA_ITEM_STATE_INVALID) { - item->state |= NR_ARENA_ITEM_STATE_INVALID; - return item->state; - } - if (needs_opacity) { - maskopacitygroup.pop_to_source(); - cct.paint_with_alpha(opacity); + retstate = (item->state |= NR_ARENA_ITEM_STATE_INVALID); + goto cleanup; } - mask = maskgroup.popmm(); - // convert luminance to alpha - cairo_pattern_t *p = mask->cobj(); - cairo_surface_t *s; - cairo_pattern_get_surface(p, &s); - ink_cairo_surface_filter(s, s, MaskLuminanceToAlpha()); + cairo_surface_t *mask_s = cairo_get_group_target(ict); + // Convert mask's luminance to alpha + ink_cairo_surface_filter(mask_s, mask_s, MaskLuminanceToAlpha()); + cairo_pop_group_to_source(ict); + cairo_set_operator(ict, CAIRO_OPERATOR_IN); + cairo_paint(ict); + cairo_set_operator(ict, CAIRO_OPERATOR_OVER); } - // render the object (possibly to the intermediate surface) - state = NR_ARENA_ITEM_VIRTUAL (item, render) (this_ct, item, this_area, pb, flags); + // 3. Render object itself. + cairo_push_group(ict); + state = NR_ARENA_ITEM_VIRTUAL (item, render) (ict, item, &carea, pb, flags); if (state & NR_ARENA_ITEM_STATE_INVALID) { - /* Clean up and return error */ - item->state |= NR_ARENA_ITEM_STATE_INVALID; - return item->state; + retstate = (item->state |= NR_ARENA_ITEM_STATE_INVALID); + goto cleanup; } - // apply filter + // 4. Apply filter. if (item->filter && filter) { - item->filter->render(item, ct, area, this_ct, &carea); - } - - if (needs_intermediate_rendering) { - cairo_surface_t *intermediate = cairo_get_target(this_ct); - cairo_set_source_surface(ct, intermediate, carea.x0, carea.y0); - if (mask) { - cairo_mask(ct, mask->cobj()); - // opacity of masked objects is handled by premultiplying the mask + // HACK: SPCanvasArena doesn't exist when this is called for offscreen rendering + // Proper fix: call this function with a drawing context class + // that contains information about the surface's bounds + NRRectL bgarea; + if (flags & NR_ARENA_ITEM_RENDER_NO_CACHE || !item->arena->canvasarena) { + bgarea = carea; } else { - // opacity of non-masked objects must be rendered explicitly - if (needs_opacity) { - cairo_paint_with_alpha(ct, opacity); - } else { - cairo_paint(ct); - } + bgarea = NRRectL(item->arena->canvasarena->cache_area); } - cairo_set_source_rgba(ct,0,0,0,0); + item->filter->render(item, ct, &bgarea, ict, &carea); + // Note that because the object was rendered to a group, + // the internals of the filter need to use cairo_get_group_target() + // instead of cairo_get_target(). } - return item->state | NR_ARENA_ITEM_STATE_RENDER; + // 5. Render object inside the composited mask + clip + cairo_pop_group_to_source(ict); + cairo_set_operator(ict, CAIRO_OPERATOR_IN); + cairo_paint(ict); + + // 6. Paint the completed rendering onto the base context + cairo_set_source_surface(ct, intermediate, carea.x0, carea.y0); + cairo_paint(ct); + cairo_set_source_rgba(ct, 0,0,0,0); + // the call above is to clear a ref on the intermediate surface held by ct + + retstate = item->state | NR_ARENA_ITEM_STATE_RENDER; + + cleanup: + cairo_destroy(ict); + cairo_surface_destroy(intermediate); + + return retstate; } unsigned int @@ -511,16 +537,6 @@ nr_arena_item_invoke_clip (cairo_t *ct, NRArenaItem *item, NRRectL *area) 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 clip by %p: %d %d - %d %d, item bbox %d %d - %d %d\n", @@ -528,15 +544,40 @@ nr_arena_item_invoke_clip (cairo_t *ct, NRArenaItem *item, NRRectL *area) (&item->bbox)->y0, (&item->bbox)->x1, (&item->bbox)->y1); #endif + unsigned retstate = 0; + + // don't bother if the object does not implement clipping (e.g. NRArenaImage) + if (!((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->clip) + return retstate; + if (item->visible && nr_rect_l_test_intersect_ptr(area, &item->bbox)) { - /* Need render that item */ - if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->clip) { - return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))-> - clip (ct, item, area); + // The item used as the clipping path itself has a clipping path. + // Render this item's clipping path onto a temporary surface, then composite it with the item + // using the IN operator + if (item->clip) { + cairo_push_group_with_content(ct, CAIRO_CONTENT_ALPHA); + cairo_save(ct); + cairo_set_source_rgba(ct, 0,0,0,1); + nr_arena_item_invoke_clip(ct, item->clip, area); + cairo_restore(ct); + cairo_push_group_with_content(ct, CAIRO_CONTENT_ALPHA); + } + + // rasterize the clipping path + retstate = ((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))-> + clip (ct, item, area); + + if (item->clip) { + cairo_pop_group_to_source(ct); + cairo_set_operator(ct, CAIRO_OPERATOR_IN); + cairo_paint(ct); + cairo_pop_group_to_source(ct); + cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE); + cairo_paint(ct); } } - return item->state; + return retstate; } NRArenaItem * @@ -597,7 +638,8 @@ 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->drawbox); + bool outline = (item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE); + nr_arena_request_render_rect (item->arena, outline ? &item->bbox : &item->drawbox); } /* Public */ diff --git a/src/display/nr-arena-item.h b/src/display/nr-arena-item.h index 0fc4cbe48..d65a75ed8 100644 --- a/src/display/nr-arena-item.h +++ b/src/display/nr-arena-item.h @@ -15,6 +15,8 @@ #include <cairo.h> #include <2geom/affine.h> +#include <2geom/rect.h> +#include "libnr/nr-forward.h" #include "libnr/nr-rect-l.h" #include "libnr/nr-object.h" #include "gc-soft-ptr.h" diff --git a/src/display/nr-arena-shape.cpp b/src/display/nr-arena-shape.cpp index eb7a30e58..6d65611bf 100644 --- a/src/display/nr-arena-shape.cpp +++ b/src/display/nr-arena-shape.cpp @@ -21,6 +21,7 @@ #include <2geom/svg-path-parser.h> #include "display/cairo-utils.h" #include "display/canvas-arena.h" +#include "display/canvas-bpath.h" #include "display/curve.h" #include "display/nr-arena.h" #include "display/nr-arena-shape.h" @@ -223,10 +224,10 @@ nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, g if (shape->curve) { boundingbox = bounds_exact_transformed(shape->curve->get_pathvector(), gc->transform); if (boundingbox) { - item->bbox.x0 = static_cast<NR::ICoord>(floor((*boundingbox)[0][0])); // Floor gives the coordinate in which the point resides - item->bbox.y0 = static_cast<NR::ICoord>(floor((*boundingbox)[1][0])); - item->bbox.x1 = static_cast<NR::ICoord>(ceil ((*boundingbox)[0][1])); // Ceil gives the first coordinate beyond the point - item->bbox.y1 = static_cast<NR::ICoord>(ceil ((*boundingbox)[1][1])); + item->bbox.x0 = floor((*boundingbox)[0][0]); // Floor gives the coordinate in which the point resides + item->bbox.y0 = floor((*boundingbox)[1][0]); + item->bbox.x1 = ceil ((*boundingbox)[0][1]); // Ceil gives the first coordinate beyond the point + item->bbox.y1 = ceil ((*boundingbox)[1][1]); } else { item->bbox = NR_RECT_L_EMPTY; } @@ -274,10 +275,10 @@ nr_arena_shape_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, g /// \todo just write item->bbox = boundingbox if (boundingbox) { - shape->approx_bbox.x0 = static_cast<NR::ICoord>(floor((*boundingbox)[0][0])); - shape->approx_bbox.y0 = static_cast<NR::ICoord>(floor((*boundingbox)[1][0])); - shape->approx_bbox.x1 = static_cast<NR::ICoord>(ceil ((*boundingbox)[0][1])); - shape->approx_bbox.y1 = static_cast<NR::ICoord>(ceil ((*boundingbox)[1][1])); + shape->approx_bbox.x0 = floor(boundingbox->left()); + shape->approx_bbox.y0 = floor(boundingbox->top()); + shape->approx_bbox.x1 = ceil (boundingbox->right()); + shape->approx_bbox.y1 = ceil (boundingbox->bottom()); } else { shape->approx_bbox = NR_RECT_L_EMPTY; } @@ -396,22 +397,26 @@ nr_arena_shape_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock static guint nr_arena_shape_clip(cairo_t *ct, NRArenaItem *item, NRRectL * /*area*/) { - guint result = 0; - - // NOTE: for now this is incorrect, because it doesn't honor clip-rule, - // and will be incorrect for nested clipping paths. NRArenaShape *shape = NR_ARENA_SHAPE(item); if (!shape->curve) { - result = item->state; - } else { - cairo_save(ct); - ink_cairo_transform(ct, shape->ctm); - feed_pathvector_to_cairo(ct, shape->curve->get_pathvector()); - cairo_restore(ct); + return item->state; + } - result = item->state; + cairo_save(ct); + // handle clip-rule + if (shape->style) { + if (shape->style->clip_rule.computed == SP_WIND_RULE_EVENODD) { + cairo_set_fill_rule(ct, CAIRO_FILL_RULE_EVEN_ODD); + } else { + cairo_set_fill_rule(ct, CAIRO_FILL_RULE_WINDING); + } } - return result; + ink_cairo_transform(ct, shape->ctm); + feed_pathvector_to_cairo(ct, shape->curve->get_pathvector()); + cairo_fill(ct); + cairo_restore(ct); + + return item->state; } static NRArenaItem * diff --git a/src/display/nr-arena-shape.h b/src/display/nr-arena-shape.h index 2ee0d24c8..7b86f7f59 100644 --- a/src/display/nr-arena-shape.h +++ b/src/display/nr-arena-shape.h @@ -22,6 +22,7 @@ #include "forward.h" #include "nr-arena-item.h" #include "nr-style.h" +#include "libnr/nr-rect.h" NRType nr_arena_shape_get_type (void); diff --git a/src/display/nr-arena.cpp b/src/display/nr-arena.cpp index 43edb6918..ce62a81dc 100644 --- a/src/display/nr-arena.cpp +++ b/src/display/nr-arena.cpp @@ -18,6 +18,7 @@ #include "nr-filter-types.h" #include "preferences.h" #include "color.h" +#include "libnr/nr-rect.h" static void nr_arena_class_init (NRArenaClass *klass); static void nr_arena_init (NRArena *arena); diff --git a/src/display/nr-filter-composite.cpp b/src/display/nr-filter-composite.cpp index d4cf47af4..694ccaec5 100644 --- a/src/display/nr-filter-composite.cpp +++ b/src/display/nr-filter-composite.cpp @@ -11,7 +11,6 @@ #include <cmath> -#include "2geom/isnan.h" #include "display/cairo-templates.h" #include "display/cairo-utils.h" #include "display/nr-filter-composite.h" diff --git a/src/display/nr-filter-displacement-map.cpp b/src/display/nr-filter-displacement-map.cpp index fdaf2c887..15200223b 100644 --- a/src/display/nr-filter-displacement-map.cpp +++ b/src/display/nr-filter-displacement-map.cpp @@ -28,128 +28,6 @@ FilterPrimitive * FilterDisplacementMap::create() { FilterDisplacementMap::~FilterDisplacementMap() {} -#if 0 -struct pixel_t { - unsigned char channels[4]; - inline unsigned char operator[](int c) const { return channels[c]; } - inline unsigned char& operator[](int c) { return channels[c]; } - static inline pixel_t blank() { - pixel_t p; - for(unsigned int i=0; i<4; i++) { - p[i] = 0; - } - return p; - } -}; - -static inline pixel_t pixelValue(NRPixBlock const* pb, int x, int y) { - if ( x < pb->area.x0 || x >= pb->area.x1 || y < pb->area.y0 || y >= pb->area.y1 ) return pixel_t::blank(); // This assumes anything outside the defined range is (0,0,0,0) - pixel_t const* rowData = reinterpret_cast<pixel_t const*>(NR_PIXBLOCK_PX(pb) + (y-pb->area.y0)*pb->rs); - return rowData[x-pb->area.x0]; -} - -template<bool PREMULTIPLIED> -static pixel_t interpolatePixels(NRPixBlock const* pb, double x, double y) { - // NOTE: The values of x and y are shifted by -0.5 (the "true" values would be x+0.5 and y+0.5). - // This is done because otherwise the pixel values first have to be shifted by +0.5 and then by -0.5 again... - unsigned int const sfl = 8u; - unsigned int const sf = 1u<<sfl; - unsigned int const sf2h = 1u<<(2u*sfl-1); - int xi = (int)floor(x), yi = (int)floor(y); - unsigned int xf = static_cast<unsigned int>(round(sf * (x - xi))), - yf = static_cast<unsigned int>(round(sf * (y - yi))); - pixel_t p00 = pixelValue(pb, xi+0, yi+0); - pixel_t p01 = pixelValue(pb, xi+1, yi+0); - pixel_t p10 = pixelValue(pb, xi+0, yi+1); - pixel_t p11 = pixelValue(pb, xi+1, yi+1); - - /* It's a good idea to interpolate premultiplied colors: - * - * Consider two pixels, one being rgba(255,0,0,0), which is fully transparent, - * and the other being rgba(0,0,255,255), or blue (fully opaque). - * If these two colors are interpolated the expected result would be bluish pixels - * containing no red. - * - * However, if our final alpha value is zero, then the RGB values aren't really determinate. - * We might as well avoid premultiplication in this case, which still gives us a fully - * transparent result, but with interpolated RGB parts. */ - - pixel_t r; - if (PREMULTIPLIED) { - /* Premultiplied, so do simple interpolation. */ - for (unsigned i = 0; i != 4; ++i) { - // y0,y1 have range [0,a*sf] - unsigned const y0 = sf*p00[i] + xf*((unsigned int)p01[i]-(unsigned int)p00[i]); - unsigned const y1 = sf*p10[i] + xf*((unsigned int)p11[i]-(unsigned int)p10[i]); - - unsigned const ri = sf*y0 + yf*(y1-y0); // range [0,a*sf*sf] - r[i] = (ri + sf2h)>>(2*sfl); // range [0,a] - } - } else { - /* First calculate interpolated alpha value. */ - unsigned const y0 = sf*p00[3] + xf*((unsigned int)p01[3]-(unsigned int)p00[3]); // range [0,a*sf] - unsigned const y1 = sf*p10[3] + xf*((unsigned int)p11[3]-(unsigned int)p10[3]); - unsigned const ra = sf*y0 + yf*(y1-y0); // range [0,a*sf*sf] - - if (ra==0) { - /* Fully transparent, so do simple interpolation. */ - for (unsigned i = 0; i != 3; ++i) { - // y0,y1 have range [0,255*sf] - unsigned const y0 = sf*p00[i] + xf*((unsigned int)p01[i]-(unsigned int)p00[i]); - unsigned const y1 = sf*p10[i] + xf*((unsigned int)p11[i]-(unsigned int)p10[i]); - - unsigned const ri = sf*y0 + yf*(y1-y0); // range [0,255*sf*sf] - r[i] = (ri + sf2h)>>(2*sfl); // range [0,255] - } - r[3] = 0; - } else { - /* Do premultiplication ourselves. */ - for (unsigned i = 0; i != 3; ++i) { - // Premultiplied versions. Range [0,255*a]. - unsigned const c00 = p00[i]*p00[3]; - unsigned const c01 = p01[i]*p01[3]; - unsigned const c10 = p10[i]*p10[3]; - unsigned const c11 = p11[i]*p11[3]; - - // Interpolation. - unsigned const y0 = sf*c00 + xf*(c01-c00); // range [0,255*a*sf] - unsigned const y1 = sf*c10 + xf*(c11-c10); // range [0,255*a*sf] - unsigned const ri = sf*y0 + yf*(y1-y0); // range [0,255*a*sf*sf] - r[i] = (ri + ra/2) / ra; // range [0,255] - } - r[3] = (ra + sf2h)>>(2*sfl); // range [0,a] - } - } - - return r; -} - -template<bool MAP_PREMULTIPLIED, bool DATA_PREMULTIPLIED> -static void performDisplacement(NRPixBlock const* texture, NRPixBlock const* map, int Xchannel, int Ychannel, NRPixBlock* out, double scalex, double scaley) { - bool Xneedsdemul = MAP_PREMULTIPLIED && Xchannel<3; - bool Yneedsdemul = MAP_PREMULTIPLIED && Ychannel<3; - if (!Xneedsdemul) scalex /= 255.0; - if (!Yneedsdemul) scaley /= 255.0; - - for (int yout=out->area.y0; yout < out->area.y1; yout++){ - pixel_t const* mapRowData = reinterpret_cast<pixel_t const*>(NR_PIXBLOCK_PX(map) + (yout-map->area.y0)*map->rs); - pixel_t* outRowData = reinterpret_cast<pixel_t*>(NR_PIXBLOCK_PX(out) + (yout-out->area.y0)*out->rs); - for (int xout=out->area.x0; xout < out->area.x1; xout++){ - pixel_t const mapValue = mapRowData[xout-map->area.x0]; - - double xtex = xout + (Xneedsdemul ? // Although the value of the pixel corresponds to the MIDDLE of the pixel, no +0.5 is needed because we're interpolating pixels anyway (so to get the actual pixel locations 0.5 would have to be subtracted again). - (mapValue[3]==0?0:(scalex * (mapValue[Xchannel] - mapValue[3]*0.5) / mapValue[3])) : - (scalex * (mapValue[Xchannel] - 127.5))); - double ytex = yout + (Yneedsdemul ? - (mapValue[3]==0?0:(scaley * (mapValue[Ychannel] - mapValue[3]*0.5) / mapValue[3])) : - (scaley * (mapValue[Ychannel] - 127.5))); - - outRowData[xout-out->area.x0] = interpolatePixels<DATA_PREMULTIPLIED>(texture, xtex, ytex); - } - } -} -#endif - struct Displace { Displace(cairo_surface_t *texture, cairo_surface_t *map, unsigned xch, unsigned ych, double scalex, double scaley) @@ -206,73 +84,6 @@ void FilterDisplacementMap::render_cairo(FilterSlot &slot) cairo_surface_destroy(out); } -/* -int FilterDisplacementMap::render(FilterSlot &slot, FilterUnits const &units) { - NRPixBlock *texture = slot.get(_input); - NRPixBlock *map = slot.get(_input2); - - // Bail out if either one of source images is missing - if (!map || !texture) { - g_warning("Missing source image for feDisplacementMap (map=%d texture=%d)", _input, _input2); - return 1; - } - - NR::IRect area = units.get_pixblock_filterarea_paraller(); - int x0 = std::max(map->area.x0,area.min()[NR::X]); - int y0 = std::max(map->area.y0,area.min()[NR::Y]); - int x1 = std::min(map->area.x1,area.max()[NR::X]); - int y1 = std::min(map->area.y1,area.max()[NR::Y]); - - //TODO: check whether we really need this check: - if (x1 <= x0 || y1 <= y0) return 0; //nothing to do! - - if (texture->mode != NR_PIXBLOCK_MODE_R8G8B8A8N && texture->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) { - g_warning("Source images without an alpha channel are not supported by feDisplacementMap at the moment."); - return 1; - } - - NRPixBlock *out = new NRPixBlock; - nr_pixblock_setup_fast(out, texture->mode, x0, y0, x1, y1, true); - - // convert to a suitable format - bool free_map_on_exit = false; - if (map->mode != NR_PIXBLOCK_MODE_R8G8B8A8N && map->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) { - NRPixBlock *original_map = map; - map = new NRPixBlock; - nr_pixblock_setup_fast(map, NR_PIXBLOCK_MODE_R8G8B8A8N, - original_map->area.x0, original_map->area.y0, - original_map->area.x1, original_map->area.y1, - false); - nr_blit_pixblock_pixblock(map, original_map); - free_map_on_exit = true; - } - bool map_premultiplied = (map->mode == NR_PIXBLOCK_MODE_R8G8B8A8P); - bool data_premultiplied = (out->mode == NR_PIXBLOCK_MODE_R8G8B8A8P); - - Geom::Affine trans = units.get_matrix_primitiveunits2pb(); - double scalex = scale * trans.expansionX(); - double scaley = scale * trans.expansionY(); - - if (map_premultiplied && data_premultiplied) { - performDisplacement<true,true>(texture, map, Xchannel, Ychannel, out, scalex, scaley); - } else if (map_premultiplied && !data_premultiplied) { - performDisplacement<true,false>(texture, map, Xchannel, Ychannel, out, scalex, scaley); - } else if (data_premultiplied) { - performDisplacement<false,true>(texture, map, Xchannel, Ychannel, out, scalex, scaley); - } else { - performDisplacement<false,false>(texture, map, Xchannel, Ychannel, out, scalex, scaley); - } - - if (free_map_on_exit) { - nr_pixblock_release(map); - delete map; - } - - out->empty = FALSE; - slot.set(_output, out); - return 0; -}*/ - void FilterDisplacementMap::set_input(int slot) { _input = slot; } diff --git a/src/display/nr-filter-gaussian.cpp b/src/display/nr-filter-gaussian.cpp index fca066ad4..3a6b425e1 100644 --- a/src/display/nr-filter-gaussian.cpp +++ b/src/display/nr-filter-gaussian.cpp @@ -23,8 +23,6 @@ #include <omp.h> #endif //HAVE_OPENMP -#include "2geom/isnan.h" - #include "display/cairo-utils.h" #include "display/nr-filter-primitive.h" #include "display/nr-filter-gaussian.h" diff --git a/src/display/nr-filter-slot.cpp b/src/display/nr-filter-slot.cpp index ce07ff086..3464fda66 100644 --- a/src/display/nr-filter-slot.cpp +++ b/src/display/nr-filter-slot.cpp @@ -156,7 +156,7 @@ cairo_surface_t *FilterSlot::_get_transformed_background() { Geom::Affine trans = _units.get_matrix_display2pb(); - cairo_surface_t *bg = cairo_get_target(_background_ct); + cairo_surface_t *bg = cairo_get_group_target(_background_ct); cairo_surface_t *tbg = cairo_surface_create_similar( bg, cairo_surface_get_content(bg), _slot_w, _slot_h); diff --git a/src/display/nr-filter-turbulence.cpp b/src/display/nr-filter-turbulence.cpp index f3b03c024..60d5ce872 100644 --- a/src/display/nr-filter-turbulence.cpp +++ b/src/display/nr-filter-turbulence.cpp @@ -299,7 +299,6 @@ FilterTurbulence::FilterTurbulence() , numOctaves(1) , seed(0) , updated(false) - , updated_area(NR::IPoint(), NR::IPoint()) , fTileWidth(10) //guessed , fTileHeight(10) //guessed , fTileX(1) //guessed diff --git a/src/display/nr-filter-turbulence.h b/src/display/nr-filter-turbulence.h index 50161b6be..8d3639543 100644 --- a/src/display/nr-filter-turbulence.h +++ b/src/display/nr-filter-turbulence.h @@ -64,7 +64,6 @@ private: bool stitchTiles; FilterTurbulenceType type; bool updated; - NR::IRect updated_area; unsigned char *pix_data; double fTileWidth; diff --git a/src/display/nr-filter-units.cpp b/src/display/nr-filter-units.cpp index b1c475c41..a8686545a 100644 --- a/src/display/nr-filter-units.cpp +++ b/src/display/nr-filter-units.cpp @@ -158,22 +158,13 @@ Geom::Affine FilterUnits::get_matrix_user2primitiveunits() const { return get_matrix_user2units(primitiveUnits); } -NR::IRect FilterUnits::get_pixblock_filterarea_paraller() const { +Geom::IntRect FilterUnits::get_pixblock_filterarea_paraller() const { g_assert(filter_area); - int min_x = INT_MAX, min_y = INT_MAX, max_x = INT_MIN, max_y = INT_MIN; Geom::Affine u2pb = get_matrix_user2pb(); - - for (int i = 0 ; i < 4 ; i++) { - Geom::Point p = filter_area->corner(i); - p *= u2pb; - if (p[X] < min_x) min_x = (int)std::floor(p[X]); - if (p[X] > max_x) max_x = (int)std::ceil(p[X]); - if (p[Y] < min_y) min_y = (int)std::floor(p[Y]); - if (p[Y] > max_y) max_y = (int)std::ceil(p[Y]); - } - NR::IRect ret(NR::IPoint(min_x, min_y), NR::IPoint(max_x, max_y)); - return ret; + Geom::Rect r = *filter_area * u2pb; + Geom::IntRect ir = r.roundOutwards(); + return ir; } FilterUnits& FilterUnits::operator=(FilterUnits const &other) { diff --git a/src/display/nr-filter-units.h b/src/display/nr-filter-units.h index 2fc3e5533..1cb4fdbce 100644 --- a/src/display/nr-filter-units.h +++ b/src/display/nr-filter-units.h @@ -133,7 +133,7 @@ public: * NOTE: use only in filters, that define TRAIT_PARALLER in * get_input_traits. The filter effects area may not be representable * by simple rectangle otherwise. */ - NR::IRect get_pixblock_filterarea_paraller() const; + Geom::IntRect get_pixblock_filterarea_paraller() const; FilterUnits& operator=(FilterUnits const &other); diff --git a/src/display/nr-filter.cpp b/src/display/nr-filter.cpp index 6e0aa1a42..963d98654 100644 --- a/src/display/nr-filter.cpp +++ b/src/display/nr-filter.cpp @@ -160,7 +160,7 @@ int Filter::render(NRArenaItem const *item, cairo_t *bgct, NRRectL const *bgarea } } - FilterSlot slot(const_cast<NRArenaItem*>(item), bgct, bgarea, cairo_get_target(graphic), area, units); + FilterSlot slot(const_cast<NRArenaItem*>(item), bgct, bgarea, cairo_get_group_target(graphic), area, units); slot.set_quality(filterquality); slot.set_blurquality(blurquality); @@ -236,10 +236,10 @@ void Filter::compute_drawbox(NRArenaItem const *item, NRRectL &item_bbox) { Geom::Rect enlarged = filter_effect_area(tmp_bbox); enlarged = enlarged * item->ctm; - item_bbox.x0 = (NR::ICoord) floor(enlarged.min()[X]); - item_bbox.y0 = (NR::ICoord) floor(enlarged.min()[Y]); - item_bbox.x1 = (NR::ICoord) ceil(enlarged.max()[X]); - item_bbox.y1 = (NR::ICoord) ceil(enlarged.max()[Y]); + item_bbox.x0 = floor(enlarged.min()[X]); + item_bbox.y0 = floor(enlarged.min()[Y]); + item_bbox.x1 = ceil(enlarged.max()[X]); + item_bbox.y1 = ceil(enlarged.max()[Y]); } Geom::Rect Filter::filter_effect_area(Geom::Rect const &bbox) 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 37998437d..d7f34969f 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -689,6 +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 SPCanvasItemClass *group_parent_class; @@ -732,6 +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; } /** @@ -875,6 +877,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. */ @@ -1045,7 +1061,7 @@ sp_canvas_init (SPCanvas *canvas) #if ENABLE_LCMS canvas->enable_cms_display_adj = false; - canvas->cms_key = new Glib::ustring(""); + new (&canvas->cms_key) Glib::ustring(""); #endif // ENABLE_LCMS canvas->is_scrolling = false; @@ -1105,6 +1121,8 @@ sp_canvas_destroy (GtkObject *object) shutdown_transients (canvas); + canvas->cms_key.~ustring(); + if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object); } @@ -1134,8 +1152,6 @@ sp_canvas_new_aa (void) static void sp_canvas_realize (GtkWidget *widget) { - SPCanvas *canvas = SP_CANVAS (widget); - GdkWindowAttr attributes; attributes.window_type = GDK_WINDOW_CHILD; attributes.x = widget->allocation.x; @@ -1171,8 +1187,6 @@ sp_canvas_realize (GtkWidget *widget) widget->style = gtk_style_attach (widget->style, widget->window); gtk_widget_set_realized (widget, TRUE); - - canvas->pixmap_gc = gdk_gc_new (SP_CANVAS_WINDOW (canvas)); } /** @@ -1189,9 +1203,6 @@ sp_canvas_unrealize (GtkWidget *widget) shutdown_transients (canvas); - gdk_gc_destroy (canvas->pixmap_gc); - canvas->pixmap_gc = NULL; - if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget); } @@ -1216,8 +1227,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, @@ -1602,7 +1621,7 @@ sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event) if (event->window != SP_CANVAS_WINDOW (canvas)) return FALSE; - if (canvas->pixmap_gc == NULL) // canvas being deleted + if (canvas->root == NULL) // canvas being deleted return FALSE; canvas->state = event->state; @@ -1670,7 +1689,7 @@ static void sp_canvas_paint_single_buffer(SPCanvas *canvas, int x0, int y0, int Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool fromDisplay = prefs->getBool( "/options/displayprofile/from_display"); if ( fromDisplay ) { - transf = Inkscape::CMSSystem::getDisplayPer( canvas->cms_key ? *(canvas->cms_key) : "" ); + transf = Inkscape::CMSSystem::getDisplayPer( canvas->cms_key ); } else { transf = Inkscape::CMSSystem::getDisplayTransform(); } @@ -1857,16 +1876,6 @@ sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1) rect.x1 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width); rect.y1 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height); -#ifdef DEBUG_REDRAW - // paint the area to redraw yellow - gdk_rgb_gc_set_foreground (canvas->pixmap_gc, 0xFFFF00); - gdk_draw_rectangle (SP_CANVAS_WINDOW (canvas), - canvas->pixmap_gc, - TRUE, - rect.x0 - canvas->x0, rect.y0 - canvas->y0, - rect.x1 - rect.x0, rect.y1 - rect.y0); -#endif - PaintRectSetup setup; setup.canvas = canvas; @@ -2064,7 +2073,7 @@ paint (SPCanvas *canvas) static int do_update (SPCanvas *canvas) { - if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop during interrupted display! + if (!canvas->root) // canvas may have already be destroyed by closing desktop during interrupted display! return TRUE; if (canvas->drawing_disabled) @@ -2150,12 +2159,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 @@ -2168,7 +2182,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 } - } /** @@ -2301,13 +2314,15 @@ Geom::Rect SPCanvas::getViewbox() const } /** - * Return canvas window coordinates as IRect (a rectangle defined by integers). + * Return canvas window coordinates as integer rectangle. */ -NR::IRect SPCanvas::getViewboxIntegers() const +Geom::IntRect SPCanvas::getViewboxIntegers() const { GtkWidget const *w = GTK_WIDGET(this); - return NR::IRect(NR::IPoint(x0, y0), - NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height)); + Geom::IntRect ret; + ret.setMin(Geom::IntPoint(x0, y0)); + ret.setMax(Geom::IntPoint(x0 + w->allocation.width, y0 + w->allocation.height)); + return ret; } inline int sp_canvas_tile_floor(int x) diff --git a/src/display/sp-canvas.h b/src/display/sp-canvas.h index 7a6b3295e..f284afdf2 100644 --- a/src/display/sp-canvas.h +++ b/src/display/sp-canvas.h @@ -112,9 +112,6 @@ struct SPCanvas { int close_enough; - /* GC for temporary draw pixmap */ - GdkGC *pixmap_gc; - unsigned int need_update : 1; unsigned int need_redraw : 1; unsigned int need_repick : 1; @@ -143,13 +140,13 @@ struct SPCanvas { #if ENABLE_LCMS bool enable_cms_display_adj; - Glib::ustring* cms_key; + Glib::ustring cms_key; #endif // ENABLE_LCMS bool is_scrolling; Geom::Rect getViewbox() const; - NR::IRect getViewboxIntegers() const; + Geom::IntRect getViewboxIntegers() const; }; GtkWidget *sp_canvas_new_aa(); |
