[gimp] app: make display update much faster again



commit 9aa6aa1f040e6476a1e047b09d6c0403cdb8f344
Author: Michael Natterer <mitch gimp org>
Date:   Mon Jul 15 23:24:35 2019 +0200

    app: make display update much faster again
    
    Introduce a render cache that keeps the result of scaling, color
    management, display filters and shell mask (for tools like fuzzy
    select).
    
    Change gimpdisplayshell-render.[ch] to only render to the cache and
    manage a cairo region of the cache's valid area. Call cache
    invalidation functions form various places. Change the API of all
    render functions to be in display coordinates.
    
    Also get rid of gimpdisplayxfer.[ch] because we now have a
    canvas-sized cairo surface which is a surface similar to the
    destination surface.

 app/display/Makefile.am                  |   2 -
 app/display/gimpdisplay.c                |   3 +
 app/display/gimpdisplayshell-callbacks.c |  34 +++-
 app/display/gimpdisplayshell-draw.c      |  53 ++----
 app/display/gimpdisplayshell-filter.c    |   1 +
 app/display/gimpdisplayshell-handlers.c  |   4 +
 app/display/gimpdisplayshell-profile.c   |   5 +-
 app/display/gimpdisplayshell-render.c    | 258 +++++++++++++++++++-------
 app/display/gimpdisplayshell-render.h    |  39 +++-
 app/display/gimpdisplayshell-rotate.c    |   3 +
 app/display/gimpdisplayshell-scale.c     |   2 +
 app/display/gimpdisplayshell-scroll.c    |  41 +++++
 app/display/gimpdisplayshell.c           |  12 +-
 app/display/gimpdisplayshell.h           |   8 +-
 app/display/gimpdisplayxfer.c            | 300 -------------------------------
 app/display/gimpdisplayxfer.h            |  37 ----
 16 files changed, 346 insertions(+), 456 deletions(-)
---
diff --git a/app/display/Makefile.am b/app/display/Makefile.am
index eda865265d..4429f84478 100644
--- a/app/display/Makefile.am
+++ b/app/display/Makefile.am
@@ -156,8 +156,6 @@ libappdisplay_a_sources = \
        gimpdisplayshell-transform.h            \
        gimpdisplayshell-utils.c                \
        gimpdisplayshell-utils.h                \
-       gimpdisplayxfer.c                       \
-       gimpdisplayxfer.h                       \
        gimpimagewindow.c                       \
        gimpimagewindow.h                       \
        gimpmotionbuffer.c                      \
diff --git a/app/display/gimpdisplay.c b/app/display/gimpdisplay.c
index 709f95f09a..1c2914c343 100644
--- a/app/display/gimpdisplay.c
+++ b/app/display/gimpdisplay.c
@@ -44,6 +44,7 @@
 #include "gimpdisplayshell-expose.h"
 #include "gimpdisplayshell-handlers.h"
 #include "gimpdisplayshell-icon.h"
+#include "gimpdisplayshell-render.h"
 #include "gimpdisplayshell-transform.h"
 #include "gimpimagewindow.h"
 
@@ -897,4 +898,6 @@ gimp_display_paint_area (GimpDisplay *display,
   y2 = ceil  ((gdouble) y2 / PAINT_AREA_CHUNK_HEIGHT) * PAINT_AREA_CHUNK_HEIGHT;
 
   gimp_display_shell_expose_area (shell, x1, y1, x2 - x1, y2 - y1);
+
+  gimp_display_shell_render_invalidate_area (shell, x1, y1, x2 - x1, y2 - y1);
 }
diff --git a/app/display/gimpdisplayshell-callbacks.c b/app/display/gimpdisplayshell-callbacks.c
index e567563c6a..7b2e281a29 100644
--- a/app/display/gimpdisplayshell-callbacks.c
+++ b/app/display/gimpdisplayshell-callbacks.c
@@ -38,12 +38,12 @@
 #include "gimpdisplayshell-appearance.h"
 #include "gimpdisplayshell-callbacks.h"
 #include "gimpdisplayshell-draw.h"
+#include "gimpdisplayshell-render.h"
 #include "gimpdisplayshell-scale.h"
 #include "gimpdisplayshell-scroll.h"
 #include "gimpdisplayshell-scrollbars.h"
 #include "gimpdisplayshell-selection.h"
 #include "gimpdisplayshell-title.h"
-#include "gimpdisplayxfer.h"
 #include "gimpimagewindow.h"
 #include "gimpnavigationeditor.h"
 
