summaryrefslogtreecommitdiffstats
path: root/src/display/sp-canvas.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/display/sp-canvas.cpp')
-rw-r--r--src/display/sp-canvas.cpp458
1 files changed, 177 insertions, 281 deletions
diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp
index df84e379c..9201168ef 100644
--- a/src/display/sp-canvas.cpp
+++ b/src/display/sp-canvas.cpp
@@ -8,9 +8,11 @@
* fred
* bbyak
* Jon A. Cruz <jon@joncruz.org>
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
*
* Copyright (C) 1998 The Free Software Foundation
* Copyright (C) 2002-2006 authors
+ * Copyright (C) 2016 Google Inc.
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
@@ -25,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"
@@ -35,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;
@@ -122,7 +126,7 @@ struct SPCanvasClass {
namespace {
-gint const UPDATE_PRIORITY = G_PRIORITY_HIGH_IDLE;
+gint const UPDATE_PRIORITY = G_PRIORITY_DEFAULT_IDLE;
GdkWindow *getWindow(SPCanvas *canvas)
{
@@ -318,13 +322,9 @@ void sp_canvas_item_dispose(GObject *object)
if (item == item->canvas->_grabbed_item) {
item->canvas->_grabbed_item = NULL;
-#if GTK_CHECK_VERSION(3,0,0)
- GdkDeviceManager *dm = gdk_display_get_device_manager(gdk_display_get_default());
- GdkDevice *device = gdk_device_manager_get_client_pointer(dm);
+ auto dm = gdk_display_get_device_manager(gdk_display_get_default());
+ auto device = gdk_device_manager_get_client_pointer(dm);
gdk_device_ungrab(device, GDK_CURRENT_TIME);
-#else
- gdk_pointer_ungrab (GDK_CURRENT_TIME);
-#endif
}
if (item == item->canvas->_focused_item) {
@@ -613,9 +613,8 @@ int sp_canvas_item_grab(SPCanvasItem *item, guint event_mask, GdkCursor *cursor,
// fixme: Top hack (Lauris)
// fixme: If we add key masks to event mask, Gdk will abort (Lauris)
// fixme: But Canvas actualle does get key events, so all we need is routing these here
-#if GTK_CHECK_VERSION(3,0,0)
- GdkDeviceManager *dm = gdk_display_get_device_manager(gdk_display_get_default());
- GdkDevice *device = gdk_device_manager_get_client_pointer(dm);
+ auto dm = gdk_display_get_device_manager(gdk_display_get_default());
+ auto device = gdk_device_manager_get_client_pointer(dm);
gdk_device_grab(device,
getWindow(item->canvas),
GDK_OWNERSHIP_NONE,
@@ -623,11 +622,6 @@ int sp_canvas_item_grab(SPCanvasItem *item, guint event_mask, GdkCursor *cursor,
(GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
cursor,
etime);
-#else
- gdk_pointer_grab( getWindow(item->canvas), FALSE,
- (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
- NULL, cursor, etime);
-#endif
item->canvas->_grabbed_item = item;
item->canvas->_grabbed_event_mask = event_mask;
@@ -654,13 +648,9 @@ void sp_canvas_item_ungrab(SPCanvasItem *item, guint32 etime)
item->canvas->_grabbed_item = NULL;
-#if GTK_CHECK_VERSION(3,0,0)
- GdkDeviceManager *dm = gdk_display_get_device_manager(gdk_display_get_default());
- GdkDevice *device = gdk_device_manager_get_client_pointer(dm);
+ auto dm = gdk_display_get_device_manager(gdk_display_get_default());
+ auto device = gdk_device_manager_get_client_pointer(dm);
gdk_device_ungrab(device, etime);
-#else
- gdk_pointer_ungrab (etime);
-#endif
}
/**
@@ -909,16 +899,9 @@ void sp_canvas_class_init(SPCanvasClass *klass)
widget_class->realize = SPCanvas::handle_realize;
widget_class->unrealize = SPCanvas::handle_unrealize;
-
-#if GTK_CHECK_VERSION(3,0,0)
widget_class->get_preferred_width = SPCanvas::handle_get_preferred_width;
widget_class->get_preferred_height = SPCanvas::handle_get_preferred_height;
widget_class->draw = SPCanvas::handle_draw;
-#else
- widget_class->size_request = SPCanvas::handle_size_request;
- widget_class->expose_event = SPCanvas::handle_expose;
-#endif
-
widget_class->size_allocate = SPCanvas::handle_size_allocate;
widget_class->button_press_event = SPCanvas::handle_button;
widget_class->button_release_event = SPCanvas::handle_button;
@@ -935,7 +918,6 @@ void sp_canvas_class_init(SPCanvasClass *klass)
static void sp_canvas_init(SPCanvas *canvas)
{
gtk_widget_set_has_window (GTK_WIDGET (canvas), TRUE);
- gtk_widget_set_double_buffered (GTK_WIDGET (canvas), FALSE);
gtk_widget_set_can_focus (GTK_WIDGET (canvas), TRUE);
canvas->_pick_event.type = GDK_LEAVE_NOTIFY;
@@ -956,9 +938,10 @@ static void sp_canvas_init(SPCanvas *canvas)
canvas->_drawing_disabled = false;
- canvas->_tiles=NULL;
- canvas->_tLeft=canvas->_tTop=canvas->_tRight=canvas->_tBottom=0;
- canvas->_tile_h=canvas->_tile_h=0;
+ 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;
@@ -967,32 +950,18 @@ static void sp_canvas_init(SPCanvas *canvas)
canvas->_enable_cms_display_adj = false;
new (&canvas->_cms_key) Glib::ustring("");
#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2)
-
- canvas->_is_scrolling = false;
}
void SPCanvas::shutdownTransients()
{
- // We turn off the need_redraw flag, since if the canvas is mapped again
- // it will request a redraw anyways. We do not turn off the need_update
- // flag, though, because updates are not queued when the canvas remaps
- // itself.
- //
- _need_redraw = FALSE;
- if (_tiles) g_free(_tiles);
- _tiles = NULL;
- _tLeft = _tTop = _tRight = _tBottom = 0;
- _tile_h = _tile_h = 0;
+ // Reset the clean region
+ dirtyAll();
if (_grabbed_item) {
_grabbed_item = NULL;
-#if GTK_CHECK_VERSION(3,0,0)
- GdkDeviceManager *dm = gdk_display_get_device_manager(gdk_display_get_default());
- GdkDevice *device = gdk_device_manager_get_client_pointer(dm);
+ auto dm = gdk_display_get_device_manager(gdk_display_get_default());
+ auto device = gdk_device_manager_get_client_pointer(dm);
gdk_device_ungrab(device, GDK_CURRENT_TIME);
-#else
- gdk_pointer_ungrab(GDK_CURRENT_TIME);
-#endif
}
removeIdle();
}
@@ -1005,6 +974,18 @@ void SPCanvas::dispose(GObject *object)
g_object_unref (canvas->_root);
canvas->_root = NULL;
}
+ if (canvas->_backing_store) {
+ cairo_surface_destroy(canvas->_backing_store);
+ canvas->_backing_store = NULL;
+ }
+ if (canvas->_clean_region) {
+ 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)
@@ -1047,10 +1028,6 @@ void SPCanvas::handle_realize(GtkWidget *widget)
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.visual = gdk_visual_get_system();
-#if !GTK_CHECK_VERSION(3,0,0)
- attributes.colormap = gdk_colormap_get_system();
-#endif
-
attributes.event_mask = (gtk_widget_get_events (widget) |
GDK_EXPOSURE_MASK |
GDK_BUTTON_PRESS_MASK |
@@ -1067,11 +1044,7 @@ void SPCanvas::handle_realize(GtkWidget *widget)
GDK_SCROLL_MASK |
GDK_FOCUS_CHANGE_MASK);
-#if GTK_CHECK_VERSION(3,0,0)
gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
-#else
- gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
-#endif
GdkWindow *window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
gtk_widget_set_window (widget, window);
@@ -1080,18 +1053,8 @@ void SPCanvas::handle_realize(GtkWidget *widget)
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
if (prefs->getBool("/options/useextinput/value", true)) {
gtk_widget_set_events(widget, attributes.event_mask);
-#if !GTK_CHECK_VERSION(3,0,0)
- gtk_widget_set_extension_events(widget, GDK_EXTENSION_EVENTS_ALL);
- // TODO: Extension event stuff has been deprecated in GTK+ 3
-#endif
}
-#if !GTK_CHECK_VERSION(3,0,0)
- // This does nothing in GTK+ 3
- GtkStyle *style = gtk_widget_get_style (widget);
- gtk_widget_set_style (widget, gtk_style_attach (style, window));
-#endif
-
gtk_widget_set_realized (widget, TRUE);
}
@@ -1109,8 +1072,6 @@ void SPCanvas::handle_unrealize(GtkWidget *widget)
(* GTK_WIDGET_CLASS(sp_canvas_parent_class)->unrealize)(widget);
}
-
-#if GTK_CHECK_VERSION(3,0,0)
void SPCanvas::handle_get_preferred_width(GtkWidget *widget, gint *minimum_width, gint *natural_width)
{
static_cast<void>(SP_CANVAS (widget));
@@ -1124,55 +1085,52 @@ void SPCanvas::handle_get_preferred_height(GtkWidget *widget, gint *minimum_heig
*minimum_height = 256;
*natural_height = 256;
}
-#else
-void SPCanvas::handle_size_request(GtkWidget *widget, GtkRequisition *req)
-{
- static_cast<void>(SP_CANVAS (widget));
-
- req->width = 256;
- req->height = 256;
-}
-#endif
-
void SPCanvas::handle_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
{
SPCanvas *canvas = SP_CANVAS (widget);
- GtkAllocation widg_allocation;
+ GtkAllocation old_allocation;
- gtk_widget_get_allocation (widget, &widg_allocation);
+ gtk_widget_get_allocation(widget, &old_allocation);
-// Geom::IntRect old_area = Geom::IntRect::from_xywh(canvas->x0, canvas->y0,
-// widg_allocation.width, widg_allocation.height);
+// Geom::IntRect old_area = Geom::IntRect::from_xywh(canvas->_x0, canvas->_y0,
+// old_allocation.width, old_allocation.height);
Geom::IntRect new_area = Geom::IntRect::from_xywh(canvas->_x0, canvas->_y0,
allocation->width, allocation->height);
- // Schedule redraw of new region
- canvas->resizeTiles(canvas->_x0, canvas->_y0, canvas->_x0 + allocation->width, canvas->_y0 + allocation->height);
- if (SP_CANVAS_ITEM_GET_CLASS (canvas->_root)->viewbox_changed)
- SP_CANVAS_ITEM_GET_CLASS (canvas->_root)->viewbox_changed (canvas->_root, new_area);
-
- if (allocation->width > widg_allocation.width) {
- canvas->requestRedraw(canvas->_x0 + widg_allocation.width,
- 0,
- canvas->_x0 + allocation->width,
- canvas->_y0 + allocation->height);
- }
- if (allocation->height > widg_allocation.height) {
- canvas->requestRedraw(0,
- canvas->_y0 + widg_allocation.height,
- canvas->_x0 + allocation->width,
- canvas->_y0 + allocation->height);
- }
+ // resize backing store
+ cairo_surface_t *new_backing_store = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ allocation->width, allocation->height);
+ if (canvas->_backing_store) {
+ cairo_t *cr = cairo_create(new_backing_store);
+ cairo_translate(cr, -canvas->_x0, -canvas->_y0);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source(cr, canvas->_background);
+ cairo_paint(cr);
+ cairo_set_source_surface(cr, canvas->_backing_store, canvas->_x0, canvas->_y0);
+ cairo_paint(cr);
+ cairo_destroy(cr);
+ cairo_surface_destroy(canvas->_backing_store);
+ }
+ 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);
+ if (SP_CANVAS_ITEM_GET_CLASS (canvas->_root)->viewbox_changed)
+ SP_CANVAS_ITEM_GET_CLASS (canvas->_root)->viewbox_changed (canvas->_root, new_area);
+
if (gtk_widget_get_realized (widget)) {
gdk_window_move_resize (gtk_widget_get_window (widget),
allocation->x, allocation->y,
allocation->width, allocation->height);
}
+ // Schedule redraw of any newly exposed regions
+ canvas->addIdle();
}
int SPCanvas::emitEvent(GdkEvent *event)
@@ -1206,9 +1164,7 @@ int SPCanvas::emitEvent(GdkEvent *event)
break;
case GDK_SCROLL:
mask = GDK_SCROLL_MASK;
-#if GTK_CHECK_VERSION(3,0,0)
mask |= GDK_SMOOTH_SCROLL_MASK;
-#endif
break;
default:
mask = 0;
@@ -1490,13 +1446,9 @@ gint SPCanvas::handle_scroll(GtkWidget *widget, GdkEventScroll *event)
}
static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
-#if GTK_CHECK_VERSION(3,0,0)
gdk_window_get_device_position(w,
gdk_event_get_device((GdkEvent *)(event)),
NULL, NULL, NULL);
-#else
- gdk_window_get_pointer(w, NULL, NULL, NULL);
-#endif
gdk_event_request_motions(event);
}
@@ -1526,48 +1478,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);
- GdkRGBA color;
- gtk_style_context_get_background_color(context,
- gtk_widget_get_state_flags(widget),
- &color);
- gdk_cairo_set_source_rgba(buf.ct, &color);
-#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);
@@ -1600,7 +1527,8 @@ void SPCanvas::paintSingleBuffer(Geom::IntRect const &paint_rect, Geom::IntRect
}
#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2)
- cairo_t *xct = gdk_cairo_create(gtk_widget_get_window (widget));
+ //cairo_t *xct = gdk_cairo_create(gtk_widget_get_window (widget));
+ cairo_t *xct = cairo_create(_backing_store);
cairo_translate(xct, paint_rect.left() - _x0, paint_rect.top() - _y0);
cairo_rectangle(xct, 0, 0, paint_rect.width(), paint_rect.height());
cairo_clip(xct);
@@ -1609,6 +1537,12 @@ void SPCanvas::paintSingleBuffer(Geom::IntRect const &paint_rect, Geom::IntRect
cairo_paint(xct);
cairo_destroy(xct);
cairo_surface_destroy(imgs);
+
+ // 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());
}
struct PaintRectSetup {
@@ -1751,16 +1685,12 @@ bool SPCanvas::paintRect(int xx0, int yy0, int xx1, int yy1)
// Save the mouse location
gint x, y;
-#if GTK_CHECK_VERSION(3,0,0)
- GdkDeviceManager *dm = gdk_display_get_device_manager(gdk_display_get_default());
- GdkDevice *device = gdk_device_manager_get_client_pointer(dm);
+ auto dm = gdk_display_get_device_manager(gdk_display_get_default());
+ auto device = gdk_device_manager_get_client_pointer(dm);
gdk_window_get_device_position(gtk_widget_get_window(GTK_WIDGET(this)),
device,
&x, &y, NULL);
-#else
- gdk_window_get_pointer(gtk_widget_get_window(GTK_WIDGET(this)), &x, &y, NULL);
-#endif
setup.mouse_loc = sp_canvas_window_to_world(this, Geom::Point(x,y));
@@ -1792,55 +1722,37 @@ void SPCanvas::endForcedFullRedraws()
_forced_redraw_limit = -1;
}
-#if GTK_CHECK_VERSION(3,0,0)
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 *dirty_region = cairo_region_create();
for (int i = 0; i < rects->num_rectangles; i++) {
cairo_rectangle_t rectangle = rects->rectangles[i];
-
- Geom::IntRect r = Geom::IntRect::from_xywh(rectangle.x + canvas->_x0, rectangle.y + canvas->_y0,
- rectangle.width, rectangle.height);
-
- canvas->requestRedraw(r.left(), r.top(), r.right(), r.bottom());
+ Geom::Rect dr = Geom::Rect::from_xywh(rectangle.x + canvas->_x0, rectangle.y + canvas->_y0,
+ 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(dirty_region, &irect);
}
-
cairo_rectangle_list_destroy(rects);
+ cairo_region_subtract(dirty_region, canvas->_clean_region);
- return FALSE;
-}
-#else
-gboolean SPCanvas::handle_expose(GtkWidget *widget, GdkEventExpose *event)
-{
- SPCanvas *canvas = SP_CANVAS(widget);
-
- if (!gtk_widget_is_drawable (widget) ||
- (event->window != getWindow(canvas))) {
- return FALSE;
+ // Render the dirty portion in the background
+ if (!cairo_region_is_empty(dirty_region)) {
+ canvas->addIdle();
}
+ cairo_region_destroy(dirty_region);
- int n_rects = 0;
- GdkRectangle *rects = NULL;
- gdk_region_get_rectangles(event->region, &rects, &n_rects);
-
- if(rects == NULL)
- return FALSE;
-
- for (int i = 0; i < n_rects; i++) {
- GdkRectangle rectangle = rects[i];
-
- Geom::IntRect r = Geom::IntRect::from_xywh(rectangle.x + canvas->_x0, rectangle.y + canvas->_y0,
- rectangle.width, rectangle.height);
-
- canvas->requestRedraw(r.left(), r.top(), r.right(), r.bottom());
- }
-
- return FALSE;
+ return TRUE;
}
-#endif
-
gint SPCanvas::handle_key_event(GtkWidget *widget, GdkEventKey *event)
{
@@ -1890,42 +1802,22 @@ int SPCanvas::paint()
_need_update = FALSE;
}
- if (!_need_redraw) {
- return TRUE;
- }
-
- Cairo::RefPtr<Cairo::Region> to_paint = Cairo::Region::create();
-
- for (int j = _tTop; j < _tBottom; ++j) {
- for (int i = _tLeft; i < _tRight; ++i) {
- int tile_index = (i - _tLeft) + (j - _tTop) * _tile_h;
-
- if (_tiles[tile_index]) { // if this tile is dirtied (nonzero)
- Cairo::RectangleInt rect = {i*TILE_SIZE, j*TILE_SIZE,
- TILE_SIZE, TILE_SIZE};
- to_paint->do_union(rect);
- }
- }
- }
-
- int n_rect = to_paint->get_num_rectangles();
+ GtkAllocation allocation;
+ gtk_widget_get_allocation(GTK_WIDGET(this), &allocation);
+ cairo_rectangle_int_t crect = { _x0, _y0, allocation.width, allocation.height };
+ cairo_region_t *to_draw = cairo_region_create_rectangle(&crect);
+ cairo_region_subtract(to_draw, _clean_region);
- if (n_rect > 0) {
- for (int i=0; i < n_rect; i++) {
- Cairo::RectangleInt rect = to_paint->get_rectangle(i);
- int x0 = rect.x;
- int y0 = rect.y;
- int x1 = x0 + rect.width;
- int y1 = y0 + rect.height;
- if (!paintRect(x0, y0, x1, y1)) {
- // Aborted
- return FALSE;
- };
- }
+ int n_rects = cairo_region_num_rectangles(to_draw);
+ for (int i = 0; i < n_rects; ++i) {
+ cairo_rectangle_int_t crect;
+ cairo_region_get_rectangle(to_draw, i, &crect);
+ if (!paintRect(crect.x, crect.y, crect.x + crect.width, crect.y + crect.height)) {
+ // Aborted
+ return FALSE;
+ };
}
- _need_redraw = FALSE;
-
// we've had a full unaborted redraw, reset the full redraw counter
if (_forced_redraw_limit != -1) {
_forced_redraw_count = 0;
@@ -2004,15 +1896,41 @@ 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);
-
+
+ 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_operator(cr, CAIRO_OPERATOR_SOURCE);
+ // Paint the background
+ cairo_translate(cr, -ix, -iy);
+ cairo_set_source(cr, _background);
+ cairo_paint(cr);
+ // 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;
- gtk_widget_get_allocation(&_widget, &allocation);
+ // Adjust the clean region
+ if (clear) {
+ dirtyAll();
+ } else {
+ cairo_rectangle_int_t crect = { _x0, _y0, allocation.width, allocation.height };
+ cairo_region_intersect_rectangle(_clean_region, &crect);
+ }
- resizeTiles(_x0, _y0, _x0 + allocation.width, _y0 + allocation.height);
if (SP_CANVAS_ITEM_GET_CLASS(_root)->viewbox_changed) {
SP_CANVAS_ITEM_GET_CLASS(_root)->viewbox_changed(_root, new_area);
}
@@ -2020,19 +1938,17 @@ void SPCanvas::scrollTo(double cx, double cy, unsigned int clear, bool is_scroll
if (!clear) {
// scrolling without zoom; redraw only the newly exposed areas
if ((dx != 0) || (dy != 0)) {
- this->_is_scrolling = is_scrolling;
if (gtk_widget_get_realized(GTK_WIDGET(this))) {
gdk_window_scroll(getWindow(this), -dx, -dy);
}
}
- } else {
- // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
}
+ addIdle();
}
void SPCanvas::updateNow()
{
- if (_need_update || _need_redraw) {
+ if (_need_update) {
doUpdate();
}
}
@@ -2045,26 +1961,45 @@ void SPCanvas::requestUpdate()
void SPCanvas::requestRedraw(int x0, int y0, int x1, int y1)
{
- GtkAllocation allocation;
-
if (!gtk_widget_is_drawable( GTK_WIDGET(this) )) {
return;
}
- if ((x0 >= x1) || (y0 >= y1)) {
+ if (x0 >= x1 || y0 >= y1) {
return;
}
Geom::IntRect bbox(x0, y0, x1, y1);
- gtk_widget_get_allocation(GTK_WIDGET(this), &allocation);
+ dirtyRect(bbox);
+ addIdle();
+}
- Geom::IntRect canvas_rect = Geom::IntRect::from_xywh(this->_x0, this->_y0,
- allocation.width, allocation.height);
-
- Geom::OptIntRect clip = bbox & canvas_rect;
- if (clip) {
- dirtyRect(*clip);
- 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();
}
/**
@@ -2168,63 +2103,24 @@ inline int sp_canvas_tile_ceil(int x)
return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE;
}
-void SPCanvas::resizeTiles(int nl, int nt, int nr, int nb)
-{
- if ( nl >= nr || nt >= nb ) {
- if (_tiles) g_free(_tiles);
- _tLeft = _tTop = _tRight = _tBottom = 0;
- _tile_h = _tile_h = 0;
- _tiles = NULL;
- return;
- }
- int tl = sp_canvas_tile_floor(nl);
- int tt = sp_canvas_tile_floor(nt);
- int tr = sp_canvas_tile_ceil(nr);
- int tb = sp_canvas_tile_ceil(nb);
-
- int nh = tr-tl, nv = tb-tt;
- uint8_t *ntiles = (uint8_t*) g_malloc(nh * nv * sizeof(uint8_t));
- for (int i = tl; i < tr; i++) {
- for (int j = tt; j < tb; j++) {
- int ind = (i-tl) + (j-tt)*nh;
- if ( i >= _tLeft && i < _tRight && j >= _tTop && j < _tBottom ) {
- ntiles[ind] = _tiles[(i - _tLeft) + (j - _tTop) * _tile_h]; // copy from the old tile
- } else {
- ntiles[ind] = 0; // newly exposed areas get 0
- }
- }
- }
- if (_tiles) g_free(_tiles);
- _tiles = ntiles;
- _tLeft = tl;
- _tTop = tt;
- _tRight = tr;
- _tBottom = tb;
- _tile_h = nh;
- _tile_h = nv;
-}
-
void SPCanvas::dirtyRect(Geom::IntRect const &area) {
- _need_redraw = TRUE;
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)
{
- int tl = sp_canvas_tile_floor(area.left());
- int tt = sp_canvas_tile_floor(area.top());
- int tr = sp_canvas_tile_ceil(area.right());
- int tb = sp_canvas_tile_ceil(area.bottom());
- if ( tl >= _tRight || tr <= _tLeft || tt >= _tBottom || tb <= _tTop ) return;
- if ( tl < _tLeft ) tl = _tLeft;
- if ( tr > _tRight ) tr = _tRight;
- if ( tt < _tTop ) tt = _tTop;
- if ( tb > _tBottom ) tb = _tBottom;
-
- for (int i=tl; i<tr; i++) {
- for (int j=tt; j<tb; j++) {
- _tiles[(i - _tLeft) + (j - _tTop) * _tile_h] = val;
- }
+ cairo_rectangle_int_t crect = { area.left(), area.top(), area.width(), area.height() };
+ if (val) {
+ cairo_region_subtract_rectangle(_clean_region, &crect);
+ } else {
+ cairo_region_union_rectangle(_clean_region, &crect);
}
}