Skip to content

Commit aeba9ac

Browse files
committed
Factor common parts of saving to different formats using pillow.
We don't need to drop the alpha channel for jpeg anymore as imsave handles that. In imsave, also clarify that `sm` is not going to be used in the already-usable-rgba case. Also, spliting out pil_kwargs under "other parameters" seems overkill for an effectively internal method (`savefig` is the user-facing API).
1 parent a9bba75 commit aeba9ac

File tree

2 files changed

+22
-45
lines changed

2 files changed

+22
-45
lines changed

lib/matplotlib/backends/backend_agg.py

Lines changed: 18 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,16 @@ def print_raw(self, filename_or_obj, *args):
487487

488488
print_rgba = print_raw
489489

490+
def _print_pil(self, filename_or_obj, fmt, pil_kwargs, metadata=None):
491+
"""
492+
Draw the canvas, then save it using `.image.imsave` (to which
493+
*pil_kwargs* and *metadata* are forwarded).
494+
"""
495+
FigureCanvasAgg.draw(self)
496+
mpl.image.imsave(
497+
filename_or_obj, self.buffer_rgba(), format=fmt, origin="upper",
498+
dpi=self.figure.dpi, metadata=metadata, pil_kwargs=pil_kwargs)
499+
490500
@_check_savefig_extra_args
491501
@_api.delete_parameter("3.5", "args")
492502
def print_png(self, filename_or_obj, *args,
@@ -537,10 +547,7 @@ def print_png(self, filename_or_obj, *args,
537547
If the 'pnginfo' key is present, it completely overrides
538548
*metadata*, including the default 'Software' key.
539549
"""
540-
FigureCanvasAgg.draw(self)
541-
mpl.image.imsave(
542-
filename_or_obj, self.buffer_rgba(), format="png", origin="upper",
543-
dpi=self.figure.dpi, metadata=metadata, pil_kwargs=pil_kwargs)
550+
self._print_pil(filename_or_obj, "png", pil_kwargs, metadata)
544551

545552
def print_to_buffer(self):
546553
FigureCanvasAgg.draw(self)
@@ -555,68 +562,38 @@ def print_to_buffer(self):
555562
@_check_savefig_extra_args()
556563
@_api.delete_parameter("3.5", "args")
557564
def print_jpg(self, filename_or_obj, *args, pil_kwargs=None, **kwargs):
558-
"""
559-
Write the figure to a JPEG file.
560-
561-
Parameters
562-
----------
563-
filename_or_obj : str or path-like or file-like
564-
The file to write to.
565-
566-
Other Parameters
567-
----------------
568-
pil_kwargs : dict, optional
569-
Additional keyword arguments that are passed to
570-
`PIL.Image.Image.save` when saving the figure.
571-
"""
572565
# Remove transparency by alpha-blending on an assumed white background.
573566
r, g, b, a = mcolors.to_rgba(self.figure.get_facecolor())
574567
try:
575568
self.figure.set_facecolor(a * np.array([r, g, b]) + 1 - a)
576-
FigureCanvasAgg.draw(self)
569+
self._print_pil(filename_or_obj, "jpeg", pil_kwargs)
577570
finally:
578571
self.figure.set_facecolor((r, g, b, a))
579-
if pil_kwargs is None:
580-
pil_kwargs = {}
581-
pil_kwargs.setdefault("dpi", (self.figure.dpi, self.figure.dpi))
582-
# Drop alpha channel now.
583-
return (Image.fromarray(np.asarray(self.buffer_rgba())[..., :3])
584-
.save(filename_or_obj, format='jpeg', **pil_kwargs))
585572

586573
print_jpeg = print_jpg
587574

588575
@_check_savefig_extra_args
589576
def print_tif(self, filename_or_obj, *, pil_kwargs=None):
590-
FigureCanvasAgg.draw(self)
591-
if pil_kwargs is None:
592-
pil_kwargs = {}
593-
pil_kwargs.setdefault("dpi", (self.figure.dpi, self.figure.dpi))
594-
return (Image.fromarray(np.asarray(self.buffer_rgba()))
595-
.save(filename_or_obj, format='tiff', **pil_kwargs))
577+
self._print_pil(filename_or_obj, "tiff", pil_kwargs)
596578

597579
print_tiff = print_tif
598580

599581
@_check_savefig_extra_args
600582
def print_webp(self, filename_or_obj, *, pil_kwargs=None):
583+
self._print_pil(filename_or_obj, "webp", pil_kwargs)
584+
585+
print_jpg.__doc__, print_tif.__doc__, print_webp.__doc__ = map(
601586
"""
602-
Write the figure to a WebP file.
587+
Write the figure to a {} file.
603588
604589
Parameters
605590
----------
606591
filename_or_obj : str or path-like or file-like
607592
The file to write to.
608-
609-
Other Parameters
610-
----------------
611593
pil_kwargs : dict, optional
612594
Additional keyword arguments that are passed to
613595
`PIL.Image.Image.save` when saving the figure.
614-
"""
615-
FigureCanvasAgg.draw(self)
616-
if pil_kwargs is None:
617-
pil_kwargs = {}
618-
return (Image.fromarray(np.asarray(self.buffer_rgba()))
619-
.save(filename_or_obj, format='webp', **pil_kwargs))
596+
""".format, ["JPEG", "TIFF", "WebP"])
620597

621598

622599
@_Backend.export

lib/matplotlib/image.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1601,20 +1601,20 @@ def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None,
16011601
else:
16021602
# Don't bother creating an image; this avoids rounding errors on the
16031603
# size when dividing and then multiplying by dpi.
1604-
sm = cm.ScalarMappable(cmap=cmap)
1605-
sm.set_clim(vmin, vmax)
16061604
if origin is None:
16071605
origin = mpl.rcParams["image.origin"]
16081606
if origin == "lower":
16091607
arr = arr[::-1]
16101608
if (isinstance(arr, memoryview) and arr.format == "B"
16111609
and arr.ndim == 3 and arr.shape[-1] == 4):
1612-
# Such an ``arr`` would also be handled fine by sm.to_rgba (after
1613-
# casting with asarray), but it is useful to special-case it
1610+
# Such an ``arr`` would also be handled fine by sm.to_rgba below
1611+
# (after casting with asarray), but it is useful to special-case it
16141612
# because that's what backend_agg passes, and can be in fact used
16151613
# as is, saving a few operations.
16161614
rgba = arr
16171615
else:
1616+
sm = cm.ScalarMappable(cmap=cmap)
1617+
sm.set_clim(vmin, vmax)
16181618
rgba = sm.to_rgba(arr, bytes=True)
16191619
if pil_kwargs is None:
16201620
pil_kwargs = {}

0 commit comments

Comments
 (0)