X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;ds=sidebyside;f=src%2Fdisplay%2Fsp-canvas.cpp;h=c8de868849c18c44da5f0c2a13a1526b1478646a;hb=42a33ceca3305eeb89f858cfa0a9e635109cf790;hp=1f89e8bae5e51469ff25a0f61554325492ad7b8f;hpb=e84cf433439f4d45c82a1090f84a890c13f059dc;p=inkscape.git diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp index 1f89e8bae..c8de86884 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -33,6 +33,7 @@ #include #include #include +#include "prefs-utils.h" enum { RENDERMODE_NORMAL, @@ -979,7 +980,7 @@ sp_canvas_init (SPCanvas *canvas) canvas->redraw_count = 0; canvas->forced_redraw_count = 0; - canvas->forced_redraw_limit = 0; + canvas->forced_redraw_limit = -1; canvas->slowest_buffer = 0; } @@ -1086,7 +1087,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); @@ -1273,6 +1278,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) { @@ -1560,63 +1568,26 @@ 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; + return 0; int sw, sh; 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 */ @@ -1665,6 +1636,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) { @@ -1672,12 +1656,30 @@ 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); + } + // OPTIMIZATION IDEA: if drawing is really slow (as measured by canvas->slowest // buffer), process some events even BEFORE we do any buffers? // 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. @@ -1686,17 +1688,28 @@ 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, // 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) { + if (ok_to_interrupt && (canvas->forced_redraw_limit != -1)) { ok_to_interrupt = (canvas->forced_redraw_count < canvas->forced_redraw_limit); } @@ -1711,7 +1724,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 } } @@ -1719,11 +1735,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) { + if (canvas->forced_redraw_limit != -1) { canvas->forced_redraw_count++; } do_update (canvas); - return; + return 1; // interrupted } } } @@ -1732,8 +1748,122 @@ 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; - // we've had a full redraw, reset the full redraw counter - if (canvas->forced_redraw_limit) { + 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; } } @@ -1743,6 +1873,8 @@ sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1) */ 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; } @@ -1752,7 +1884,9 @@ sp_canvas_force_full_redraw_after_interruptions(SPCanvas *canvas, unsigned int c */ void sp_canvas_end_forced_full_redraws(SPCanvas *canvas) { - canvas->forced_redraw_limit = 0; + g_return_if_fail(canvas != NULL); + + canvas->forced_redraw_limit = -1; } /**