[gimp] app: fix artifacts caused by delayed tool drawing



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]