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.
This commit is contained in:
Ell 2019-09-11 21:00:32 +03:00
parent c20a8b732c
commit d710e96d81
8 changed files with 86 additions and 24 deletions

View File

@ -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),

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;
}