diff options
Diffstat (limited to 'src/display')
| -rw-r--r-- | src/display/canvas-arena.cpp | 6 | ||||
| -rw-r--r-- | src/display/nr-filter-tile.cpp | 12 | ||||
| -rw-r--r-- | src/display/sp-canvas.cpp | 150 | ||||
| -rw-r--r-- | src/display/sp-canvas.h | 18 |
4 files changed, 100 insertions, 86 deletions
diff --git a/src/display/canvas-arena.cpp b/src/display/canvas-arena.cpp index ec99eca9a..366b3c7d1 100644 --- a/src/display/canvas-arena.cpp +++ b/src/display/canvas-arena.cpp @@ -250,7 +250,8 @@ sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event) arena->c = Geom::Point(event->crossing.x, event->crossing.y); /* fixme: Not sure abut this, but seems the right thing (Lauris) */ - arena->drawing.update(Geom::IntRect::infinite(), arena->ctx, DrawingItem::STATE_PICK, 0); + arena->drawing.update(Geom::IntRect::infinite(), arena->ctx, + DrawingItem::STATE_PICK | DrawingItem::STATE_BBOX, 0); arena->active = arena->drawing.pick(arena->c, arena->drawing.delta, arena->sticky); ret = sp_canvas_arena_send_event (arena, event); } @@ -269,7 +270,8 @@ sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event) arena->c = Geom::Point(event->motion.x, event->motion.y); /* fixme: Not sure abut this, but seems the right thing (Lauris) */ - arena->drawing.update(Geom::IntRect::infinite(), arena->ctx, DrawingItem::STATE_PICK); + arena->drawing.update(Geom::IntRect::infinite(), arena->ctx, + DrawingItem::STATE_PICK | DrawingItem::STATE_BBOX); new_arena = arena->drawing.pick(arena->c, arena->drawing.delta, arena->sticky); if (new_arena != arena->active) { GdkEventCrossing ec; diff --git a/src/display/nr-filter-tile.cpp b/src/display/nr-filter-tile.cpp index 7172f88ee..c151c8537 100644 --- a/src/display/nr-filter-tile.cpp +++ b/src/display/nr-filter-tile.cpp @@ -32,18 +32,6 @@ FilterTile::~FilterTile() void FilterTile::render_cairo(FilterSlot &slot) { - // FIX ME! - static bool tile_warning = false; - if (!tile_warning) { - g_warning("Renderer for feTile has non-optimal implementation, expect slowness and bugs."); - tile_warning = true; - } - - // Fixing isn't so easy as the Inkscape renderer breaks the canvas into "rendering" tiles for - // faster rendering. (The "rendering" tiles are not the same as the tiles in this primitive.) - // Only if the the feTile tile source falls inside the current "rendering" tile will the tile - // image be available. - // This input source contains only the "rendering" tile. cairo_surface_t *in = slot.getcairo(_input); diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp index 644da8d5a..decb14184 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -27,6 +27,7 @@ #include "helper/sp-marshal.h" #include <2geom/rect.h> #include <2geom/affine.h> +#include "display/cairo-utils.h" #include "display/sp-canvas.h" #include "display/sp-canvas-group.h" #include "preferences.h" @@ -37,6 +38,7 @@ #include "display/cairo-utils.h" #include "debug/gdk-event-latency-tracker.h" #include "desktop.h" +#include "color.h" using Inkscape::Debug::GdkEventLatencyTracker; @@ -959,6 +961,8 @@ static void sp_canvas_init(SPCanvas *canvas) canvas->_backing_store = NULL; canvas->_clean_region = cairo_region_create(); + canvas->_background = cairo_pattern_create_rgb(1, 1, 1); + canvas->_background_is_checkerboard = false; canvas->_forced_redraw_count = 0; canvas->_forced_redraw_limit = -1; @@ -972,10 +976,7 @@ static void sp_canvas_init(SPCanvas *canvas) void SPCanvas::shutdownTransients() { // Reset the clean region - if (_clean_region && !cairo_region_is_empty(_clean_region)) { - cairo_region_destroy(_clean_region); - _clean_region = cairo_region_create(); - } + dirtyAll(); if (_grabbed_item) { _grabbed_item = NULL; @@ -1006,6 +1007,10 @@ void SPCanvas::dispose(GObject *object) cairo_region_destroy(canvas->_clean_region); canvas->_clean_region = NULL; } + if (canvas->_background) { + cairo_pattern_destroy(canvas->_background); + canvas->_background = NULL; + } canvas->shutdownTransients(); #if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) @@ -1149,7 +1154,7 @@ void SPCanvas::handle_size_allocate(GtkWidget *widget, GtkAllocation *allocation Geom::IntRect new_area = Geom::IntRect::from_xywh(canvas->_x0, canvas->_y0, allocation->width, allocation->height); - // resize backing store; the clean region does not change + // resize backing store cairo_surface_t *new_backing_store = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation->width, allocation->height); if (canvas->_backing_store) { @@ -1162,9 +1167,12 @@ void SPCanvas::handle_size_allocate(GtkWidget *widget, GtkAllocation *allocation } canvas->_backing_store = new_backing_store; + // Clip the clean region to the new allocation + cairo_rectangle_int_t crect = { canvas->_x0, canvas->_y0, allocation->width, allocation->height }; + cairo_region_intersect_rectangle(canvas->_clean_region, &crect); + gtk_widget_set_allocation (widget, allocation); - // Schedule redraw of new region if (SP_CANVAS_ITEM_GET_CLASS (canvas->_root)->viewbox_changed) SP_CANVAS_ITEM_GET_CLASS (canvas->_root)->viewbox_changed (canvas->_root, new_area); @@ -1173,6 +1181,7 @@ void SPCanvas::handle_size_allocate(GtkWidget *widget, GtkAllocation *allocation allocation->x, allocation->y, allocation->width, allocation->height); } + // Schedule redraw of any newly exposed regions canvas->addIdle(); } @@ -1527,44 +1536,23 @@ int SPCanvas::handle_motion(GtkWidget *widget, GdkEventMotion *event) void SPCanvas::paintSingleBuffer(Geom::IntRect const &paint_rect, Geom::IntRect const &canvas_rect, int /*sw*/) { - GtkWidget *widget = GTK_WIDGET (this); - - // Mark the region clean - markRect(paint_rect, 0); - SPCanvasBuf buf; buf.buf = NULL; buf.buf_rowstride = 0; buf.rect = paint_rect; buf.visible_rect = canvas_rect; buf.is_empty = true; - //buf.ct = gdk_cairo_create(widget->window); // create temporary surface cairo_surface_t *imgs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, paint_rect.width(), paint_rect.height()); buf.ct = cairo_create(imgs); - //cairo_translate(buf.ct, -x0, -y0); - - // fix coordinates, clip all drawing to the tile and clear the background - //cairo_translate(buf.ct, paint_rect.left() - canvas->x0, paint_rect.top() - canvas->y0); - //cairo_rectangle(buf.ct, 0, 0, paint_rect.width(), paint_rect.height()); - //cairo_set_line_width(buf.ct, 3); - //cairo_set_source_rgba(buf.ct, 1.0, 0.0, 0.0, 0.1); - //cairo_stroke_preserve(buf.ct); - //cairo_clip(buf.ct); - -#if GTK_CHECK_VERSION(3,0,0) - GtkStyleContext *context = gtk_widget_get_style_context(widget); - gtk_render_background(context, buf.ct, 0, 0, paint_rect.width(), paint_rect.height()); -#else - GtkStyle *style = gtk_widget_get_style (widget); - gdk_cairo_set_source_color(buf.ct, &style->bg[GTK_STATE_NORMAL]); -#endif + cairo_save(buf.ct); + cairo_translate(buf.ct, -paint_rect.left(), -paint_rect.top()); + cairo_set_source(buf.ct, _background); cairo_set_operator(buf.ct, CAIRO_OPERATOR_SOURCE); - //cairo_rectangle(buf.ct, 0, 0, paint_rect.width(), paint_rec.height()); cairo_paint(buf.ct); - cairo_set_operator(buf.ct, CAIRO_OPERATOR_OVER); + cairo_restore(buf.ct); if (_root->visible) { SP_CANVAS_ITEM_GET_CLASS(_root)->render(_root, &buf); @@ -1608,8 +1596,8 @@ void SPCanvas::paintSingleBuffer(Geom::IntRect const &paint_rect, Geom::IntRect cairo_destroy(xct); cairo_surface_destroy(imgs); - cairo_rectangle_int_t crect = { paint_rect.left(), paint_rect.right(), paint_rect.width(), paint_rect.height() }; - cairo_region_union_rectangle(_clean_region, &crect); + // Mark the painted rectangle clean + markRect(paint_rect, 0); gtk_widget_queue_draw_area(GTK_WIDGET(this), paint_rect.left() -_x0, paint_rect.top() - _y0, paint_rect.width(), paint_rect.height()); @@ -1799,8 +1787,14 @@ void SPCanvas::endForcedFullRedraws() gboolean SPCanvas::handle_draw(GtkWidget *widget, cairo_t *cr) { SPCanvas *canvas = SP_CANVAS(widget); + // Blit from the backing store, without regard for the clean region. + // This is necessary because GTK clears the widget for us, which causes + // severe flicker while drawing if we don't blit the old contents. + cairo_set_source_surface(cr, canvas->_backing_store, 0, 0); + cairo_paint(cr); + cairo_rectangle_list_t *rects = cairo_copy_clip_rectangle_list(cr); - cairo_region_t *draw_region = cairo_region_create(); + cairo_region_t *dirty_region = cairo_region_create(); for (int i = 0; i < rects->num_rectangles; i++) { cairo_rectangle_t rectangle = rects->rectangles[i]; @@ -1808,36 +1802,16 @@ gboolean SPCanvas::handle_draw(GtkWidget *widget, cairo_t *cr) { rectangle.width, rectangle.height); Geom::IntRect ir = dr.roundOutwards(); cairo_rectangle_int_t irect = { ir.left(), ir.top(), ir.width(), ir.height() }; - cairo_region_union_rectangle(draw_region, &irect); + cairo_region_union_rectangle(dirty_region, &irect); } cairo_rectangle_list_destroy(rects); - - cairo_region_t *draw_dirty = cairo_region_copy(draw_region); - cairo_region_subtract(draw_dirty, canvas->_clean_region); - cairo_region_intersect(draw_region, canvas->_clean_region); - - // Draw the clean portion - if (!cairo_region_is_empty(draw_region)) { - cairo_region_translate(draw_region, -canvas->_x0, -canvas->_y0); - cairo_save(cr); - int n_rects = cairo_region_num_rectangles(draw_region); - for (int i = 0; i < n_rects; ++i) { - cairo_rectangle_int_t crect; - cairo_region_get_rectangle(draw_region, i, &crect); - cairo_rectangle(cr, crect.x, crect.y, crect.width, crect.height); - } - cairo_clip(cr); - cairo_set_source_surface(cr, canvas->_backing_store, 0, 0); - cairo_paint(cr); - cairo_restore(cr); - } + cairo_region_subtract(dirty_region, canvas->_clean_region); // Render the dirty portion in the background - if (!cairo_region_is_empty(draw_dirty)) { + if (!cairo_region_is_empty(dirty_region)) { canvas->addIdle(); } - cairo_region_destroy(draw_region); - cairo_region_destroy(draw_dirty); + cairo_region_destroy(dirty_region); return TRUE; } @@ -1999,30 +1973,36 @@ void SPCanvas::scrollTo(double cx, double cy, unsigned int clear, bool is_scroll Geom::IntRect old_area = getViewboxIntegers(); Geom::IntRect new_area = old_area + Geom::IntPoint(dx, dy); - - _dx0 = cx; // here the 'd' stands for double, not delta! - _dy0 = cy; - _x0 = ix; - _y0 = iy; gtk_widget_get_allocation(&_widget, &allocation); + // adjust backing store contents assert(_backing_store); cairo_surface_t *new_backing_store = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height); cairo_t *cr = cairo_create(new_backing_store); - cairo_set_source_rgb(cr, 1, 1, 1); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + // Paint the background + cairo_translate(cr, -ix, -iy); + cairo_set_source(cr, _background); cairo_paint(cr); - cairo_set_source_surface(cr, _backing_store, -dx, -dy); + // Copy the old backing store contents + cairo_set_source_surface(cr, _backing_store, _x0, _y0); + cairo_rectangle(cr, _x0, _y0, allocation.width, allocation.height); + cairo_clip(cr); cairo_paint(cr); cairo_destroy(cr); cairo_surface_destroy(_backing_store); _backing_store = new_backing_store; + _dx0 = cx; // here the 'd' stands for double, not delta! + _dy0 = cy; + _x0 = ix; + _y0 = iy; + + // Adjust the clean region if (clear) { - cairo_region_destroy(_clean_region); - _clean_region = cairo_region_create(); + dirtyAll(); } else { cairo_rectangle_int_t crect = { _x0, _y0, allocation.width, allocation.height }; cairo_region_intersect_rectangle(_clean_region, &crect); @@ -2070,6 +2050,35 @@ void SPCanvas::requestRedraw(int x0, int y0, int x1, int y1) addIdle(); } +void SPCanvas::setBackgroundColor(guint32 rgba) { + double new_r = SP_RGBA32_R_F(rgba); + double new_g = SP_RGBA32_G_F(rgba); + double new_b = SP_RGBA32_B_F(rgba); + if (!_background_is_checkerboard) { + double old_r, old_g, old_b; + cairo_pattern_get_rgba(_background, &old_r, &old_g, &old_b, NULL); + if (new_r == old_r && new_g == old_g && new_b == old_b) return; + } + if (_background) { + cairo_pattern_destroy(_background); + } + _background = cairo_pattern_create_rgb(new_r, new_g, new_b); + _background_is_checkerboard = false; + dirtyAll(); + addIdle(); +} + +void SPCanvas::setBackgroundCheckerboard() { + if (_background_is_checkerboard) return; + if (_background) { + cairo_pattern_destroy(_background); + } + _background = ink_cairo_pattern_create_checkerboard(); + _background_is_checkerboard = true; + dirtyAll(); + addIdle(); +} + /** * Sets world coordinates from win and canvas. */ @@ -2175,6 +2184,13 @@ void SPCanvas::dirtyRect(Geom::IntRect const &area) { markRect(area, 1); } +void SPCanvas::dirtyAll() { + if (_clean_region && !cairo_region_is_empty(_clean_region)) { + cairo_region_destroy(_clean_region); + _clean_region = cairo_region_create(); + } +} + void SPCanvas::markRect(Geom::IntRect const &area, uint8_t val) { cairo_rectangle_int_t crect = { area.left(), area.top(), area.width(), area.height() }; diff --git a/src/display/sp-canvas.h b/src/display/sp-canvas.h index a1f8d0a1a..171fdaf67 100644 --- a/src/display/sp-canvas.h +++ b/src/display/sp-canvas.h @@ -87,6 +87,9 @@ struct SPCanvas { Geom::IntRect getViewboxIntegers() const; SPCanvasGroup *getRoot(); + void setBackgroundColor(guint32 rgba); + void setBackgroundCheckerboard(); + /// Returns new canvas as widget. static GtkWidget *createAA(); @@ -105,7 +108,8 @@ private: /// Marks the specified area as dirty (requiring redraw) void dirtyRect(Geom::IntRect const &area); - /// Marks specific canvas rectangle as clean (val == 0) or dirty (otherwise) + /// Marks the whole widget for redraw + void dirtyAll(); void markRect(Geom::IntRect const &area, uint8_t val); /// Invokes update, paint, and repick on canvas. @@ -177,14 +181,18 @@ public: bool _is_dragging; double _dx0; double _dy0; - int _x0; - int _y0; + int _x0; ///< World coordinate of the leftmost pixels + int _y0; ///< World coordinate of the topmost pixels - /* Area that needs redrawing, stored as a microtile array */ + /// Image surface storing the contents of the widget cairo_surface_t *_backing_store; + /// Area of the widget that has up-to-date content cairo_region_t *_clean_region; + /// Widget background, defaults to white + cairo_pattern_t *_background; + bool _background_is_checkerboard; - /** Last known modifier state, for deferred repick when a button is down. */ + /// Last known modifier state, for deferred repick when a button is down. int _state; /** The item containing the mouse pointer, or NULL if none. */ |
