[gimp] app: fix artifacts caused by delayed tool drawing
- From: Michael Natterer <mitch src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] app: fix artifacts caused by delayed tool drawing
- Date: Sun, 2 May 2010 10:13:13 +0000 (UTC)
commit 84cf53d90837227899a5e590781677fdbf4b4fe6
Author: Michael Natterer <mitch gimp org>
Date: Sun May 2 12:12:48 2010 +0200
app: fix artifacts caused by delayed tool drawing
and as a "side effect", speed up rendering the image significantly:
- disable double buffering on the canvas widget.
- implement background clearing ourselves (needed after turning off
double buffering).
- remove any fiddling with clipping regions on the tool drawing GCs
and pull the pause/resume code out of the actual image expose
function.
- if there are overlay widgets on the canvas, implement double
buffering manually to aviod flicker, but do it in a way that keeps
pausing/resuming the active tool *outside* the double buffered
drawing.
app/display/gimpcanvas.c | 1 +
app/display/gimpdisplayshell-callbacks.c | 135 ++++++++++++++++++++++--------
app/display/gimpdisplayshell-callbacks.h | 4 +
app/display/gimpdisplayshell.c | 3 +
4 files changed, 107 insertions(+), 36 deletions(-)
---
diff --git a/app/display/gimpcanvas.c b/app/display/gimpcanvas.c
index 9b27a85..5d3bf02 100644
--- a/app/display/gimpcanvas.c
+++ b/app/display/gimpcanvas.c
@@ -187,6 +187,7 @@ gimp_canvas_init (GimpCanvas *canvas)
GtkWidget *widget = GTK_WIDGET (canvas);
gint i;
+ gtk_widget_set_double_buffered (widget, FALSE);
gtk_widget_set_can_focus (widget, TRUE);
gtk_widget_add_events (widget, GIMP_CANVAS_EVENT_MASK);
gtk_widget_set_extension_events (widget, GDK_EXTENSION_EVENTS_ALL);
diff --git a/app/display/gimpdisplayshell-callbacks.c b/app/display/gimpdisplayshell-callbacks.c
index 57e1844..4b84a02 100644
--- a/app/display/gimpdisplayshell-callbacks.c
+++ b/app/display/gimpdisplayshell-callbacks.c
@@ -384,6 +384,14 @@ gimp_display_shell_canvas_expose (GtkWidget *widget,
{
if (gimp_display_get_image (shell->display))
{
+ gimp_display_shell_pause (shell);
+
+ /* only double-buffer if there are overlay children, or
+ * they will flicker badly
+ */
+ if (GIMP_OVERLAY_BOX (widget)->children)
+ gdk_window_begin_paint_region (eevent->window, eevent->region);
+
gimp_display_shell_canvas_expose_image (shell, eevent);
}
else
@@ -395,6 +403,31 @@ gimp_display_shell_canvas_expose (GtkWidget *widget,
return FALSE;
}
+gboolean
+gimp_display_shell_canvas_expose_after (GtkWidget *widget,
+ GdkEventExpose *eevent,
+ GimpDisplayShell *shell)
+{
+ /* are we in destruction? */
+ if (! shell->display || ! gimp_display_get_shell (shell->display))
+ return TRUE;
+
+ /* ignore events on overlays */
+ if (eevent->window == gtk_widget_get_window (widget))
+ {
+ if (gimp_display_get_image (shell->display))
+ {
+ /* see above */
+ if (GIMP_OVERLAY_BOX (widget)->children)
+ gdk_window_end_paint (eevent->window);
+
+ gimp_display_shell_resume (shell);
+ }
+ }
+
+ return FALSE;
+}
+
static void
gimp_display_shell_check_device_cursor (GimpDisplayShell *shell)
{
@@ -2172,55 +2205,71 @@ static void
gimp_display_shell_canvas_expose_image (GimpDisplayShell *shell,
GdkEventExpose *eevent)
{
- GdkRegion *region = NULL;
+ GdkRegion *clear_region;
+ GdkRegion *image_region;
+ GdkRectangle image_rect;
GdkRectangle *rects;
gint n_rects;
gint i;
- /* If the call to gimp_display_shell_pause() would cause a redraw,
- * we need to make sure that no XOR drawing happens on areas that
- * have already been cleared by the windowing system.
+ /* first, clear the exposed part of the region that is outside the
+ * image, which is the exposed region minus the image rectangle
*/
- if (shell->paused_count == 0)
- {
- GdkRectangle area;
- area.x = 0;
- area.y = 0;
- area.width = shell->disp_width;
- area.height = shell->disp_height;
+ clear_region = gdk_region_copy (eevent->region);
+
+ image_rect.x = - shell->offset_x;
+ image_rect.y = - shell->offset_y;
+ gimp_display_shell_draw_get_scaled_image_size (shell,
+ &image_rect.width,
+ &image_rect.height);
+ image_region = gdk_region_rectangle (&image_rect);
- region = gdk_region_rectangle (&area);
+ gdk_region_subtract (clear_region, image_region);
+ gdk_region_destroy (image_region);
- gdk_region_subtract (region, eevent->region);
+ if (! gdk_region_empty (clear_region))
+ {
+ gdk_region_get_rectangles (clear_region, &rects, &n_rects);
+
+ for (i = 0; i < n_rects; i++)
+ gdk_window_clear_area (gtk_widget_get_window (shell->canvas),
+ rects[i].x,
+ rects[i].y,
+ rects[i].width,
+ rects[i].height);
- gimp_canvas_set_clip_region (GIMP_CANVAS (shell->canvas),
- GIMP_CANVAS_STYLE_XOR, region);
- gimp_canvas_set_clip_region (GIMP_CANVAS (shell->canvas),
- GIMP_CANVAS_STYLE_XOR_DASHED, region);
+ g_free (rects);
}
- gimp_display_shell_pause (shell);
+ /* then, draw the exposed part of the region that is inside the
+ * image, which is the exposed region minus the region used for
+ * clearing above
+ */
+
+ image_region = gdk_region_copy (eevent->region);
+
+ gdk_region_subtract (image_region, clear_region);
+ gdk_region_destroy (clear_region);
- if (region)
+ if (! gdk_region_empty (image_region))
{
- gimp_canvas_set_clip_region (GIMP_CANVAS (shell->canvas),
- GIMP_CANVAS_STYLE_XOR, NULL);
- gimp_canvas_set_clip_region (GIMP_CANVAS (shell->canvas),
- GIMP_CANVAS_STYLE_XOR_DASHED, NULL);
- gdk_region_destroy (region);
- }
+ gdk_region_get_rectangles (image_region, &rects, &n_rects);
- gdk_region_get_rectangles (eevent->region, &rects, &n_rects);
+ for (i = 0; i < n_rects; i++)
+ gimp_display_shell_draw_area (shell,
+ rects[i].x,
+ rects[i].y,
+ rects[i].width,
+ rects[i].height);
- for (i = 0; i < n_rects; i++)
- gimp_display_shell_draw_area (shell,
- rects[i].x,
- rects[i].y,
- rects[i].width,
- rects[i].height);
+ g_free (rects);
+ }
- g_free (rects);
+ gdk_region_destroy (image_region);
+
+ /* finally, draw all the remaining image window stuff on top
+ */
/* draw the transform tool preview */
gimp_display_shell_preview_transform (shell);
@@ -2239,15 +2288,29 @@ gimp_display_shell_canvas_expose_image (GimpDisplayShell *shell,
/* restart (and recalculate) the selection boundaries */
gimp_display_shell_selection_control (shell, GIMP_SELECTION_ON);
-
- gimp_display_shell_resume (shell);
}
static void
gimp_display_shell_canvas_expose_drop_zone (GimpDisplayShell *shell,
GdkEventExpose *eevent)
{
- cairo_t *cr;
+ cairo_t *cr;
+ GdkRectangle *rects;
+ gint n_rects;
+ gint i;
+
+ gdk_region_get_rectangles (eevent->region, &rects, &n_rects);
+
+ for (i = 0; i < n_rects; i++)
+ {
+ gdk_window_clear_area (gtk_widget_get_window (shell->canvas),
+ rects[i].x,
+ rects[i].y,
+ rects[i].width,
+ rects[i].height);
+ }
+
+ g_free (rects);
cr = gdk_cairo_create (gtk_widget_get_window (shell->canvas));
gdk_cairo_region (cr, eevent->region);
diff --git a/app/display/gimpdisplayshell-callbacks.h b/app/display/gimpdisplayshell-callbacks.h
index a10691b..9c36e2c 100644
--- a/app/display/gimpdisplayshell-callbacks.h
+++ b/app/display/gimpdisplayshell-callbacks.h
@@ -31,6 +31,9 @@ void gimp_display_shell_canvas_size_allocate (GtkWidget *widget,
gboolean gimp_display_shell_canvas_expose (GtkWidget *widget,
GdkEventExpose *eevent,
GimpDisplayShell *shell);
+gboolean gimp_display_shell_canvas_expose_after (GtkWidget *widget,
+ GdkEventExpose *eevent,
+ GimpDisplayShell *shell);
gboolean gimp_display_shell_canvas_tool_events (GtkWidget *widget,
GdkEvent *event,
GimpDisplayShell *shell);
@@ -55,6 +58,7 @@ void gimp_display_shell_quick_mask_toggled (GtkWidget *widget,
gboolean gimp_display_shell_nav_button_press (GtkWidget *widget,
GdkEventButton *bevent,
GimpDisplayShell *shell);
+
gboolean gimp_display_shell_flush_event_queue (GimpDisplayShell *shell);
diff --git a/app/display/gimpdisplayshell.c b/app/display/gimpdisplayshell.c
index cca9567..227e5b3 100644
--- a/app/display/gimpdisplayshell.c
+++ b/app/display/gimpdisplayshell.c
@@ -554,6 +554,9 @@ gimp_display_shell_constructor (GType type,
g_signal_connect (shell->canvas, "expose-event",
G_CALLBACK (gimp_display_shell_canvas_expose),
shell);
+ g_signal_connect_after (shell->canvas, "expose-event",
+ G_CALLBACK (gimp_display_shell_canvas_expose_after),
+ shell);
g_signal_connect (shell->canvas, "enter-notify-event",
G_CALLBACK (gimp_display_shell_canvas_tool_events),
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]