|
7 | 7 | from math import ceil
|
8 | 8 | import os
|
9 | 9 | import logging
|
| 10 | +from pathlib import Path |
10 | 11 | import urllib.parse
|
11 | 12 | import urllib.request
|
12 | 13 |
|
@@ -1432,24 +1433,48 @@ def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None,
|
1432 | 1433 | The DPI to store in the metadata of the file. This does not affect the
|
1433 | 1434 | resolution of the output image.
|
1434 | 1435 | """
|
1435 |
| - from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas |
1436 | 1436 | from matplotlib.figure import Figure
|
1437 | 1437 | if isinstance(fname, os.PathLike):
|
1438 | 1438 | fname = os.fspath(fname)
|
1439 |
| - if (format == 'png' |
1440 |
| - or (format is None |
1441 |
| - and isinstance(fname, str) |
1442 |
| - and fname.lower().endswith('.png'))): |
1443 |
| - image = AxesImage(None, cmap=cmap, origin=origin) |
1444 |
| - image.set_data(arr) |
1445 |
| - image.set_clim(vmin, vmax) |
1446 |
| - image.write_png(fname) |
1447 |
| - else: |
| 1439 | + if format is None: |
| 1440 | + format = (Path(fname).suffix[1:] if isinstance(fname, str) |
| 1441 | + else rcParams["savefig.format"]).lower() |
| 1442 | + if format in ["pdf", "ps", "eps", "svg"]: |
| 1443 | + # Vector formats that are not handled by PIL. |
1448 | 1444 | fig = Figure(dpi=dpi, frameon=False)
|
1449 |
| - FigureCanvas(fig) |
1450 | 1445 | fig.figimage(arr, cmap=cmap, vmin=vmin, vmax=vmax, origin=origin,
|
1451 | 1446 | resize=True)
|
1452 | 1447 | fig.savefig(fname, dpi=dpi, format=format, transparent=True)
|
| 1448 | + else: |
| 1449 | + # Don't bother creating an image; this avoids rounding errors on the |
| 1450 | + # size when dividing and then multiplying by dpi. |
| 1451 | + sm = cm.ScalarMappable(cmap=cmap) |
| 1452 | + sm.set_clim(vmin, vmax) |
| 1453 | + if origin is None: |
| 1454 | + origin = rcParams["image.origin"] |
| 1455 | + if origin == "lower": |
| 1456 | + arr = arr[::-1] |
| 1457 | + rgba = sm.to_rgba(arr, bytes=True) |
| 1458 | + if format == "png": |
| 1459 | + _png.write_png(rgba, fname, dpi=dpi) |
| 1460 | + else: |
| 1461 | + try: |
| 1462 | + from PIL import Image |
| 1463 | + except ImportError as exc: |
| 1464 | + raise ImportError( |
| 1465 | + f"Saving to {format} requires Pillow") from exc |
| 1466 | + pil_shape = (rgba.shape[1], rgba.shape[0]) |
| 1467 | + image = Image.frombuffer( |
| 1468 | + "RGBA", pil_shape, rgba, "raw", "RGBA", 0, 1) |
| 1469 | + if format in ["jpg", "jpeg"]: |
| 1470 | + format = "jpeg" # Pillow doesn't recognize "jpg". |
| 1471 | + color = tuple( |
| 1472 | + int(x * 255) |
| 1473 | + for x in mcolors.to_rgb(rcParams["savefig.facecolor"])) |
| 1474 | + background = Image.new("RGB", pil_shape, color) |
| 1475 | + background.paste(image, image) |
| 1476 | + image = background |
| 1477 | + image.save(fname, format=format, dpi=(dpi, dpi)) |
1453 | 1478 |
|
1454 | 1479 |
|
1455 | 1480 | def pil_to_array(pilImage):
|
|
0 commit comments