summaryrefslogtreecommitdiffstats
path: root/src/display/sp-canvas.cpp
diff options
context:
space:
mode:
authorKrzysztof Kosi??ski <tweenk.pl@gmail.com>2016-04-12 15:23:42 +0000
committerKrzysztof KosiƄski <tweenk.pl@gmail.com>2016-04-12 15:23:42 +0000
commit490f3d7181baff4d215d0077be9e4939f9af5fde (patch)
tree357b178ad76cf09d8ddb7dd27d79dbfef10b658b /src/display/sp-canvas.cpp
parentFixed FIXMEs in Cmake build (set flags when needed) (diff)
downloadinkscape-490f3d7181baff4d215d0077be9e4939f9af5fde.tar.gz
inkscape-490f3d7181baff4d215d0077be9e4939f9af5fde.zip
Hackfest 2016: Fix SPCanvas to comply with GTK3 rendering model.
For now, causes redraw issues with SVG preview in the open dialog. (bzr r14795)
Diffstat (limited to 'src/display/sp-canvas.cpp')
-rw-r--r--src/display/sp-canvas.cpp279
1 files changed, 136 insertions, 143 deletions
diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp
index df84e379c..2dced2512 100644
--- a/src/display/sp-canvas.cpp
+++ b/src/display/sp-canvas.cpp
@@ -122,7 +122,7 @@ struct SPCanvasClass {
namespace {
-gint const UPDATE_PRIORITY = G_PRIORITY_HIGH_IDLE;
+gint const UPDATE_PRIORITY = G_PRIORITY_DEFAULT_IDLE;
GdkWindow *getWindow(SPCanvas *canvas)
{
@@ -956,9 +956,8 @@ 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->_dirty_region = cairo_region_create();
canvas->_forced_redraw_count = 0;
canvas->_forced_redraw_limit = -1;
@@ -967,8 +966,6 @@ 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()
@@ -979,10 +976,6 @@ void SPCanvas::shutdownTransients()
// itself.
//
_need_redraw = FALSE;
- if (_tiles) g_free(_tiles);
- _tiles = NULL;
- _tLeft = _tTop = _tRight = _tBottom = 0;
- _tile_h = _tile_h = 0;
if (_grabbed_item) {
_grabbed_item = NULL;
@@ -1005,6 +998,14 @@ 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->_dirty_region) {
+ cairo_region_destroy(canvas->_dirty_region);
+ canvas->_dirty_region = NULL;
+ }
canvas->shutdownTransients();
#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2)
@@ -1138,41 +1139,53 @@ void SPCanvas::handle_size_request(GtkWidget *widget, GtkRequisition *req)
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);
+ // 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_set_source_surface(cr, canvas->_backing_store, 0, 0);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint(cr);
+ cairo_destroy(cr);
+ cairo_surface_destroy(canvas->_backing_store);
+ }
+ canvas->_backing_store = new_backing_store;
+
+ gtk_widget_set_allocation (widget, allocation);
+
// 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,
+ if (gtk_widget_get_realized (widget)) {
+ gdk_window_move_resize (gtk_widget_get_window (widget),
+ allocation->x, allocation->y,
+ allocation->width, allocation->height);
+ }
+
+ if (allocation->width > old_allocation.width) {
+ canvas->requestRedraw(canvas->_x0 + old_allocation.width,
+ canvas->_y0,
canvas->_x0 + allocation->width,
canvas->_y0 + allocation->height);
}
- if (allocation->height > widg_allocation.height) {
- canvas->requestRedraw(0,
- canvas->_y0 + widg_allocation.height,
+ if (allocation->height > old_allocation.height) {
+ canvas->requestRedraw(canvas->_x0,
+ canvas->_y0 + old_allocation.height,
canvas->_x0 + allocation->width,
canvas->_y0 + allocation->height);
}
-
- gtk_widget_set_allocation (widget, allocation);
-
- if (gtk_widget_get_realized (widget)) {
- gdk_window_move_resize (gtk_widget_get_window (widget),
- allocation->x, allocation->y,
- allocation->width, allocation->height);
- }
}
int SPCanvas::emitEvent(GdkEvent *event)
@@ -1600,7 +1613,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 +1623,12 @@ void SPCanvas::paintSingleBuffer(Geom::IntRect const &paint_rect, Geom::IntRect
cairo_paint(xct);
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_subtract_rectangle(_dirty_region, &crect);
+
+ gtk_widget_queue_draw_area(GTK_WIDGET(this), paint_rect.left() -_x0, paint_rect.top() - _y0,
+ paint_rect.width(), paint_rect.height());
}
struct PaintRectSetup {
@@ -1792,52 +1812,66 @@ 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);
cairo_rectangle_list_t *rects = cairo_copy_clip_rectangle_list(cr);
+ cairo_region_t *draw_region = cairo_region_create();
for (int i = 0; i < rects->num_rectangles; i++) {
cairo_rectangle_t rectangle = rects->rectangles[i];
+ 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(draw_region, &irect);
+ }
+ cairo_rectangle_list_destroy(rects);
- 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());
+ cairo_region_t *draw_dirty = cairo_region_copy(draw_region);
+ cairo_region_intersect(draw_dirty, canvas->_dirty_region);
+ cairo_region_subtract(draw_region, draw_dirty);
+
+ // 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_rectangle_list_destroy(rects);
+ // Render the dirty portion in the background
+ int n_rects = cairo_region_num_rectangles(draw_dirty);
+ for (int i = 0; i < n_rects; ++i) {
+ cairo_rectangle_int_t crect;
+ cairo_region_get_rectangle(draw_dirty, i, &crect);
+ canvas->requestRedraw(crect.x, crect.y, crect.x + crect.width, crect.y + crect.height);
+ }
+ cairo_region_destroy(draw_region);
+ cairo_region_destroy(draw_dirty);
- return FALSE;
+ return TRUE;
}
-#else
+#if !GTK_CHECK_VERSION(3,0,0)
gboolean SPCanvas::handle_expose(GtkWidget *widget, GdkEventExpose *event)
{
- SPCanvas *canvas = SP_CANVAS(widget);
+ cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(widget));
- if (!gtk_widget_is_drawable (widget) ||
- (event->window != getWindow(canvas))) {
- return FALSE;
- }
-
- int n_rects = 0;
- GdkRectangle *rects = NULL;
- gdk_region_get_rectangles(event->region, &rects, &n_rects);
+ gdk_cairo_region (cr, event->region);
+ cairo_clip (cr);
+ gboolean result = SPCanvas::handle_draw(widget, cr);
- if(rects == NULL)
- return FALSE;
-
- for (int i = 0; i < n_rects; i++) {
- GdkRectangle rectangle = rects[i];
+ cairo_destroy (cr);
- 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 result;
}
#endif
@@ -1889,39 +1923,18 @@ int SPCanvas::paint()
sp_canvas_item_invoke_update(_root, Geom::identity(), 0);
_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();
-
- 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(_dirty_region);
+ for (int i = 0; i < n_rects; ++i) {
+ cairo_rectangle_int_t crect;
+ cairo_region_get_rectangle(_dirty_region, i, &crect);
+ if (!paintRect(crect.x, crect.y, crect.x + crect.width, crect.y + crect.height)) {
+ // Aborted
+ return FALSE;
+ };
}
_need_redraw = FALSE;
@@ -2012,21 +2025,46 @@ void SPCanvas::scrollTo(double cx, double cy, unsigned int clear, bool is_scroll
gtk_widget_get_allocation(&_widget, &allocation);
- resizeTiles(_x0, _y0, _x0 + allocation.width, _y0 + allocation.height);
+ 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);
+ cairo_paint(cr);
+ cairo_set_source_surface(cr, _backing_store, -dx, -dy);
+ cairo_paint(cr);
+ cairo_destroy(cr);
+ cairo_surface_destroy(_backing_store);
+ _backing_store = new_backing_store;
+
+ cairo_rectangle_int_t crect = { _x0, _y0, allocation.width, allocation.height };
+ cairo_region_intersect_rectangle(_dirty_region, &crect);
+
if (SP_CANVAS_ITEM_GET_CLASS(_root)->viewbox_changed) {
SP_CANVAS_ITEM_GET_CLASS(_root)->viewbox_changed(_root, new_area);
}
- if (!clear) {
+ if (clear) {
+ requestRedraw(_x0, _y0, allocation.width, allocation.height);
+ } else {
// 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
+
+ if (dx > 0) {
+ requestRedraw(_x0 + allocation.width - dx, _y0, _x0 + allocation.width, _y0 + allocation.height);
+ } else if (dx < 0) {
+ requestRedraw(_x0, _y0, _x0 - dx, _y0 + allocation.height);
+ }
+ if (dy > 0) {
+ requestRedraw(_x0, _y0 + allocation.height - dy, _x0 + allocation.width, _y0 + allocation.height);
+ } else if (dy < 0) {
+ requestRedraw(_x0, _y0, _x0 + allocation.width, _y0 - dy);
+ }
}
}
@@ -2168,42 +2206,6 @@ 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);
@@ -2211,20 +2213,11 @@ void SPCanvas::dirtyRect(Geom::IntRect const &area) {
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_union_rectangle(_dirty_region, &crect);
+ } else {
+ cairo_region_subtract_rectangle(_dirty_region, &crect);
}
}