@@ -80,9 +80,10 @@ void
 gimp_display_shell_canvas_realize (GtkWidget        *canvas,
                                    GimpDisplayShell *shell)
 {
-  GimpCanvasPaddingMode padding_mode;
-  GimpRGB               padding_color;
-  GtkAllocation         allocation;
+  GimpCanvasPaddingMode  padding_mode;
+  GimpRGB                padding_color;
+  GtkAllocation          allocation;
+  const gchar           *env;
 
   gtk_widget_grab_focus (canvas);
 
@@ -115,7 +116,27 @@ gimp_display_shell_canvas_realize (GtkWidget        *canvas,
   /*  allow shrinking  */
   gtk_widget_set_size_request (GTK_WIDGET (shell), 0, 0);
 
-  shell->xfer = gimp_display_xfer_realize (GTK_WIDGET(shell));
+  shell->render_buf_width  = 256;
+  shell->render_buf_height = 256;
+
+  env = g_getenv ("GIMP_DISPLAY_RENDER_BUF_SIZE");
+
+  if (env)
+    {
+      gint width  = atoi (env);
+      gint height = width;
+
+      env = strchr (env, 'x');
+      if (env)
+        height = atoi (env + 1);
+
+      if (width  > 0 && width  <= 8192 &&
+          height > 0 && height <= 8192)
+        {
+          shell->render_buf_width  = width;
+          shell->render_buf_height = height;
+        }
+    }
 }
 
 static gboolean
@@ -130,6 +151,9 @@ gimp_display_shell_canvas_tick (GtkWidget        *widget,
   if ((shell->disp_width  != allocation.width) ||
       (shell->disp_height != allocation.height))
     {
+      g_clear_pointer (&shell->render_cache, cairo_surface_destroy);
+      gimp_display_shell_render_invalidate_full (shell);
+
       if (shell->zoom_on_resize   &&
           shell->disp_width  > 64 &&
           shell->disp_height > 64 &&
diff --git a/app/display/gimpdisplayshell-draw.c b/app/display/gimpdisplayshell-draw.c
index 654828016c..ccc14005eb 100644
--- a/app/display/gimpdisplayshell-draw.c
+++ b/app/display/gimpdisplayshell-draw.c
@@ -32,17 +32,10 @@
 
 #include "gimpcanvas.h"
 #include "gimpcanvas-style.h"
-#include "gimpcanvaspath.h"
 #include "gimpdisplay.h"
 #include "gimpdisplayshell.h"
 #include "gimpdisplayshell-draw.h"
 #include "gimpdisplayshell-render.h"
-#include "gimpdisplayshell-scale.h"
-#include "gimpdisplayshell-transform.h"
-#include "gimpdisplayxfer.h"
-
-
-#define GIMP_DISPLAY_RENDER_ENABLE_SCALING 1
 
 
 /*  public functions  */
@@ -157,8 +150,8 @@ gimp_display_shell_draw_image (GimpDisplayShell *shell,
    *  chunk size as necessary, to accommodate for the display
    *  transform and window scale factor.
    */
-  chunk_width  = GIMP_DISPLAY_RENDER_BUF_WIDTH;
-  chunk_height = GIMP_DISPLAY_RENDER_BUF_HEIGHT;
+  chunk_width  = shell->render_buf_width;
+  chunk_height = shell->render_buf_height;
 
 #ifdef GIMP_DISPLAY_RENDER_ENABLE_SCALING
   /* multiply the image scale-factor by the window scale-factor, and divide
@@ -197,23 +190,8 @@ gimp_display_shell_draw_image (GimpDisplayShell *shell,
 
       for (c = 0; c < n_cols; c++)
         {
-          gint    x1 = x + (2 *  c      * w + n_cols) / (2 * n_cols);
-          gint    x2 = x + (2 * (c + 1) * w + n_cols) / (2 * n_cols);
-          gdouble ix1, iy1;
-          gdouble ix2, iy2;
-          gint    ix, iy;
-          gint    iw, ih;
-
-          /* map chunk from screen space to scaled image space */
-          gimp_display_shell_untransform_bounds_with_scale (
-            shell, scale,
-            x1,   y1,   x2,   y2,
-            &ix1, &iy1, &ix2, &iy2);
-
-          ix = floor (ix1);
-          iy = floor (iy1);
-          iw = ceil  (ix2) - ix;
-          ih = ceil  (iy2) - iy;
+          gint x1 = x + (2 *  c      * w + n_cols) / (2 * n_cols);
+          gint x2 = x + (2 * (c + 1) * w + n_cols) / (2 * n_cols);
 
           cairo_save (cr);
 
@@ -221,14 +199,21 @@ gimp_display_shell_draw_image (GimpDisplayShell *shell,
           cairo_rectangle (cr, x1, y1, x2 - x1, y2 - y1);
           cairo_clip (cr);
 
-          /* transform to scaled image space, and apply uneven scaling */
-          if (shell->rotate_transform)
-            cairo_transform (cr, shell->rotate_transform);
-          cairo_translate (cr, -shell->offset_x, -shell->offset_y);
-          cairo_scale (cr, shell->scale_x / scale, shell->scale_y / scale);
-
-          /* render image */
-          gimp_display_shell_render (shell, cr, ix, iy, iw, ih, scale);
+          if (! gimp_display_shell_render_is_valid (shell,
+                                                    x1, y1, x2 - x1, y2 - y1))
+            {
+              /* render image to the render cache */
+              gimp_display_shell_render (shell, cr,
+                                         x1, y1, x2 - x1, y2 - y1,
+                                         scale);
+
+              gimp_display_shell_render_validate_area (shell,
+                                                       x1, y1, x2 - x1, y2 - y1);
+            }
+
+          /* render from the render cache to screen */
+          cairo_set_source_surface (cr, shell->render_cache, 0, 0);
+          cairo_paint (cr);
 
           cairo_restore (cr);
 
diff --git a/app/display/gimpdisplayshell-filter.c b/app/display/gimpdisplayshell-filter.c
index 3992238fb9..ad482820aa 100644
--- a/app/display/gimpdisplayshell-filter.c
+++ b/app/display/gimpdisplayshell-filter.c
@@ -139,6 +139,7 @@ gimp_display_shell_filter_changed_idle (gpointer data)
 
   gimp_display_shell_profile_update (shell);
   gimp_display_shell_expose_full (shell);
+  gimp_display_shell_render_invalidate_full (shell);
 
   shell->filter_idle_id = 0;
 
diff --git a/app/display/gimpdisplayshell-handlers.c b/app/display/gimpdisplayshell-handlers.c
index 9d7be6f76a..d3bac52f1c 100644
--- a/app/display/gimpdisplayshell-handlers.c
+++ b/app/display/gimpdisplayshell-handlers.c
@@ -61,6 +61,7 @@
 #include "gimpdisplayshell-handlers.h"
 #include "gimpdisplayshell-icon.h"
 #include "gimpdisplayshell-profile.h"
+#include "gimpdisplayshell-render.h"
 #include "gimpdisplayshell-rulers.h"
 #include "gimpdisplayshell-scale.h"
 #include "gimpdisplayshell-scroll.h"
@@ -840,6 +841,7 @@ gimp_display_shell_size_changed_detailed_handler (GimpImage        *image,
       gimp_display_shell_scroll_clamp_and_update (shell);
 
       gimp_display_shell_expose_full (shell);
+      gimp_display_shell_render_invalidate_full (shell);
     }
 }
 
@@ -1048,6 +1050,7 @@ gimp_display_shell_monitor_res_notify_handler (GObject          *config,
       gimp_display_shell_scaled (shell);
 
       gimp_display_shell_expose_full (shell);
+      gimp_display_shell_render_invalidate_full (shell);
     }
 }
 
@@ -1121,6 +1124,7 @@ gimp_display_shell_quality_notify_handler (GObject          *config,
                                            GimpDisplayShell *shell)
 {
   gimp_display_shell_expose_full (shell);
+  gimp_display_shell_render_invalidate_full (shell);
 }
 
 static void
diff --git a/app/display/gimpdisplayshell-profile.c b/app/display/gimpdisplayshell-profile.c
index 57f290a04a..df6c4b144b 100644
--- a/app/display/gimpdisplayshell-profile.c
+++ b/app/display/gimpdisplayshell-profile.c
@@ -40,7 +40,6 @@
 #include "gimpdisplayshell-actions.h"
 #include "gimpdisplayshell-filter.h"
 #include "gimpdisplayshell-profile.h"
-#include "gimpdisplayxfer.h"
 
 #include "gimp-intl.h"
 
@@ -159,8 +158,8 @@ gimp_display_shell_profile_update (GimpDisplayShell *shell)
 
   if (shell->filter_transform || shell->profile_transform)
     {
-      gint w = GIMP_DISPLAY_RENDER_BUF_WIDTH;
-      gint h = GIMP_DISPLAY_RENDER_BUF_HEIGHT;
+      gint w = shell->render_buf_width;
+      gint h = shell->render_buf_height;
 
       shell->profile_data =
         gegl_malloc (w * h * babl_format_get_bytes_per_pixel (src_format));
diff --git a/app/display/gimpdisplayshell-render.c b/app/display/gimpdisplayshell-render.c
index 0470d7b73f..7d0ea1af4a 100644
--- a/app/display/gimpdisplayshell-render.c
+++ b/app/display/gimpdisplayshell-render.c
@@ -28,9 +28,6 @@
 
 #include "config/gimpdisplayconfig.h"
 
-#include "gegl/gimp-gegl-utils.h"
-
-#include "core/gimpdrawable.h"
 #include "core/gimpimage.h"
 #include "core/gimppickable.h"
 #include "core/gimpprojectable.h"
@@ -41,38 +38,129 @@
 #include "gimpdisplayshell-filter.h"
 #include "gimpdisplayshell-profile.h"
 #include "gimpdisplayshell-render.h"
-#include "gimpdisplayshell-scroll.h"
-#include "gimpdisplayxfer.h"
 
 
+void
+gimp_display_shell_render_invalidate_full (GimpDisplayShell *shell)
+{
+  g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
+
+  g_clear_pointer (&shell->render_cache_valid, cairo_region_destroy);
+}
+
+void
+gimp_display_shell_render_invalidate_area (GimpDisplayShell *shell,
+                                           gint              x,
+                                           gint              y,
+                                           gint              width,
+                                           gint              height)
+{
+  g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
+
+  if (shell->render_cache_valid)
+    {
+      cairo_rectangle_int_t rect;
+
+      rect.x      = x;
+      rect.y      = y;
+      rect.width  = width;
+      rect.height = height;
+
+      cairo_region_subtract_rectangle (shell->render_cache_valid, &rect);
+    }
+}
+
+void
+gimp_display_shell_render_validate_area (GimpDisplayShell *shell,
+                                         gint              x,
+                                         gint              y,
+                                         gint              width,
+                                         gint              height)
+{
+  g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
+
+  if (shell->render_cache_valid)
+    {
+      cairo_rectangle_int_t rect;
+
+      rect.x      = x;
+      rect.y      = y;
+      rect.width  = width;
+      rect.height = height;
+
+      cairo_region_union_rectangle (shell->render_cache_valid, &rect);
+    }
+}
+
+gboolean
+gimp_display_shell_render_is_valid (GimpDisplayShell *shell,
+                                    gint              x,
+                                    gint              y,
+                                    gint              width,
+                                    gint              height)
+{
+  if (shell->render_cache_valid)
+    {
+      cairo_rectangle_int_t  rect;
+      cairo_region_overlap_t overlap;
+
+      rect.x      = x;
+      rect.y      = y;
+      rect.width  = width;
+      rect.height = height;
+
+      overlap = cairo_region_contains_rectangle (shell->render_cache_valid,
+                                                 &rect);
+
+      return (overlap == CAIRO_REGION_OVERLAP_IN);
+    }
+
+  return FALSE;
+}
+
 void
 gimp_display_shell_render (GimpDisplayShell *shell,
                            cairo_t          *cr,
-                           gint              x,
-                           gint              y,
-                           gint              w,
-                           gint              h,
+                           gint              tx,
+                           gint              ty,
+                           gint              twidth,
+                           gint              theight,
                            gdouble           scale)
 {
-  GimpImage       *image;
-  GeglBuffer      *buffer;
+  GimpImage  *image;
+  GeglBuffer *buffer;
 #ifdef USE_NODE_BLIT
-  GeglNode        *node;
+  GeglNode   *node;
 #endif
-  cairo_surface_t *xfer;
-  gint             xfer_src_x;
-  gint             xfer_src_y;
-  gint             mask_src_x = 0;
-  gint             mask_src_y = 0;
-  gint             cairo_stride;
-  guchar          *cairo_data;
+  cairo_t    *my_cr;
+  gint        cairo_stride;
+  guchar     *cairo_data;
+  gdouble     x1, y1;
+  gdouble     x2, y2;
+  gint        x;
+  gint        y;
+  gint        width;
+  gint        height;
 
   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
   g_return_if_fail (cr != NULL);
-  g_return_if_fail (w > 0 && w <= GIMP_DISPLAY_RENDER_BUF_WIDTH);
-  g_return_if_fail (h > 0 && h <= GIMP_DISPLAY_RENDER_BUF_HEIGHT);
   g_return_if_fail (scale > 0.0);
 
+  /* map chunk from screen space to scaled image space */
+  gimp_display_shell_untransform_bounds_with_scale (shell, scale,
+                                                    tx, ty,
+                                                    tx + twidth, ty + theight,
+                                                    &x1, &y1,
+                                                    &x2, &y2);
+
+  x      = floor (x1);
+  y      = floor (y1);
+  width  = ceil  (x2) - x;
+  height = ceil  (y2) - y;
+
+  g_return_if_fail (width  > 0 && width  <= shell->render_buf_width);
+  g_return_if_fail (height > 0 && height <= shell->render_buf_height);
+
   image  = gimp_display_get_image (shell->display);
   buffer = gimp_pickable_get_buffer (GIMP_PICKABLE (image));
 #ifdef USE_NODE_BLIT
@@ -81,12 +169,45 @@ gimp_display_shell_render (GimpDisplayShell *shell,
   gimp_projectable_begin_render (GIMP_PROJECTABLE (image));
 #endif
 
-  xfer = gimp_display_xfer_get_surface (shell->xfer, w, h,
-                                        &xfer_src_x, &xfer_src_y);
+  if (! shell->render_surface)
+    {
+      shell->render_surface =
+        cairo_surface_create_similar_image (cairo_get_target (cr),
+                                            CAIRO_FORMAT_ARGB32,
+                                            shell->render_buf_width,
+                                            shell->render_buf_height);
+    }
 
-  cairo_stride = cairo_image_surface_get_stride (xfer);
-  cairo_data   = cairo_image_surface_get_data (xfer) +
-                 xfer_src_y * cairo_stride + xfer_src_x * 4;
+  cairo_surface_flush (shell->render_surface);
+
+  if (! shell->render_cache)
+    {
+      shell->render_cache =
+        cairo_surface_create_similar_image (cairo_get_target (cr),
+                                            CAIRO_FORMAT_ARGB32,
+                                            shell->disp_width,
+                                            shell->disp_height);
+    }
+
+  if (! shell->render_cache_valid)
+    {
+      shell->render_cache_valid = cairo_region_create ();
+    }
+
+  my_cr = cairo_create (shell->render_cache);
+
+  /* clip to chunk bounds, in screen space */
+  cairo_rectangle (my_cr, tx, ty, twidth, theight);
+  cairo_clip (my_cr);
+
+  /* transform to scaled image space, and apply uneven scaling */
+  if (shell->rotate_transform)
+    cairo_transform (my_cr, shell->rotate_transform);
+  cairo_translate (my_cr, -shell->offset_x, -shell->offset_y);
+  cairo_scale (my_cr, shell->scale_x / scale, shell->scale_y / scale);
+
+  cairo_stride = cairo_image_surface_get_stride (shell->render_surface);
+  cairo_data   = cairo_image_surface_get_data (shell->render_surface);
 
   if (shell->profile_transform ||
       gimp_display_shell_has_filter (shell))
@@ -105,8 +226,8 @@ gimp_display_shell_render (GimpDisplayShell *shell,
       if ((gimp_display_shell_has_filter (shell) || ! can_convert_to_u8) &&
           ! shell->filter_buffer)
         {
-          gint fw = GIMP_DISPLAY_RENDER_BUF_WIDTH;
-          gint fh = GIMP_DISPLAY_RENDER_BUF_HEIGHT;
+          gint fw = shell->render_buf_width;
+          gint fh = shell->render_buf_height;
 
           shell->filter_data =
             gegl_malloc (fw * fh *
@@ -131,13 +252,13 @@ gimp_display_shell_render (GimpDisplayShell *shell,
            */
 #ifndef USE_NODE_BLIT
           gegl_buffer_get (buffer,
-                           GEGL_RECTANGLE (x, y, w, h), scale,
+                           GEGL_RECTANGLE (x, y, width, height), scale,
                            gimp_projectable_get_format (GIMP_PROJECTABLE (image)),
                            shell->profile_data, shell->profile_stride,
                            GEGL_ABYSS_CLAMP);
 #else
           gegl_node_blit (node,
-                          scale, GEGL_RECTANGLE (x, y, w, h),
+                          scale, GEGL_RECTANGLE (x, y, width, height),
                           gimp_projectable_get_format (GIMP_PROJECTABLE (image)),
                           shell->profile_data, shell->profile_stride,
                           GEGL_BLIT_CACHE);
@@ -149,13 +270,13 @@ gimp_display_shell_render (GimpDisplayShell *shell,
            */
 #ifndef USE_NODE_BLIT
           gegl_buffer_get (buffer,
-                           GEGL_RECTANGLE (x, y, w, h), scale,
+                           GEGL_RECTANGLE (x, y, width, height), scale,
                            shell->filter_format,
                            shell->filter_data, shell->filter_stride,
                            GEGL_ABYSS_CLAMP);
 #else
           gegl_node_blit (node,
-                          scale, GEGL_RECTANGLE (x, y, w, h),
+                          scale, GEGL_RECTANGLE (x, y, width, height),
                           shell->filter_format,
                           shell->filter_data, shell->filter_stride,
                           GEGL_BLIT_CACHE);
@@ -169,9 +290,11 @@ gimp_display_shell_render (GimpDisplayShell *shell,
         {
           gimp_color_transform_process_buffer (shell->filter_transform,
                                                shell->profile_buffer,
-                                               GEGL_RECTANGLE (0, 0, w, h),
+                                               GEGL_RECTANGLE (0, 0,
+                                                               width, height),
                                                shell->filter_buffer,
-                                               GEGL_RECTANGLE (0, 0, w, h));
+                                               GEGL_RECTANGLE (0, 0,
+                                                               width, height));
         }
 
       /*  if there are filters, apply them
@@ -194,7 +317,8 @@ gimp_display_shell_render (GimpDisplayShell *shell,
            */
           gimp_color_display_stack_convert_buffer (shell->filter_stack,
                                                    filter_buffer,
-                                                   GEGL_RECTANGLE (x, y, w, h));
+                                                   GEGL_RECTANGLE (x, y,
+                                                                   width, height));
 
           g_object_unref (filter_buffer);
         }
@@ -210,9 +334,11 @@ gimp_display_shell_render (GimpDisplayShell *shell,
                */
               gimp_color_transform_process_buffer (shell->profile_transform,
                                                    shell->filter_buffer,
-                                                   GEGL_RECTANGLE (0, 0, w, h),
+                                                   GEGL_RECTANGLE (0, 0,
+                                                                   width, height),
                                                    shell->filter_buffer,
-                                                   GEGL_RECTANGLE (0, 0, w, h));
+                                                   GEGL_RECTANGLE (0, 0,
+                                                                   width, height));
             }
           else if (! can_convert_to_u8)
             {
@@ -221,16 +347,19 @@ gimp_display_shell_render (GimpDisplayShell *shell,
                */
               gimp_color_transform_process_buffer (shell->profile_transform,
                                                    shell->profile_buffer,
-                                                   GEGL_RECTANGLE (0, 0, w, h),
+                                                   GEGL_RECTANGLE (0, 0,
+                                                                   width, height),
                                                    shell->filter_buffer,
-                                                   GEGL_RECTANGLE (0, 0, w, h));
+                                                   GEGL_RECTANGLE (0, 0,
+                                                                   width, height));
             }
           else
             {
               GeglBuffer *buffer =
                 gegl_buffer_linear_new_from_data (cairo_data,
                                                   babl_format ("cairo-ARGB32"),
-                                                  GEGL_RECTANGLE (0, 0, w, h),
+                                                  GEGL_RECTANGLE (0, 0,
+                                                                  width, height),
                                                   cairo_stride,
                                                   NULL, NULL);
 
@@ -239,9 +368,11 @@ gimp_display_shell_render (GimpDisplayShell *shell,
                */
               gimp_color_transform_process_buffer (shell->profile_transform,
                                                    shell->profile_buffer,
-                                                   GEGL_RECTANGLE (0, 0, w, h),
+                                                   GEGL_RECTANGLE (0, 0,
+                                                                   width, height),
                                                    buffer,
-                                                   GEGL_RECTANGLE (0, 0, w, h));
+                                                   GEGL_RECTANGLE (0, 0,
+                                                                   width, height));
               g_object_unref (buffer);
             }
         }
@@ -252,7 +383,7 @@ gimp_display_shell_render (GimpDisplayShell *shell,
       if (gimp_display_shell_has_filter (shell) || ! can_convert_to_u8)
         {
           gegl_buffer_get (shell->filter_buffer,
-                           GEGL_RECTANGLE (0, 0, w, h), 1.0,
+                           GEGL_RECTANGLE (0, 0, width, height), 1.0,
                            babl_format ("cairo-ARGB32"),
                            cairo_data, cairo_stride,
                            GEGL_ABYSS_CLAMP);
@@ -265,13 +396,13 @@ gimp_display_shell_render (GimpDisplayShell *shell,
        */
 #ifndef USE_NODE_BLIT
       gegl_buffer_get (buffer,
-                       GEGL_RECTANGLE (x, y, w, h), scale,
+                       GEGL_RECTANGLE (x, y, width, height), scale,
                        babl_format ("cairo-ARGB32"),
                        cairo_data, cairo_stride,
                        GEGL_ABYSS_CLAMP);
 #else
       gegl_node_blit (node,
-                      scale, GEGL_RECTANGLE (x, y, w, h),
+                      scale, GEGL_RECTANGLE (x, y, width, height),
                       babl_format ("cairo-ARGB32"),
                       cairo_data, cairo_stride,
                       GEGL_BLIT_CACHE);
@@ -282,26 +413,30 @@ gimp_display_shell_render (GimpDisplayShell *shell,
   gimp_projectable_end_render (GIMP_PROJECTABLE (image));
 #endif
 
+  cairo_surface_mark_dirty (shell->render_surface);
+
+  cairo_set_source_surface (my_cr, shell->render_surface, x, y);
+  cairo_paint (my_cr);
+
   if (shell->mask)
     {
       if (! shell->mask_surface)
         {
           shell->mask_surface =
             cairo_image_surface_create (CAIRO_FORMAT_A8,
-                                        GIMP_DISPLAY_RENDER_BUF_WIDTH,
-                                        GIMP_DISPLAY_RENDER_BUF_HEIGHT);
+                                        shell->render_buf_width,
+                                        shell->render_buf_height);
         }
 
-      cairo_surface_mark_dirty (shell->mask_surface);
+      cairo_surface_flush (shell->mask_surface);
 
       cairo_stride = cairo_image_surface_get_stride (shell->mask_surface);
-      cairo_data   = cairo_image_surface_get_data (shell->mask_surface) +
-                     mask_src_y * cairo_stride + mask_src_x;
+      cairo_data   = cairo_image_surface_get_data (shell->mask_surface);
 
       gegl_buffer_get (shell->mask,
                        GEGL_RECTANGLE (x - floor (shell->mask_offset_x * scale),
                                        y - floor (shell->mask_offset_y * scale),
-                                       w, h),
+                                       width, height),
                        scale,
                        babl_format ("Y u8"),
                        cairo_data, cairo_stride,
@@ -309,11 +444,11 @@ gimp_display_shell_render (GimpDisplayShell *shell,
 
       if (shell->mask_inverted)
         {
-          gint mask_height = h;
+          gint mask_height = height;
 
           while (mask_height--)
             {
-              gint    mask_width = w;
+              gint    mask_width = width;
               guchar *d          = cairo_data;
 
               while (mask_width--)
@@ -326,19 +461,12 @@ gimp_display_shell_render (GimpDisplayShell *shell,
               cairo_data += cairo_stride;
             }
         }
-    }
 
-  /*  put it to the screen  */
-  cairo_set_source_surface (cr, xfer,
-                            x - xfer_src_x,
-                            y - xfer_src_y);
-  cairo_paint (cr);
+      cairo_surface_mark_dirty (shell->mask_surface);
 
-  if (shell->mask)
-    {
-      gimp_cairo_set_source_rgba (cr, &shell->mask_color);
-      cairo_mask_surface (cr, shell->mask_surface,
-                          x - mask_src_x,
-                          y - mask_src_y);
+      gimp_cairo_set_source_rgba (my_cr, &shell->mask_color);
+      cairo_mask_surface (my_cr, shell->mask_surface, x, y);
     }
+
+  cairo_destroy (my_cr);
 }
diff --git a/app/display/gimpdisplayshell-render.h b/app/display/gimpdisplayshell-render.h
index 7b4a644cfb..31898a211e 100644
--- a/app/display/gimpdisplayshell-render.h
+++ b/app/display/gimpdisplayshell-render.h
@@ -18,12 +18,37 @@
 #ifndef __GIMP_DISPLAY_SHELL_RENDER_H__
 #define __GIMP_DISPLAY_SHELL_RENDER_H__
 
-void  gimp_display_shell_render (GimpDisplayShell *shell,
-                                 cairo_t          *cr,
-                                 gint              x,
-                                 gint              y,
-                                 gint              w,
-                                 gint              h,
-                                 gdouble           scale);
+
+#define GIMP_DISPLAY_RENDER_ENABLE_SCALING 1
+#define GIMP_DISPLAY_RENDER_MAX_SCALE      4.0
+
+
+void     gimp_display_shell_render_invalidate_full (GimpDisplayShell *shell);
+void     gimp_display_shell_render_invalidate_area (GimpDisplayShell *shell,
+                                                    gint              x,
+                                                    gint              y,
+                                                    gint              width,
+                                                    gint              height);
+
+void     gimp_display_shell_render_validate_area   (GimpDisplayShell *shell,
+                                                    gint              x,
+                                                    gint              y,
+                                                    gint              width,
+                                                    gint              height);
+
+gboolean gimp_display_shell_render_is_valid        (GimpDisplayShell *shell,
+                                                    gint              x,
+                                                    gint              y,
+                                                    gint              width,
+                                                    gint              height);
+
+void     gimp_display_shell_render                 (GimpDisplayShell *shell,
+                                                    cairo_t          *cr,
+                                                    gint              x,
+                                                    gint              y,
+                                                    gint              width,
+                                                    gint              height,
+                                                    gdouble           scale);
+
 
 #endif  /*  __GIMP_DISPLAY_SHELL_RENDER_H__  */
diff --git a/app/display/gimpdisplayshell-rotate.c b/app/display/gimpdisplayshell-rotate.c
index dd9ed6940f..59e71c1134 100644
--- a/app/display/gimpdisplayshell-rotate.c
+++ b/app/display/gimpdisplayshell-rotate.c
@@ -29,6 +29,7 @@
 #include "gimpdisplay.h"
 #include "gimpdisplayshell.h"
 #include "gimpdisplayshell-expose.h"
+#include "gimpdisplayshell-render.h"
 #include "gimpdisplayshell-rotate.h"
 #include "gimpdisplayshell-scale.h"
 #include "gimpdisplayshell-scroll.h"
@@ -84,6 +85,7 @@ gimp_display_shell_flip (GimpDisplayShell *shell,
       gimp_display_shell_restore_viewport_center (shell, cx, cy);
 
       gimp_display_shell_expose_full (shell);
+      gimp_display_shell_render_invalidate_full (shell);
     }
 }
 
@@ -121,6 +123,7 @@ gimp_display_shell_rotate_to (GimpDisplayShell *shell,
   gimp_display_shell_restore_viewport_center (shell, cx, cy);
 
   gimp_display_shell_expose_full (shell);
+  gimp_display_shell_render_invalidate_full (shell);
 }
 
 void
diff --git a/app/display/gimpdisplayshell-scale.c b/app/display/gimpdisplayshell-scale.c
index 73811e664e..dec74fb885 100644
--- a/app/display/gimpdisplayshell-scale.c
+++ b/app/display/gimpdisplayshell-scale.c
@@ -36,6 +36,7 @@
 #include "gimpdisplay.h"
 #include "gimpdisplayshell.h"
 #include "gimpdisplayshell-expose.h"
+#include "gimpdisplayshell-render.h"
 #include "gimpdisplayshell-rotate.h"
 #include "gimpdisplayshell-scale.h"
 #include "gimpdisplayshell-scroll.h"
@@ -719,6 +720,7 @@ gimp_display_shell_scale_resize (GimpDisplayShell *shell,
   gimp_display_shell_scaled (shell);
 
   gimp_display_shell_expose_full (shell);
+  gimp_display_shell_render_invalidate_full (shell);
 
   /* re-enable the active tool */
   gimp_display_shell_resume (shell);
diff --git a/app/display/gimpdisplayshell-scroll.c b/app/display/gimpdisplayshell-scroll.c
index 1987d5d9a4..d120f4c2ab 100644
--- a/app/display/gimpdisplayshell-scroll.c
+++ b/app/display/gimpdisplayshell-scroll.c
@@ -36,6 +36,7 @@
 #include "gimpdisplay-foreach.h"
 #include "gimpdisplayshell.h"
 #include "gimpdisplayshell-expose.h"
+#include "gimpdisplayshell-render.h"
 #include "gimpdisplayshell-rotate.h"
 #include "gimpdisplayshell-rulers.h"
 #include "gimpdisplayshell-scale.h"
@@ -94,6 +95,45 @@ gimp_display_shell_scroll (GimpDisplayShell *shell,
       gimp_overlay_box_scroll (GIMP_OVERLAY_BOX (shell->canvas),
                                -x_offset, -y_offset);
 
+      if (shell->render_cache)
+        {
+          cairo_surface_t       *surface;
+          cairo_t               *cr;
+
+          surface =
+            cairo_surface_create_similar_image (shell->render_cache,
+                                                CAIRO_FORMAT_ARGB32,
+                                                shell->disp_width,
+                                                shell->disp_height);
+
+          cr = cairo_create (surface);
+          cairo_set_source_surface (cr, shell->render_cache, 0, 0);
+          cairo_paint (cr);
+          cairo_destroy (cr);
+
+          cr = cairo_create (shell->render_cache);
+          cairo_set_source_surface (cr, surface,
+                                    -x_offset, -y_offset);
+          cairo_paint (cr);
+          cairo_destroy (cr);
+
+          cairo_surface_destroy (surface);
+        }
+
+      if (shell->render_cache_valid)
+        {
+          cairo_rectangle_int_t rect;
+
+          cairo_region_translate (shell->render_cache_valid,
+                                  -x_offset, -y_offset);
+
+          rect.x      = 0;
+          rect.y      = 0;
+          rect.width  = shell->disp_width;
+          rect.height = shell->disp_height;
+
+          cairo_region_intersect_rectangle (shell->render_cache_valid, &rect);
+        }
     }
 
   /* re-enable the active tool */
@@ -137,6 +177,7 @@ gimp_display_shell_scroll_set_offset (GimpDisplayShell *shell,
   gimp_display_shell_scrolled (shell);
 
   gimp_display_shell_expose_full (shell);
+  gimp_display_shell_render_invalidate_full (shell);
 
   /* re-enable the active tool */
   gimp_display_shell_resume (shell);
diff --git a/app/display/gimpdisplayshell.c b/app/display/gimpdisplayshell.c
index b029487787..a1075cb66f 100644
--- a/app/display/gimpdisplayshell.c
+++ b/app/display/gimpdisplayshell.c
@@ -723,8 +723,12 @@ gimp_display_shell_dispose (GObject *object)
       shell->filter_idle_id = 0;
     }
 
-  g_clear_pointer (&shell->mask_surface, cairo_surface_destroy);
-  g_clear_pointer (&shell->checkerboard, cairo_pattern_destroy);
+  g_clear_pointer (&shell->render_cache,       cairo_surface_destroy);
+  g_clear_pointer (&shell->render_cache_valid, cairo_region_destroy);
+
+  g_clear_pointer (&shell->render_surface, cairo_surface_destroy);
+  g_clear_pointer (&shell->mask_surface,   cairo_surface_destroy);
+  g_clear_pointer (&shell->checkerboard,   cairo_pattern_destroy);
 
   gimp_display_shell_profile_finalize (shell);
 
@@ -1032,6 +1036,7 @@ gimp_display_shell_profile_changed (GimpColorManaged *managed)
 
   gimp_display_shell_profile_update (shell);
   gimp_display_shell_expose_full (shell);
+  gimp_display_shell_render_invalidate_full (shell);
 }
 
 static void
@@ -1312,6 +1317,7 @@ gimp_display_shell_reconnect (GimpDisplayShell *shell)
   gimp_display_shell_scaled (shell);
 
   gimp_display_shell_expose_full (shell);
+  gimp_display_shell_render_invalidate_full (shell);
 }
 
 static gboolean
@@ -1377,6 +1383,7 @@ gimp_display_shell_empty (GimpDisplayShell *shell)
   gimp_display_shell_rotate_update_transform (shell);
 
   gimp_display_shell_expose_full (shell);
+  gimp_display_shell_render_invalidate_full (shell);
 
   user_context = gimp_get_user_context (shell->display->gimp);
 
@@ -1867,4 +1874,5 @@ gimp_display_shell_set_mask (GimpDisplayShell *shell,
   shell->mask_inverted = inverted;
 
   gimp_display_shell_expose_full (shell);
+  gimp_display_shell_render_invalidate_full (shell);
 }
diff --git a/app/display/gimpdisplayshell.h b/app/display/gimpdisplayshell.h
index a96f67aa76..00176344aa 100644
--- a/app/display/gimpdisplayshell.h
+++ b/app/display/gimpdisplayshell.h
@@ -166,7 +166,13 @@ struct _GimpDisplayShell
   guchar             *filter_data;     /*  filter_buffer's pixels             */
   gint                filter_stride;   /*  filter_buffer's stride             */
 
-  GimpDisplayXfer   *xfer;             /*  manages image buffer transfers     */
+  cairo_surface_t   *render_cache;
+  cairo_region_t    *render_cache_valid;
+
+  gint               render_buf_width;
+  gint               render_buf_height;
+
+  cairo_surface_t   *render_surface;   /*  buffer for rendering the mask      */
   cairo_surface_t   *mask_surface;     /*  buffer for rendering the mask      */
   cairo_pattern_t   *checkerboard;     /*  checkerboard pattern               */
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]