From 5c55265f5b1317062d0340dae3c44a1f7d1d3cd2 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Fri, 29 Mar 2024 03:33:10 -0400 Subject: [PATCH 1/2] gtk4: Fix Cairo backend on HiDPI screens With GTK4, the Cairo context we get is always in logical pixels, and is automatically scaled to the right size, without any worry about blurriness. So in that case, we can ignore all scale factor changes, and assume it's always 1. The remaining effect of tracking scale factor changes is to trigger a re-draw, but GTK will send a resize event to go along with it, which will do that for us. Fixes #25847 Replaces #25861 --- lib/matplotlib/backends/backend_gtk4.py | 10 ++-------- lib/matplotlib/backends/backend_gtk4cairo.py | 9 ++++----- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/lib/matplotlib/backends/backend_gtk4.py b/lib/matplotlib/backends/backend_gtk4.py index 256a8ec9e864..dd86ab628ce7 100644 --- a/lib/matplotlib/backends/backend_gtk4.py +++ b/lib/matplotlib/backends/backend_gtk4.py @@ -34,7 +34,6 @@ class FigureCanvasGTK4(_FigureCanvasGTK, Gtk.DrawingArea): required_interactive_framework = "gtk4" supports_blit = False manager_class = _api.classproperty(lambda cls: FigureManagerGTK4) - _context_is_scaled = False def __init__(self, figure=None): super().__init__(figure=figure) @@ -228,13 +227,8 @@ def _post_draw(self, widget, ctx): lw = 1 dash = 3 - if not self._context_is_scaled: - x0, y0, w, h = (dim / self.device_pixel_ratio - for dim in self._rubberband_rect) - else: - x0, y0, w, h = self._rubberband_rect - lw *= self.device_pixel_ratio - dash *= self.device_pixel_ratio + x0, y0, w, h = (dim / self.device_pixel_ratio + for dim in self._rubberband_rect) x1 = x0 + w y1 = y0 + h diff --git a/lib/matplotlib/backends/backend_gtk4cairo.py b/lib/matplotlib/backends/backend_gtk4cairo.py index b1d543704351..838ea03fcce6 100644 --- a/lib/matplotlib/backends/backend_gtk4cairo.py +++ b/lib/matplotlib/backends/backend_gtk4cairo.py @@ -5,7 +5,10 @@ class FigureCanvasGTK4Cairo(FigureCanvasCairo, FigureCanvasGTK4): - _context_is_scaled = True + def _set_device_pixel_ratio(self, ratio): + # Cairo in GTK4 always uses logical pixels, so we don't need to do anything for + # changes to the device pixel ratio. + return False def on_draw_event(self, widget, ctx): if self._idle_draw_id: @@ -16,15 +19,11 @@ def on_draw_event(self, widget, ctx): with (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar else nullcontext()): self._renderer.set_context(ctx) - scale = self.device_pixel_ratio - # Scale physical drawing to logical size. - ctx.scale(1 / scale, 1 / scale) allocation = self.get_allocation() Gtk.render_background( self.get_style_context(), ctx, allocation.x, allocation.y, allocation.width, allocation.height) - self._renderer.dpi = self.figure.dpi self.figure.draw(self._renderer) From 6fee86a1cfcf6a7b9b2e0fa8e6e6158b37c91357 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Tue, 25 Jun 2024 02:10:48 -0400 Subject: [PATCH 2/2] gtk3: Fix Cairo backend With GTK3, the Cairo surface we get is for the whole window, which means the automatic size inference from #22004 gets the wrong size. For the GtkDrawingArea, the Cairo context is aligned and clipped to the widget, so nothing goes out-of-bounds. However, since the Cairo renderer flips the origin using the height in the calculation (which is, for the window, bigger than the drawing widget), everything is drawn lower than it should. --- lib/matplotlib/backends/backend_gtk3cairo.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/backends/backend_gtk3cairo.py b/lib/matplotlib/backends/backend_gtk3cairo.py index 24a26111f062..371b8dc1b31f 100644 --- a/lib/matplotlib/backends/backend_gtk3cairo.py +++ b/lib/matplotlib/backends/backend_gtk3cairo.py @@ -13,15 +13,19 @@ def on_draw_event(self, widget, ctx): with (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar else nullcontext()): - self._renderer.set_context(ctx) - scale = self.device_pixel_ratio - # Scale physical drawing to logical size. - ctx.scale(1 / scale, 1 / scale) allocation = self.get_allocation() + # Render the background before scaling, as the allocated size here is in + # logical pixels. Gtk.render_background( self.get_style_context(), ctx, - allocation.x, allocation.y, - allocation.width, allocation.height) + 0, 0, allocation.width, allocation.height) + scale = self.device_pixel_ratio + # Scale physical drawing to logical size. + ctx.scale(1 / scale, 1 / scale) + self._renderer.set_context(ctx) + # Set renderer to physical size so it renders in full resolution. + self._renderer.width = allocation.width * scale + self._renderer.height = allocation.height * scale self._renderer.dpi = self.figure.dpi self.figure.draw(self._renderer)