>From 335ff5f1ca94ba619c2d862e731cae2c428d227e Mon Sep 17 00:00:00 2001 From: Federico Mena Quintero Date: Wed, 22 Aug 2007 15:36:44 -0500 Subject: [PATCH] Avoid tearing while repainting --- ChangeLog | 17 +++ libgnomecanvas/gnome-canvas.c | 259 +++++++++++++++++++++-------------------- 2 files changed, 152 insertions(+), 124 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9bb1d0a..d0991ab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2007-08-22 Federico Mena Quintero + + Avoid tearing while repainting. + + * libgnomecanvas/gnome-canvas.c (REDRAW_QUANTUM_SIZE): Renamed + from IMAGE_{WIDTH, HEIGHT}; use a single value. + (IMAGE_WIDTH_AA, IMAGE_HEIGHT_AA): Removed. + (paint): Don't use different redraw quantum sizes for the + antialiased and non-antialiased case. Extract big rectangles from + the microtile array, turn them into a GdkRegion, and *then* expose + with that region, so that we can repaint without tearing. + (gnome_canvas_paint_rect): Don't use different redraw quantum + sizes for the antialiased and non-antialiased case. + (gnome_canvas_paint_rect): Don't tile the area to paint; instead, draw + the complete rectangle in one pass. The callers already took care of + generating appropriate rectangles for us. + ============================= 2.20.1.1 ====================== 2007-10-20 Sven Herzberg diff --git a/libgnomecanvas/gnome-canvas.c b/libgnomecanvas/gnome-canvas.c index 92e5ad7..5e89bc2 100644 --- a/libgnomecanvas/gnome-canvas.c +++ b/libgnomecanvas/gnome-canvas.c @@ -2904,11 +2904,7 @@ gnome_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event) return FALSE; } -#define IMAGE_WIDTH 512 -#define IMAGE_HEIGHT 512 - -#define IMAGE_WIDTH_AA 256 -#define IMAGE_HEIGHT_AA 64 +#define REDRAW_QUANTUM_SIZE 512 static void gnome_canvas_paint_rect (GnomeCanvas *canvas, gint x0, gint y0, gint x1, gint y1) @@ -2916,9 +2912,7 @@ gnome_canvas_paint_rect (GnomeCanvas *canvas, gint x0, gint y0, gint x1, gint y1 GtkWidget *widget; gint draw_x1, draw_y1; gint draw_x2, draw_y2; - gint xblock, yblock; - guchar *px; - GdkPixmap *pixmap; + gint draw_width, draw_height; g_return_if_fail (!canvas->need_update); @@ -2929,94 +2923,91 @@ gnome_canvas_paint_rect (GnomeCanvas *canvas, gint x0, gint y0, gint x1, gint y1 draw_x2 = MIN (draw_x1 + GTK_WIDGET (canvas)->allocation.width, x1); draw_y2 = MIN (draw_y1 + GTK_WIDGET (canvas)->allocation.height, y1); - /* As we can come from expose, we have to tile here */ - xblock = (canvas->aa) ? IMAGE_WIDTH_AA : IMAGE_WIDTH; - yblock = (canvas->aa) ? IMAGE_HEIGHT_AA : IMAGE_HEIGHT; - - px = NULL; - pixmap = NULL; - - for (y0 = draw_y1; y0 < draw_y2; y0 += yblock) { - y1 = MIN (y0 + yblock, draw_y2); - for (x0 = draw_x1; x0 < draw_x2; x0 += xblock) { - x1 = MIN (x0 + xblock, draw_x2); - - canvas->redraw_x1 = x0; - canvas->redraw_y1 = y0; - canvas->redraw_x2 = x1; - canvas->redraw_y2 = y1; - canvas->draw_xofs = x0; - canvas->draw_yofs = y0; - - if (canvas->aa) { - GnomeCanvasBuf buf; - GdkColor *color; - - if (!px) px = g_new (guchar, IMAGE_WIDTH_AA * IMAGE_HEIGHT_AA * 3); - - buf.buf = px; - buf.buf_rowstride = IMAGE_WIDTH_AA * 3; - buf.rect.x0 = x0; - buf.rect.y0 = y0; - buf.rect.x1 = x1; - buf.rect.y1 = y1; - color = &widget->style->bg[GTK_STATE_NORMAL]; - buf.bg_color = (((color->red & 0xff00) << 8) | (color->green & 0xff00) | (color->blue >> 8)); - buf.is_bg = 1; - buf.is_buf = 0; - - g_signal_emit (G_OBJECT (canvas), canvas_signals[RENDER_BACKGROUND], 0, &buf); - - if (canvas->root->object.flags & GNOME_CANVAS_ITEM_VISIBLE) - (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->render) (canvas->root, &buf); - - if (buf.is_bg) { - gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color); - gdk_draw_rectangle (canvas->layout.bin_window, - canvas->pixmap_gc, - TRUE, - (x0 + canvas->zoom_xofs), - (y0 + canvas->zoom_yofs), - x1 - x0, y1 - y0); - } else { - gdk_draw_rgb_image_dithalign (canvas->layout.bin_window, - canvas->pixmap_gc, - (x0 + canvas->zoom_xofs), - (y0 + canvas->zoom_yofs), - x1 - x0, y1 - y0, - canvas->dither, - buf.buf, - IMAGE_WIDTH_AA * 3, - x0, y0); - } - } else { - if (!pixmap) pixmap = gdk_pixmap_new (canvas->layout.bin_window, IMAGE_WIDTH, IMAGE_HEIGHT, - gtk_widget_get_visual (widget)->depth); - - g_signal_emit (G_OBJECT (canvas), canvas_signals[DRAW_BACKGROUND], 0, pixmap, - x0, y0, x1 - x0, y1 - y0); - - if (canvas->root->object.flags & GNOME_CANVAS_ITEM_VISIBLE) - (* GNOME_CANVAS_ITEM_GET_CLASS ( - canvas->root)->draw) ( - canvas->root, pixmap, - x0, y0, - x1 - x0, y1 - y0); - /* Copy the pixmap to the window and clean up */ - - gdk_draw_pixmap (canvas->layout.bin_window, - canvas->pixmap_gc, - pixmap, - 0, 0, - x0 + canvas->zoom_xofs, - y0 + canvas->zoom_yofs, - x1 - x0, y1 - y0); - } + draw_width = draw_x2 - draw_x1; + draw_height = draw_y2 - draw_y1; + + if (draw_width < 1 || draw_height < 1) + return; + + canvas->redraw_x1 = draw_x1; + canvas->redraw_y1 = draw_y1; + canvas->redraw_x2 = draw_x2; + canvas->redraw_y2 = draw_y2; + canvas->draw_xofs = draw_x1; + canvas->draw_yofs = draw_y1; + + if (canvas->aa) { + GnomeCanvasBuf buf; + guchar *px; + GdkColor *color; + + px = g_new (guchar, draw_width * 3 * draw_height); + + buf.buf = px; + buf.buf_rowstride = draw_width * 3; + buf.rect.x0 = draw_x1; + buf.rect.y0 = draw_y1; + buf.rect.x1 = draw_x2; + buf.rect.y1 = draw_y2; + color = &widget->style->bg[GTK_STATE_NORMAL]; + buf.bg_color = (((color->red & 0xff00) << 8) | (color->green & 0xff00) | (color->blue >> 8)); + buf.is_bg = 1; + buf.is_buf = 0; + + g_signal_emit (G_OBJECT (canvas), canvas_signals[RENDER_BACKGROUND], 0, &buf); + + if (canvas->root->object.flags & GNOME_CANVAS_ITEM_VISIBLE) + (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->render) (canvas->root, &buf); + + if (buf.is_bg) { + gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color); + gdk_draw_rectangle (canvas->layout.bin_window, + canvas->pixmap_gc, + TRUE, + (draw_x1 + canvas->zoom_xofs), + (draw_y1 + canvas->zoom_yofs), + draw_width, draw_height); + } else { + gdk_draw_rgb_image_dithalign (canvas->layout.bin_window, + canvas->pixmap_gc, + (draw_x1 + canvas->zoom_xofs), + (draw_y1 + canvas->zoom_yofs), + draw_width, draw_height, + canvas->dither, + buf.buf, + buf.buf_rowstride, + draw_x1, draw_y1); } - } - if (px) g_free (px); - if (pixmap) gdk_pixmap_unref (pixmap); + g_free (px); + } else { + GdkPixmap *pixmap; + + pixmap = gdk_pixmap_new (canvas->layout.bin_window, + draw_width, draw_height, + gtk_widget_get_visual (widget)->depth); + + g_signal_emit (G_OBJECT (canvas), canvas_signals[DRAW_BACKGROUND], 0, pixmap, + draw_x1, draw_y1, draw_width, draw_height); + + if (canvas->root->object.flags & GNOME_CANVAS_ITEM_VISIBLE) + (* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->draw) ( + canvas->root, pixmap, + draw_x1, draw_y1, + draw_width, draw_height); + + /* Copy the pixmap to the window and clean up */ + + gdk_draw_pixmap (canvas->layout.bin_window, + canvas->pixmap_gc, + pixmap, + 0, 0, + draw_x1 + canvas->zoom_xofs, + draw_y1 + canvas->zoom_yofs, + draw_width, draw_height); + + gdk_pixmap_unref (pixmap); + } } /* Expose handler for the canvas */ @@ -3074,48 +3065,68 @@ paint (GnomeCanvas *canvas) { ArtIRect *rects; gint n_rects, i; + ArtIRect visible_rect; + GdkRegion *region; + GdkEventExpose expose_event; + GdkRectangle region_area; - if (canvas->aa) - rects = art_rect_list_from_uta (canvas->redraw_area, - IMAGE_WIDTH_AA, IMAGE_HEIGHT_AA, - &n_rects); - else - rects = art_rect_list_from_uta (canvas->redraw_area, - IMAGE_WIDTH, IMAGE_HEIGHT, - &n_rects); + /* Extract big rectangles from the microtile array */ + + rects = art_rect_list_from_uta (canvas->redraw_area, + REDRAW_QUANTUM_SIZE, REDRAW_QUANTUM_SIZE, + &n_rects); art_uta_free (canvas->redraw_area); canvas->redraw_area = NULL; canvas->need_redraw = FALSE; - /* Send synthetic expose events */ + /* Turn those rectangles into a GdkRegion for exposing */ + + visible_rect.x0 = canvas->layout.hadjustment->value; + visible_rect.y0 = canvas->layout.vadjustment->value; + visible_rect.x1 = visible_rect.x0 + GTK_WIDGET (canvas)->allocation.width; + visible_rect.y1 = visible_rect.y0 + GTK_WIDGET (canvas)->allocation.height; + + region = gdk_region_new (); + for (i = 0; i < n_rects; i++) { - GdkEventExpose ex; - gint x0, y0, x1, y1; - - x0 = MAX (canvas->layout.hadjustment->value - canvas->zoom_xofs, rects[i].x0); - y0 = MAX (canvas->layout.vadjustment->value - canvas->zoom_yofs, rects[i].y0); - x1 = MIN (x0 + GTK_WIDGET (canvas)->allocation.width, rects[i].x1); - y1 = MIN (y0 + GTK_WIDGET (canvas)->allocation.height, rects[i].y1); - - if ((x0 < x1) && (y0 < y1)) { - /* Here we are - whatever type is canvas, we have to send synthetic expose to layout (Lauris) */ - ex.type = GDK_EXPOSE; - ex.window = canvas->layout.bin_window; - ex.send_event = TRUE; - ex.area.x = x0 + canvas->zoom_xofs; - ex.area.y = y0 + canvas->zoom_yofs; - ex.area.width = x1 - x0; - ex.area.height = y1 - y0; - ex.region = gdk_region_rectangle (&ex.area); - ex.count = 0; - gtk_widget_send_expose (GTK_WIDGET (canvas), (GdkEvent *) &ex); - gdk_region_destroy (ex.region); + ArtIRect clipped; + + art_irect_intersect (&clipped, &visible_rect, rects + i); + if (!art_irect_empty (&clipped)) { + GdkRectangle gdkrect; + + gdkrect.x = clipped.x0; + gdkrect.y = clipped.y0; + gdkrect.width = clipped.x1 - clipped.x0; + gdkrect.height = clipped.y1 - clipped.y0; + + gdk_region_union_with_rect (region, &gdkrect); } } art_free (rects); + /* Expose! */ + + gdk_region_get_clipbox (region, ®ion_area); + + expose_event.type = GDK_EXPOSE; + expose_event.window = canvas->layout.bin_window; + expose_event.send_event = TRUE; + expose_event.area.x = region_area.x; + expose_event.area.y = region_area.y; + expose_event.area.width = region_area.width; + expose_event.area.height = region_area.height; + expose_event.region = region; + expose_event.count = 0; + + gdk_window_begin_paint_region (canvas->layout.bin_window, region); + gtk_widget_send_expose (GTK_WIDGET (canvas), (GdkEvent *) &expose_event); + gdk_window_end_paint (canvas->layout.bin_window); + + gdk_region_destroy (region); + canvas->redraw_x1 = 0; canvas->redraw_y1 = 0; canvas->redraw_x2 = 0; -- 1.5.2.4