Skip to content
21 changes: 13 additions & 8 deletions src/spatialdata_plot/pl/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,14 +244,17 @@ def _prepare_params_plot(
# handle axes and size
wspace = 0.75 / rcParams["figure.figsize"][0] + 0.02 if wspace is None else wspace
figsize = rcParams["figure.figsize"] if figsize is None else figsize
dpi = rcParams["figure.dpi"] if dpi is None else dpi
# When creating a new figure, fall back to rcParams; when the user provides
# their own axes, preserve the figure's existing DPI (only override if
# the user explicitly passed dpi= to show()).
resolved_dpi = rcParams["figure.dpi"] if dpi is None else dpi
if num_panels > 1 and ax is None:
fig, grid = _panel_grid(
num_panels=num_panels,
hspace=hspace,
wspace=wspace,
ncols=ncols,
dpi=dpi,
dpi=resolved_dpi,
figsize=figsize,
)
axs: None | Sequence[Axes] = [plt.subplot(grid[c]) for c in range(num_panels)]
Expand All @@ -266,14 +269,16 @@ def _prepare_params_plot(
)
assert ax is None or isinstance(ax, Sequence), f"Invalid type of `ax`: {type(ax)}, expected `Sequence`."
axs = ax
if dpi is not None:
fig.set_dpi(dpi)
else:
axs = None
if ax is None:
fig, ax = plt.subplots(figsize=figsize, dpi=dpi, constrained_layout=True)
fig, ax = plt.subplots(figsize=figsize, dpi=resolved_dpi, constrained_layout=True)
elif isinstance(ax, Axes):
# needed for rasterization if user provides Axes object
fig = ax.get_figure()
fig.set_dpi(dpi)
if dpi is not None:
fig.set_dpi(dpi)

# set scalebar
if scalebar_dx is not None:
Expand Down Expand Up @@ -1955,10 +1960,10 @@ def _rasterize_if_necessary(
target_y_dims = dpi * height
target_x_dims = dpi * width

# Heuristics for when to rasterize
# Rasterize when the source image is substantially larger than what the
# current figure DPI × size requires. The +100 margin avoids rasterizing
# when the image is only slightly larger than the target.
do_rasterization = y_dims > target_y_dims + 100 or x_dims > target_x_dims + 100
if x_dims < 2000 and y_dims < 2000:
do_rasterization = False

if do_rasterization:
logger.info("Rasterizing image for faster rendering.")
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/_images/ColorbarControls_colorbar_img_bottom.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/_images/ColorbarControls_colorbar_img_top.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/_images/Labels_can_control_label_outline.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/_images/Labels_can_render_outline_color.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/_images/Labels_can_render_outline_with_fill.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/_images/Labels_can_stack_render_labels.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/_images/Labels_outline_uses_data_driven_colors.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/_images/Show_user_ax_dpi_preserved.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions tests/pl/test_show.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ def test_plot_no_decorations(self, sdata_blobs: SpatialData):
"""Visual test: frameon=False + title='' produces just the plot content (regression for #204)."""
sdata_blobs.pl.render_images(element="blobs_image").pl.show(frameon=False, title="", colorbar=False)

def test_plot_user_ax_dpi_preserved(self, sdata_blobs: SpatialData):
"""Visual test: low DPI produces visibly pixelated rasterization (regression for #310).

Uses dpi=15 so the 512x512 blobs image is downsampled to ~96x72.
If the bug regresses and DPI is overridden to the default (~100),
no rasterization occurs and the sharper render fails comparison.
"""
fig, ax = plt.subplots(dpi=15)
sdata_blobs.pl.render_images(element="blobs_image").pl.show(ax=ax)

def test_no_plt_show_when_ax_provided(self, sdata_blobs: SpatialData):
"""plt.show() must not be called when the user supplies ax= (regression for #362)."""
_, ax = plt.subplots()
Expand Down Expand Up @@ -110,3 +120,27 @@ def test_frameon_false_multi_panel(sdata_blobs: SpatialData):
for ax in axs:
assert not ax.axison
plt.close("all")


def test_user_figure_dpi_preserved_when_ax_provided(sdata_blobs: SpatialData):
"""User's figure DPI must not be overridden when ax is passed without explicit dpi (regression for #310)."""
fig, ax = plt.subplots(dpi=300)
sdata_blobs.pl.render_images(element="blobs_image").pl.show(ax=ax, show=False)
assert fig.get_dpi() == 300
plt.close(fig)


def test_explicit_dpi_overrides_figure_dpi(sdata_blobs: SpatialData):
"""Explicit dpi= in show() should override the figure's DPI."""
fig, ax = plt.subplots(dpi=300)
sdata_blobs.pl.render_images(element="blobs_image").pl.show(ax=ax, dpi=150, show=False)
assert fig.get_dpi() == 150
plt.close(fig)


def test_dpi_default_used_when_no_ax(sdata_blobs: SpatialData):
"""When no ax is provided and dpi is not set, rcParams default should be used."""
ax = sdata_blobs.pl.render_images(element="blobs_image").pl.show(return_ax=True, show=False)
fig = ax.get_figure()
assert fig.get_dpi() == matplotlib.rcParams["figure.dpi"]
plt.close(fig)
Loading