[gimp] Issue #3781 - Display artifacts on HiDPI when render cache is invalidated



commit d710e96d818f7eb7b7aede0c3fdebe5ab58dbd17
Author: Ell <ell_se yahoo com>
Date:   Wed Sep 11 21:00:32 2019 +0300

    Issue #3781 - Display artifacts on HiDPI when render cache is invalidated
    
    In GimpDisplayShell, scale the render cache by the window's scale
    factor, and render its content in device pixels, instead of scaled
    application pixels.  When painting the cache to the screen, unscale
    the cairo context by the same factor, so that it's painted in the
    native resolution.  Note that the various
    gimp_display_shell_render_foo() functions still speak in
    application pixels, and the scaling happens internally in
    gimp_display_shell_render().
    
    Aside from rendering at native resolution on HiDPI, this also fixes
    an issue where grid-like display artifacts would appear when the
    render cache was not fully validated due to the non-native scaling.

 app/display/gimpdisplayshell-callbacks.c |  5 +++++
 app/display/gimpdisplayshell-draw.c      | 14 +++++++-----
 app/display/gimpdisplayshell-render.c    | 38 +++++++++++++++++++++++++++-----
 app/display/gimpdisplayshell-render.h    |  5 ++---
 app/display/gimpdisplayshell-scroll.c    | 13 ++++++-----
 app/display/gimpdisplayshell.c           |  2 ++
 app/display/gimpdisplayshell.h           |  2 ++
 app/display/gimpimagewindow.c            | 31 ++++++++++++++++++++++----
 8 files changed, 86 insertions(+), 24 deletions(-)
