Skip to content

Fix Cairo backends on HiDPI screens #21025

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/matplotlib/backend_bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -2259,6 +2259,7 @@ def print_figure(
# Remove the figure manager, if any, to avoid resizing the GUI widget.
with cbook._setattr_cm(self, manager=None), \
cbook._setattr_cm(self.figure, dpi=dpi), \
cbook._setattr_cm(canvas, _device_pixel_ratio=1), \
cbook._setattr_cm(canvas, _is_saving=True), \
ExitStack() as stack:

Expand Down
8 changes: 3 additions & 5 deletions lib/matplotlib/backends/backend_cairo.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):

ctx.save()
ctx.select_font_face(*_cairo_font_args_from_font_prop(prop))
ctx.set_font_size(prop.get_size_in_points() * self.dpi / 72)
ctx.set_font_size(self.points_to_pixels(prop.get_size_in_points()))
opts = cairo.FontOptions()
opts.set_antialias(
cairo.ANTIALIAS_DEFAULT if mpl.rcParams["text.antialiased"]
Expand All @@ -271,7 +271,7 @@ def _draw_mathtext(self, gc, x, y, s, prop, angle):
ctx.move_to(ox, -oy)
ctx.select_font_face(
*_cairo_font_args_from_font_prop(ttfFontProperty(font)))
ctx.set_font_size(fontsize * self.dpi / 72)
ctx.set_font_size(self.points_to_pixels(fontsize))
ctx.show_text(chr(idx))

for ox, oy, w, h in rects:
Expand Down Expand Up @@ -303,9 +303,7 @@ def get_text_width_height_descent(self, s, prop, ismath):
# save/restore prevents the problem
ctx.save()
ctx.select_font_face(*_cairo_font_args_from_font_prop(prop))
# Cairo (says it) uses 1/96 inch user space units, ref: cairo_gstate.c
# but if /96.0 is used the font is too small
ctx.set_font_size(prop.get_size_in_points() * self.dpi / 72)
ctx.set_font_size(self.points_to_pixels(prop.get_size_in_points()))

y_bearing, w, h = ctx.text_extents(s)[1:4]
ctx.restore()
Expand Down
1 change: 1 addition & 0 deletions lib/matplotlib/backends/backend_gtk3cairo.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def on_draw_event(self, widget, ctx):
allocation.width, allocation.height)
self._renderer.set_width_height(
allocation.width, allocation.height)
self._renderer.dpi = self.figure.dpi
self.figure.draw(self._renderer)


Expand Down
1 change: 1 addition & 0 deletions lib/matplotlib/backends/backend_gtk4cairo.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def on_draw_event(self, widget, ctx):
allocation.width, allocation.height)
self._renderer.set_width_height(
allocation.width, allocation.height)
self._renderer.dpi = self.figure.dpi
self.figure.draw(self._renderer)


Expand Down
2 changes: 2 additions & 0 deletions lib/matplotlib/backends/backend_qtcairo.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def __init__(self, figure=None):

def draw(self):
if hasattr(self._renderer.gc, "ctx"):
self._renderer.dpi = self.figure.dpi
self.figure.draw(self._renderer)
super().draw()

Expand All @@ -23,6 +24,7 @@ def paintEvent(self, event):
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
self._renderer.set_ctx_from_surface(surface)
self._renderer.set_width_height(width, height)
self._renderer.dpi = self.figure.dpi
self.figure.draw(self._renderer)
buf = self._renderer.gc.ctx.get_target().get_data()
if QT_API == "PyQt6":
Expand Down
1 change: 1 addition & 0 deletions lib/matplotlib/backends/backend_tkcairo.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def draw(self):
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
self._renderer.set_ctx_from_surface(surface)
self._renderer.set_width_height(width, height)
self._renderer.dpi = self.figure.dpi
self.figure.draw(self._renderer)
buf = np.reshape(surface.get_data(), (height, width, 4))
_backend_tk.blit(
Expand Down
1 change: 1 addition & 0 deletions lib/matplotlib/backends/backend_wxcairo.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def draw(self, drawDC=None):
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
self._renderer.set_ctx_from_surface(surface)
self._renderer.set_width_height(width, height)
self._renderer.dpi = self.figure.dpi
self.figure.draw(self._renderer)
self.bitmap = wxcairo.BitmapFromImageSurface(surface)
self._isDrawn = True
Expand Down
29 changes: 27 additions & 2 deletions lib/matplotlib/tests/test_figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
from types import SimpleNamespace
import warnings

import numpy as np
import pytest
from PIL import Image

import matplotlib as mpl
from matplotlib import cbook, rcParams
from matplotlib._api.deprecation import MatplotlibDeprecationWarning
Expand All @@ -16,8 +20,6 @@
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.gridspec as gridspec
import numpy as np
import pytest


@image_comparison(['figure_align_labels'], extensions=['png', 'svg'],
Expand Down Expand Up @@ -496,6 +498,29 @@ def test_savefig_backend():
fig.savefig("test.png", backend="pdf")


@pytest.mark.parametrize('backend', [
pytest.param('Agg', marks=[pytest.mark.backend('Agg')]),
pytest.param('Cairo', marks=[pytest.mark.backend('Cairo')]),
])
def test_savefig_pixel_ratio(backend):
fig, ax = plt.subplots()
ax.plot([1, 2, 3])
with io.BytesIO() as buf:
fig.savefig(buf, format='png')
ratio1 = Image.open(buf)
ratio1.load()

fig, ax = plt.subplots()
ax.plot([1, 2, 3])
fig.canvas._set_device_pixel_ratio(2)
with io.BytesIO() as buf:
fig.savefig(buf, format='png')
ratio2 = Image.open(buf)
ratio2.load()

assert ratio1 == ratio2


def test_figure_repr():
fig = plt.figure(figsize=(10, 20), dpi=10)
assert repr(fig) == "<Figure size 100x200 with 0 Axes>"
Expand Down