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