---
diff --git a/app/display/gimpdisplayshell-callbacks.c b/app/display/gimpdisplayshell-callbacks.c
index 4b26715377..25358c8854 100644
--- a/app/display/gimpdisplayshell-callbacks.c
+++ b/app/display/gimpdisplayshell-callbacks.c
@@ -97,6 +97,11 @@ gimp_display_shell_canvas_realize (GtkWidget        *canvas,
   shell->disp_width  = allocation.width;
   shell->disp_height = allocation.height;
 
+  gimp_display_shell_render_set_scale (
+    shell,
+    gdk_window_get_scale_factor (
+      gtk_widget_get_window (gtk_widget_get_toplevel (canvas))));
+
   /*  set up the scrollbar observers  */
   g_signal_connect (shell->hsbdata, "value-changed",
                     G_CALLBACK (gimp_display_shell_hadjustment_changed),
diff --git a/app/display/gimpdisplayshell-draw.c b/app/display/gimpdisplayshell-draw.c
index 8562c1d928..a6de49e20a 100644
--- a/app/display/gimpdisplayshell-draw.c
+++ b/app/display/gimpdisplayshell-draw.c
@@ -163,17 +163,12 @@ gimp_display_shell_draw_image (GimpDisplayShell *shell,
   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
    * the cairo scale-factor by the same amount (further down), so that we make
    * full use of the screen resolution, even on hidpi displays.
    */
-  scale *=
-    gdk_window_get_scale_factor (
-      gtk_widget_get_window (gtk_widget_get_toplevel (GTK_WIDGET (shell))));
-#endif
+  scale *= shell->render_scale;
 
-  scale  = MIN (scale, GIMP_DISPLAY_RENDER_MAX_SCALE);
   scale *= MAX (shell->scale_x, shell->scale_y);
 
   if (scale != shell->scale_x)
@@ -221,6 +216,13 @@ gimp_display_shell_draw_image (GimpDisplayShell *shell,
                                                        x1, y1, x2 - x1, y2 - y1);
             }
 
+          /* divide the cairo scale-factor by the window scale-factor, since
+           * the render cache uses device pixels.  see comment further up.
+           */
+          cairo_scale (cr,
+                       1.0 / shell->render_scale,
+                       1.0 / shell->render_scale);
+
           /* render from the render cache to screen */
           cairo_set_source_surface (cr, shell->render_cache, 0, 0);
           cairo_paint (cr);
diff --git a/app/display/gimpdisplayshell-render.c b/app/display/gimpdisplayshell-render.c
index f49a8206ef..b650c74b31 100644
--- a/app/display/gimpdisplayshell-render.c
+++ b/app/display/gimpdisplayshell-render.c
@@ -40,6 +40,28 @@
 #include "gimpdisplayshell-render.h"
 
 
+#define GIMP_DISPLAY_RENDER_ENABLE_SCALING 1
+#define GIMP_DISPLAY_RENDER_MAX_SCALE      4
+
+
+void
+gimp_display_shell_render_set_scale (GimpDisplayShell *shell,
+                                     gint              scale)
+{
+  g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
+
+#if GIMP_DISPLAY_RENDER_ENABLE_SCALING
+  scale = CLAMP (scale, 1, GIMP_DISPLAY_RENDER_MAX_SCALE);
+
+  if (scale != shell->render_scale)
+    {
+      shell->render_scale = scale;
+
+      gimp_display_shell_render_invalidate_full (shell);
+    }
+#endif
+}
+
 void
 gimp_display_shell_render_invalidate_full (GimpDisplayShell *shell)
 {
@@ -165,6 +187,11 @@ gimp_display_shell_render (GimpDisplayShell *shell,
   g_return_if_fail (width  > 0 && width  <= shell->render_buf_width);
   g_return_if_fail (height > 0 && height <= shell->render_buf_height);
 
+  tx      *= shell->render_scale;
+  ty      *= shell->render_scale;
+  twidth  *= shell->render_scale;
+  theight *= shell->render_scale;
+
   display_config = shell->display->config;
 
   if (shell->show_all)
@@ -199,11 +226,11 @@ gimp_display_shell_render (GimpDisplayShell *shell,
 
   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);
+      shell->render_cache = cairo_surface_create_similar_image (
+        cairo_get_target (cr),
+        CAIRO_FORMAT_ARGB32,
+        shell->disp_width  * shell->render_scale,
+        shell->disp_height * shell->render_scale);
     }
 
   if (! shell->render_cache_valid)
@@ -218,6 +245,7 @@ gimp_display_shell_render (GimpDisplayShell *shell,
   cairo_clip (my_cr);
 
   /* transform to scaled image space, and apply uneven scaling */
+  cairo_scale (my_cr, shell->render_scale, shell->render_scale);
   if (shell->rotate_transform)
     cairo_transform (my_cr, shell->rotate_transform);
   cairo_translate (my_cr, -shell->offset_x, -shell->offset_y);
diff --git a/app/display/gimpdisplayshell-render.h b/app/display/gimpdisplayshell-render.h
index 31898a211e..8cdc89b25a 100644
--- a/app/display/gimpdisplayshell-render.h
+++ b/app/display/gimpdisplayshell-render.h
@@ -19,9 +19,8 @@
 #define __GIMP_DISPLAY_SHELL_RENDER_H__
 
 
-#define GIMP_DISPLAY_RENDER_ENABLE_SCALING 1
-#define GIMP_DISPLAY_RENDER_MAX_SCALE      4.0
-
+void     gimp_display_shell_render_set_scale       (GimpDisplayShell *shell,
+                                                    gint              scale);
 
 void     gimp_display_shell_render_invalidate_full (GimpDisplayShell *shell);
 void     gimp_display_shell_render_invalidate_area (GimpDisplayShell *shell,
diff --git a/app/display/gimpdisplayshell-scroll.c b/app/display/gimpdisplayshell-scroll.c
index 9e3c4771b2..453574b2f7 100644
--- a/app/display/gimpdisplayshell-scroll.c
+++ b/app/display/gimpdisplayshell-scroll.c
@@ -100,11 +100,11 @@ gimp_display_shell_scroll (GimpDisplayShell *shell,
           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);
+          surface = cairo_surface_create_similar_image (
+            shell->render_cache,
+            CAIRO_FORMAT_ARGB32,
+            shell->disp_width  * shell->render_scale,
+            shell->disp_height * shell->render_scale);
 
           cr = cairo_create (surface);
           cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
@@ -115,7 +115,8 @@ gimp_display_shell_scroll (GimpDisplayShell *shell,
           cr = cairo_create (shell->render_cache);
           cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
           cairo_set_source_surface (cr, surface,
-                                    -x_offset, -y_offset);
+                                    -x_offset * shell->render_scale,
+                                    -y_offset * shell->render_scale);
           cairo_paint (cr);
           cairo_destroy (cr);
 
diff --git a/app/display/gimpdisplayshell.c b/app/display/gimpdisplayshell.c
index 31b96a7432..779d1bda91 100644
--- a/app/display/gimpdisplayshell.c
+++ b/app/display/gimpdisplayshell.c
@@ -345,6 +345,8 @@ gimp_display_shell_init (GimpDisplayShell *shell)
   shell->filter_profile    = gimp_babl_get_builtin_color_profile (GIMP_RGB,
                                                                   GIMP_TRC_NON_LINEAR);
 
+  shell->render_scale      = 1;
+
   shell->render_buf_width  = 256;
   shell->render_buf_height = 256;
 
diff --git a/app/display/gimpdisplayshell.h b/app/display/gimpdisplayshell.h
index 1abe00a7c1..52df072758 100644
--- a/app/display/gimpdisplayshell.h
+++ b/app/display/gimpdisplayshell.h
@@ -171,6 +171,8 @@ struct _GimpDisplayShell
   guchar             *filter_data;     /*  filter_buffer's pixels             */
   gint                filter_stride;   /*  filter_buffer's stride             */
 
+  gint               render_scale;
+
   cairo_surface_t   *render_cache;
   cairo_region_t    *render_cache_valid;
 
diff --git a/app/display/gimpimagewindow.c b/app/display/gimpimagewindow.c
index f0493bb3ea..f8d206264c 100644
--- a/app/display/gimpimagewindow.c
+++ b/app/display/gimpimagewindow.c
@@ -71,6 +71,8 @@
 #include "gimpdisplayshell.h"
 #include "gimpdisplayshell-appearance.h"
 #include "gimpdisplayshell-close.h"
+#include "gimpdisplayshell-expose.h"
+#include "gimpdisplayshell-render.h"
 #include "gimpdisplayshell-scale.h"
 #include "gimpdisplayshell-scroll.h"
 #include "gimpdisplayshell-tool-events.h"
@@ -137,6 +139,8 @@ struct _GimpImageWindowPrivate
 
   GdkMonitor        *initial_monitor;
 
+  gint               scale_factor;
+
   gint               suspend_keep_pos;
 
   gint               update_ui_manager_idle_id;
@@ -636,10 +640,12 @@ static gboolean
 gimp_image_window_configure_event (GtkWidget         *widget,
                                    GdkEventConfigure *event)
 {
-  GimpImageWindow *window = GIMP_IMAGE_WINDOW (widget);
-  GtkAllocation    allocation;
-  gint             current_width;
-  gint             current_height;
+  GimpImageWindow        *window  = GIMP_IMAGE_WINDOW (widget);
+  GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window);
+  GtkAllocation           allocation;
+  gint                    current_width;
+  gint                    current_height;
+  gint                    scale_factor;
 
   gtk_widget_get_allocation (widget, &allocation);
 
@@ -664,6 +670,23 @@ gimp_image_window_configure_event (GtkWidget         *widget,
         shell->size_allocate_from_configure_event = TRUE;
     }
 
+  scale_factor = gdk_window_get_scale_factor (gtk_widget_get_window (widget));
+
+  if (scale_factor != private->scale_factor)
+    {
+      GList *list;
+
+      private->scale_factor = scale_factor;
+
+      for (list = private->shells; list; list = g_list_next (list))
+        {
+          GimpDisplayShell *shell = list->data;
+
+          gimp_display_shell_render_set_scale (shell, scale_factor);
+          gimp_display_shell_expose_full (shell);
+        }
+    }
+
   return TRUE;
 }
 


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