Skip to content
Open
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
9 changes: 9 additions & 0 deletions lib/matplotlib/figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -3026,6 +3026,15 @@ def __getstate__(self):
# add version information to the state
state['__mpl_version__'] = mpl.__version__

# Ensure DPI stored in pickle is the unscaled, original DPI.
# When a HiDPI backend (e.g., macOSX) sets a device pixel ratio, the
# runtime Figure.dpi is scaled from the original value. If we persisted
# the scaled dpi here, unpickling and recreating the canvas would set
# _original_dpi from an already-scaled value and the backend would
# scale it again, effectively doubling the dpi on each cycle.
# Persisting the original dpi avoids cumulative scaling.
state["_dpi"] = getattr(self, "_original_dpi", self._dpi)

# check whether the figure manager (if any) is registered with pyplot
from matplotlib import _pylab_helpers
if self.canvas.manager in _pylab_helpers.Gcf.figs.values():
Expand Down
24 changes: 24 additions & 0 deletions lib/matplotlib/tests/test_pickle.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,27 @@ def test_dynamic_norm():
def test_vertexselector():
line, = plt.plot([0, 1], picker=True)
pickle.loads(pickle.dumps(VertexSelector(line)))


def test_hidpi_pickle_unpickle_does_not_double_dpi():
# Emulate a HiDPI backend by setting device pixel ratio to 2. Verify that
# pickling and unpickling the figure does not cause the dpi to double on
# subsequent device pixel ratio application.
fig = plt.figure(dpi=100)
# Emulate HiDPI scaling applied by backend.
fig.canvas._set_device_pixel_ratio(2)
assert fig.dpi == 200

# First unpickle cycle should not change effective dpi when DPR=2 is
# applied again by the backend.
pf = pickle.dumps(fig, pickle.HIGHEST_PROTOCOL)
fig2 = pickle.loads(pf)
# Emulate backend reapplying DPR on new canvas.
fig2.canvas._set_device_pixel_ratio(2)
assert fig2.dpi == 200

# Repeated cycles should remain stable.
pf2 = pickle.dumps(fig2, pickle.HIGHEST_PROTOCOL)
fig3 = pickle.loads(pf2)
fig3.canvas._set_device_pixel_ratio(2)
assert fig3.dpi == 200