X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fdisplay%2Fsp-canvas.cpp;h=fef2fcc0c1f4653ba54b02a3a3dff92cd079ce11;hb=caa537aad8330d63ee007884befe3568bd42b8d2;hp=91712e633a4d59b29addf8cbd0d94c22fbc82d1c;hpb=efeb53ea75db6db7cacd663d1897c213ff87d110;p=inkscape.git diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp index 91712e633..fef2fcc0c 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -33,6 +33,12 @@ #include #include #include +#include "prefs-utils.h" + +// Tiles are a way to minimize the number of redraws, eliminating too small redraws. +// The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile. +// If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted. +#define TILE_SIZE 32 enum { RENDERMODE_NORMAL, @@ -738,11 +744,16 @@ sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned i } } - NR::Rect const &bounds = corners.bounds(); - item->x1 = bounds.min()[NR::X]; - item->y1 = bounds.min()[NR::Y]; - item->x2 = bounds.max()[NR::X]; - item->y2 = bounds.max()[NR::Y]; + NR::Maybe const bounds = corners.bounds(); + if (bounds) { + item->x1 = bounds->min()[NR::X]; + item->y1 = bounds->min()[NR::Y]; + item->x2 = bounds->max()[NR::X]; + item->y2 = bounds->max()[NR::Y]; + } else { + // FIXME ? + item->x1 = item->x2 = item->y1 = item->y2 = 0; + } } /** @@ -977,8 +988,14 @@ sp_canvas_init (SPCanvas *canvas) canvas->redraw_aborted.y1 = -NR_HUGE_L; canvas->redraw_count = 0; + + canvas->forced_redraw_count = 0; + canvas->forced_redraw_limit = -1; canvas->slowest_buffer = 0; + + canvas->is_scrolling = false; + } /** @@ -1083,7 +1100,11 @@ sp_canvas_realize (GtkWidget *widget) widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); gdk_window_set_user_data (widget->window, widget); - gtk_widget_set_events(widget, attributes.event_mask); + + if ( prefs_get_int_attribute ("options.useextinput", "value", 1) ) + gtk_widget_set_events(widget, attributes.event_mask); + + widget->style = gtk_style_attach (widget->style, widget->window); GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); @@ -1270,6 +1291,9 @@ pick_current_item (SPCanvas *canvas, GdkEvent *event) int button_down = 0; double x, y; + if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display! + return FALSE; + int retval = FALSE; if (canvas->gen_all_enter_events == false) { @@ -1513,7 +1537,7 @@ sp_canvas_paint_single_buffer (SPCanvas *canvas, int x0, int y0, int x1, int y1, buf.buf = nr_pixelstore_1M_new (FALSE, 0); } - buf.buf_rowstride = sw * 3; + buf.buf_rowstride = sw * 3; // CAIRO FIXME: for cairo output, the buffer must be RGB unpacked, i.e. sw * 4 buf.rect.x0 = x0; buf.rect.y0 = y0; buf.rect.x1 = x1; @@ -1540,6 +1564,28 @@ sp_canvas_paint_single_buffer (SPCanvas *canvas, int x0, int y0, int x1, int y1, x0 - canvas->x0, y0 - canvas->y0, x1 - x0, y1 - y0); } else { +/* +// CAIRO FIXME: after SPCanvasBuf is made 32bpp throughout, this rgb_draw below can be replaced with the below. +// Why this must not be done currently: +// - all canvas items (handles, nodes etc) paint themselves assuming 24bpp +// - cairo assumes bgra, but we have rgba, so r and b get swapped (until we paint all with cairo too) +// - it does not seem to be any faster; in fact since with 32bpp, buf contains less pixels, +// we need more bufs to paint a given area and as a result it's even a bit slower + + cairo_surface_t* cst = cairo_image_surface_create_for_data ( + buf.buf, + CAIRO_FORMAT_RGB24, // unpacked, i.e. 32 bits! one byte is unused + x1 - x0, y1 - y0, + buf.buf_rowstride + ); + cairo_t *ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas)); + cairo_set_source_surface (ct, cst, x0 - canvas->x0, y0 - canvas->y0); + cairo_paint (ct); + cairo_destroy (ct); + cairo_surface_finish (cst); + cairo_surface_destroy (cst); +*/ + gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas), canvas->pixmap_gc, x0 - canvas->x0, y0 - canvas->y0, @@ -1557,64 +1603,28 @@ sp_canvas_paint_single_buffer (SPCanvas *canvas, int x0, int y0, int x1, int y1, } } -/** - * Helper that draws a specific rectangular part of the canvas. +/* Paint the given rect, while updating canvas->redraw_aborted and running iterations after each + * buffer; make sure canvas->redraw_aborted never goes past aborted_limit (used for 2-rect + * optimized repaint) */ -static void -sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1) +static int +sp_canvas_paint_rect_internal (SPCanvas *canvas, NRRectL *rect, NR::ICoord *x_aborted_limit, NR::ICoord *y_aborted_limit) { - g_return_if_fail (!canvas->need_update); - - // Monotonously increment the canvas-global counter on each paint. This will let us find out - // when a new paint happened in event processing during this paint, so we can abort it. - canvas->redraw_count++; - - NRRectL rect; - rect.x0 = xx0; - rect.x1 = xx1; - rect.y0 = yy0; - rect.y1 = yy1; - - if (canvas->redraw_aborted.x0 < canvas->redraw_aborted.x1 || canvas->redraw_aborted.y0 < canvas->redraw_aborted.y1) { - // There was an aborted redraw last time, now we need to redraw BOTH it and the new rect. - - // OPTIMIZATION IDEA: - // if (area(rect) + area(redraw_aborted)) * 1.2 > area (union) - // then paint their union (as done below) - // else - // two_rects = true; paint new rect and aborted rect SEPARATELY without unioning. - // Without this, when you scroll down and quickly up, the entire screen has to be redrawn, - // because the union of the aborted strip at top and the newly exposed strip at bottom - // covers the whole screen. - - // For now, just always do a union. - rect.x0 = MIN(rect.x0, canvas->redraw_aborted.x0); - rect.x1 = MAX(rect.x1, canvas->redraw_aborted.x1); - rect.y0 = MIN(rect.y0, canvas->redraw_aborted.y0); - rect.y1 = MAX(rect.y1, canvas->redraw_aborted.y1); - } - - // Clip drawable rect by the visible area - int draw_x1 = MAX (rect.x0, canvas->x0); - int draw_y1 = MAX (rect.y0, canvas->y0); - int draw_x2 = MIN (rect.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width); - int draw_y2 = MIN (rect.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height); + int draw_x1 = rect->x0; + int draw_x2 = rect->x1; + int draw_y1 = rect->y0; + int draw_y2 = rect->y1; // Here we'll store the time it took to draw the slowest buffer of this paint. glong slowest_buffer = 0; - // Initially, the rect to redraw later (in case we're aborted) is the same as the one we're going to draw now. - canvas->redraw_aborted.x0 = draw_x1; - canvas->redraw_aborted.x1 = draw_x2; - canvas->redraw_aborted.y0 = draw_y1; - canvas->redraw_aborted.y1 = draw_y2; - // Find the optimal buffer dimensions int bw = draw_x2 - draw_x1; int bh = draw_y2 - draw_y1; if ((bw < 1) || (bh < 1)) - return; - int sw, sh; + return 0; + + int sw, sh; // CAIRO FIXME: the sw/sh calculations below all assume 24bpp, need fixing for 32bpp if (canvas->rendermode != RENDERMODE_OUTLINE) { // use 256K as a compromise to not slow down gradients /* 256K is the cached buffer and we need 3 channels */ if (bw * bh < 87381) { // 256K/3 @@ -1662,6 +1672,19 @@ sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1) // Time values to measure each buffer's paint time GTimeVal tstart, tfinish; + // paint from the corner nearest the mouse pointer + + gint x, y; + gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL); + NR::Point pw = sp_canvas_window_to_world (canvas, NR::Point(x,y)); + + bool reverse_x = (pw[NR::X] > ((draw_x2 + draw_x1) / 2)); + bool reverse_y = (pw[NR::Y] > ((draw_y2 + draw_y1) / 2)); + + if ((bw > bh) && (sh > sw)) { + int t = sw; sw = sh; sh = t; + } + // This is the main loop which corresponds to the visible left-to-right, top-to-bottom drawing // of screen blocks (buffers). for (int y0 = draw_y1; y0 < draw_y2; y0 += sh) { @@ -1669,12 +1692,45 @@ sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1) for (int x0 = draw_x1; x0 < draw_x2; x0 += sw) { int x1 = MIN (x0 + sw, draw_x2); + int dx0 = x0; + int dx1 = x1; + int dy0 = y0; + int dy1 = y1; + + if (reverse_x) { + dx0 = (draw_x2 - (x0 + sw)) + draw_x1; + dx0 = MAX (dx0, draw_x1); + dx1 = (draw_x2 - x0) + draw_x1; + dx1 = MIN (dx1, draw_x2); + } + if (reverse_y) { + dy0 = (draw_y2 - (y0 + sh)) + draw_y1; + dy0 = MAX (dy0, draw_y1); + dy1 = (draw_y2 - y0) + draw_y1; + dy1 = MIN (dy1, draw_y2); + } + + // SMOOTH SCROLLING: if we are scrolling, process pending events even before doing any rendering. + // This allows for scrolling smoothly without hiccups. Any accumulated redraws will be made + // when scrolling stops. The scrolling flag is set by sp_canvas_scroll_to for each scroll and zeroed + // here for each redraw, to ensure it never gets stuck. + // OPTIMIZATION IDEA: if drawing is really slow (as measured by canvas->slowest - // buffer), process some events even BEFORE we do any buffers? + // buffer), do the same - process some events even before we paint any buffers + + if (canvas->is_scrolling) { + while (Gtk::Main::events_pending()) { // process any events + Gtk::Main::iteration(false); + } + canvas->is_scrolling = false; + if (this_count != canvas->redraw_count) { // if there was redraw, + return 1; // interrupt this one + } + } // Paint one buffer; measure how long it takes. g_get_current_time (&tstart); - sp_canvas_paint_single_buffer (canvas, x0, y0, x1, y1, draw_x1, draw_y1, draw_x2, draw_y2, sw); + sp_canvas_paint_single_buffer (canvas, dx0, dy0, dx1, dy1, draw_x1, draw_y1, draw_x2, draw_y2, sw); g_get_current_time (&tfinish); // Remember the slowest_buffer of this paint. @@ -1683,16 +1739,32 @@ sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1) slowest_buffer = this_buffer; // After each successful buffer, reduce the rect remaining to redraw by what is already redrawn - if (x1 >= draw_x2 && canvas->redraw_aborted.y0 < y1) - canvas->redraw_aborted.y0 = y1; - if (y1 >= draw_y2 && canvas->redraw_aborted.x0 < x1) - canvas->redraw_aborted.x0 = x1; + if (x1 >= draw_x2) { + if (reverse_y) { + if (canvas->redraw_aborted.y1 > dy0) { canvas->redraw_aborted.y1 = dy0; } + } else { + if (canvas->redraw_aborted.y0 < y1) { canvas->redraw_aborted.y0 = y1; } + } + } + + if (y1 >= draw_y2) { + if (reverse_x) { + if (canvas->redraw_aborted.x1 > dx0) { canvas->redraw_aborted.x1 = dx0; } + } else { + if (canvas->redraw_aborted.x0 < x1) { canvas->redraw_aborted.x0 = x1; } + } + } // INTERRUPTIBLE DISPLAY: // Process events that may have arrived while we were busy drawing; - // only if we're drawing multiple buffers, and only if this one was not very fast - if (multiple_buffers && this_buffer > 25000) { + // only if we're drawing multiple buffers, and only if this one was not very fast, + // and only if we're allowed to interrupt this redraw + bool ok_to_interrupt = (multiple_buffers && this_buffer > 25000); + if (ok_to_interrupt && (canvas->forced_redraw_limit != -1)) { + ok_to_interrupt = (canvas->forced_redraw_count < canvas->forced_redraw_limit); + } + if (ok_to_interrupt) { // Run at most max_iterations of the main loop; we cannot process ALL events // here because some things (e.g. rubberband) flood with dirtying events but will // not redraw themselves @@ -1703,7 +1775,10 @@ sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1) // If one of the iterations has redrawn by itself, abort if (this_count != canvas->redraw_count) { canvas->slowest_buffer = slowest_buffer; - return; + if (canvas->forced_redraw_limit != -1) { + canvas->forced_redraw_count++; + } + return 1; // interrupted } } @@ -1711,8 +1786,11 @@ sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1) // if so, force update and abort if (canvas->need_redraw || canvas->need_update) { canvas->slowest_buffer = slowest_buffer; + if (canvas->forced_redraw_limit != -1) { + canvas->forced_redraw_count++; + } do_update (canvas); - return; + return 1; // interrupted } } } @@ -1720,6 +1798,146 @@ sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1) // Remember the slowest buffer of this paint in canvas canvas->slowest_buffer = slowest_buffer; + + return 0; // finished +} + + +/** + * Helper that draws a specific rectangular part of the canvas. + */ +static void +sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1) +{ + g_return_if_fail (!canvas->need_update); + + // Monotonously increment the canvas-global counter on each paint. This will let us find out + // when a new paint happened in event processing during this paint, so we can abort it. + canvas->redraw_count++; + + NRRectL rect; + rect.x0 = xx0; + rect.x1 = xx1; + rect.y0 = yy0; + rect.y1 = yy1; + + // Clip rect-to-draw by the current visible area + rect.x0 = MAX (rect.x0, canvas->x0); + rect.y0 = MAX (rect.y0, canvas->y0); + 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); + + // Clip rect-aborted-last-time by the current visible area + canvas->redraw_aborted.x0 = MAX (canvas->redraw_aborted.x0, canvas->x0); + canvas->redraw_aborted.y0 = MAX (canvas->redraw_aborted.y0, canvas->y0); + canvas->redraw_aborted.x1 = MIN (canvas->redraw_aborted.x1, canvas->x0/*draw_x1*/ + GTK_WIDGET (canvas)->allocation.width); + canvas->redraw_aborted.y1 = MIN (canvas->redraw_aborted.y1, canvas->y0/*draw_y1*/ + GTK_WIDGET (canvas)->allocation.height); + + if (canvas->redraw_aborted.x0 < canvas->redraw_aborted.x1 && canvas->redraw_aborted.y0 < canvas->redraw_aborted.y1) { + // There was an aborted redraw last time, now we need to redraw BOTH it and the new rect. + + // save the old aborted rect in case we decide to paint it separately (see below) + NRRectL aborted = canvas->redraw_aborted; + + // calculate the rectangle union of the both rects (the smallest rectangle which covers both) + NRRectL nion; + nr_rect_l_union (&nion, &rect, &aborted); + + // subtract one of the rects-to-draw from the other (the smallest rectangle which covers + // all of the first not covered by the second) + NRRectL rect_minus_aborted; + nr_rect_l_subtract (&rect_minus_aborted, &rect, &aborted); + + // Initially, the rect to redraw later (in case we're aborted) is the same as the union of both rects + canvas->redraw_aborted = nion; + + // calculate areas of the three rects + if ((nr_rect_l_area(&rect_minus_aborted) + nr_rect_l_area(&aborted)) * 1.2 < nr_rect_l_area(&nion)) { + // If the summary area of the two rects is significantly (at least by 20%) less than + // the area of their rectangular union, it makes sense to paint the two rects + // separately instead of painting their union. This gives a significant speedup when, + // for example, your current canvas is almost painted, with only a strip at bottom + // left, and at that moment you abort it by scrolling down which reveals a new strip at + // the top. Straightforward painting of the union of the aborted rect and the new rect + // will have to repaint the entire canvas! By contrast, the optimized approach below + // paints the two narrow strips in order which is much faster. + + // find out which rect to draw first - compare them first by y then by x of the top left corners + NRRectL *first; + NRRectL *second; + if (rect.y0 == aborted.y0) { + if (rect.x0 < aborted.x0) { + first = ▭ + second = &aborted; + } else { + second = ▭ + first = &aborted; + } + } else if (rect.y0 < aborted.y0) { + first = ▭ + second = &aborted; + } else { + second = ▭ + first = &aborted; + } + + NRRectL second_minus_first; + nr_rect_l_subtract (&second_minus_first, second, first); + + // paint the first rect; + if (sp_canvas_paint_rect_internal (canvas, first, &(second_minus_first.x0), &(second_minus_first.y0))) { + // aborted! + return; + } + + // if not aborted, assign (second rect minus first) as the new redraw_aborted and paint the same + canvas->redraw_aborted = second_minus_first; + if (sp_canvas_paint_rect_internal (canvas, &second_minus_first, NULL, NULL)) { + return; // aborted + } + + } else { + // no need for separate drawing, just draw the union as one rect + if (sp_canvas_paint_rect_internal (canvas, &nion, NULL, NULL)) { + return; // aborted + } + } + } else { + // Nothing was aborted last time, just draw the rect we're given + + // Initially, the rect to redraw later (in case we're aborted) is the same as the one we're going to draw now. + canvas->redraw_aborted = rect; + + if (sp_canvas_paint_rect_internal (canvas, &rect, NULL, NULL)) { + return; // aborted + } + } + + // we've had a full unaborted redraw, reset the full redraw counter + if (canvas->forced_redraw_limit != -1) { + canvas->forced_redraw_count = 0; + } +} + +/** + * Force a full redraw after a specified number of interrupted redraws + */ +void +sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int count) { + g_return_if_fail(canvas != NULL); + + canvas->forced_redraw_limit = count; + canvas->forced_redraw_count = 0; +} + +/** + * End forced full redraw requests + */ +void +sp_canvas_end_forced_full_redraws(SPCanvas *canvas) { + g_return_if_fail(canvas != NULL); + + canvas->forced_redraw_limit = -1; } /** @@ -1830,43 +2048,40 @@ paint (SPCanvas *canvas) int const canvas_x1 = canvas->x0 + widget->allocation.width; int const canvas_y1 = canvas->y0 + widget->allocation.height; - NRRectL topaint; - topaint.x0 = topaint.y0 = topaint.x1 = topaint.y1 = 0; + bool dirty = false; - for (int j=canvas->tTop&(~3);jtBottom;j+=4) { - for (int i=canvas->tLeft&(~3);itRight;i+=4) { - int mode=0; - - int pl=i+1,pr=i,pt=j+4,pb=j; - for (int l=MAX(j,canvas->tTop);ltBottom);l++) { - for (int k=MAX(i,canvas->tLeft);ktRight);k++) { - if ( canvas->tiles[(k-canvas->tLeft)+(l-canvas->tTop)*canvas->tileH] ) { - mode|=1<<((k-i)+(l-j)*4); - if ( k < pl ) pl=k; - if ( k+1 > pr ) pr=k+1; - if ( l < pt ) pt=l; - if ( l+1 > pb ) pb=l+1; - } - canvas->tiles[(k-canvas->tLeft)+(l-canvas->tTop)*canvas->tileH]=0; - } - } - - if ( mode ) { - NRRectL tile; - tile.x0 = MAX (pl*32, canvas->x0); - tile.y0 = MAX (pt*32, canvas->y0); - tile.x1 = MIN (pr*32, canvas_x1); - tile.y1 = MIN (pb*32, canvas_y1); - if ((tile.x0 < tile.x1) && (tile.y0 < tile.y1)) { - nr_rect_l_union (&topaint, &topaint, &tile); - } + int pl = canvas->tRight, pr = canvas->tLeft, pt = canvas->tBottom, pb = canvas->tTop; // start with "inverted" tile rect + for (int j=canvas->tTop; jtBottom; j++) { + for (int i=canvas->tLeft; itRight; i++) { + + int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH; + + if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero) + dirty = true; + // make (pl..pr)x(pt..pb) the minimal rect covering all dirtied tiles + if ( i < pl ) pl = i; + if ( i+1 > pr ) pr = i+1; + if ( j < pt ) pt = j; + if ( j+1 > pb ) pb = j+1; } + + canvas->tiles[tile_index] = 0; // undirty this tile } } canvas->need_redraw = FALSE; - sp_canvas_paint_rect (canvas, topaint.x0, topaint.y0, topaint.x1, topaint.y1); + + if ( dirty ) { + NRRectL topaint; + topaint.x0 = MAX (pl*TILE_SIZE, canvas->x0); + topaint.y0 = MAX (pt*TILE_SIZE, canvas->y0); + topaint.x1 = MIN (pr*TILE_SIZE, canvas_x1); + topaint.y1 = MIN (pb*TILE_SIZE, canvas_y1); + if ((topaint.x0 < topaint.x1) && (topaint.y0 < topaint.y1)) { + sp_canvas_paint_rect (canvas, topaint.x0, topaint.y0, topaint.x1, topaint.y1); + } + } return TRUE; } @@ -1950,7 +2165,7 @@ sp_canvas_root (SPCanvas *canvas) * Scrolls canvas to specific position. */ void -sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear) +sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling) { g_return_if_fail (canvas != NULL); g_return_if_fail (SP_IS_CANVAS (canvas)); @@ -1965,27 +2180,14 @@ sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear) 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); + sp_canvas_resize_tiles (canvas, canvas->x0, canvas->y0, canvas->x0+canvas->widget.allocation.width, canvas->y0+canvas->widget.allocation.height); if (!clear) { // scrolling without zoom; redraw only the newly exposed areas if ((dx != 0) || (dy != 0)) { - int width, height; - width = canvas->widget.allocation.width; - height = canvas->widget.allocation.height; + canvas->is_scrolling = is_scrolling; if (GTK_WIDGET_REALIZED (canvas)) { gdk_window_scroll (SP_CANVAS_WINDOW (canvas), -dx, -dy); - gdk_window_process_updates (SP_CANVAS_WINDOW (canvas), TRUE); - } - if (dx < 0) { - sp_canvas_request_redraw (canvas, ix + 0, iy + 0, ix - dx, iy + height); - } else if (dx > 0) { - sp_canvas_request_redraw (canvas, ix + width - dx, iy + 0, ix + width, iy + height); - } - if (dy < 0) { - sp_canvas_request_redraw (canvas, ix + 0, iy + 0, ix + width, iy - dy); - } else if (dy > 0) { - sp_canvas_request_redraw (canvas, ix + 0, iy + height - dy, ix + width, iy + height); } } } else { @@ -2116,7 +2318,7 @@ bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &w } /** - * Return canvas window coordinates as NRRect. + * Return canvas window coordinates as NR::Rect. */ NR::Rect SPCanvas::getViewbox() const { @@ -2128,16 +2330,16 @@ NR::Rect SPCanvas::getViewbox() const inline int sp_canvas_tile_floor(int x) { - return (x&(~31))/32; + return (x & (~(TILE_SIZE - 1))) / TILE_SIZE; } inline int sp_canvas_tile_ceil(int x) { - return ((x+31)&(~31))/32; + return ((x + (TILE_SIZE - 1)) & (~(TILE_SIZE - 1))) / TILE_SIZE; } /** - * Helper that changes tile size for canvas redraw. + * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array */ void sp_canvas_resize_tiles(SPCanvas* canvas,int nl,int nt,int nr,int nb) { @@ -2153,15 +2355,15 @@ void sp_canvas_resize_tiles(SPCanvas* canvas,int nl,int nt,int nr,int nb) 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= canvas->tLeft && i < canvas->tRight && j >= canvas->tTop && j < canvas->tBottom ) { - ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; + ntiles[ind]=canvas->tiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]; // copy from the old tile } else { - ntiles[ind]=0; + ntiles[ind]=0; // newly exposed areas get 0 } } } @@ -2176,7 +2378,7 @@ void sp_canvas_resize_tiles(SPCanvas* canvas,int nl,int nt,int nr,int nb) } /** - * Helper that marks specific canvas rectangle for redraw. + * Helper that marks specific canvas rectangle for redraw by dirtying its tiles */ void sp_canvas_dirty_rect(SPCanvas* canvas,int nl,int nt,int nr,int nb) { @@ -2195,9 +2397,9 @@ void sp_canvas_dirty_rect(SPCanvas* canvas,int nl,int nt,int nr,int nb) canvas->need_redraw = TRUE; - for (int i=tl;itiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH]=1; + for (int i=tl; itiles[(i-canvas->tLeft)+(j-canvas->tTop)*canvas->tileH] = 1; } } }