From c18678004c411bf98dea620ad8ad6a149b096574 Mon Sep 17 00:00:00 2001 From: Andres Gutierrrez Date: Sun, 27 Jul 2025 15:50:01 -0400 Subject: [PATCH 1/8] FIX: .avif files not registered --- lib/matplotlib/backend_bases.py | 2 ++ lib/matplotlib/backends/backend_agg.py | 11 +++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 4adaecb7f8c0..23d9f8f8463d 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -76,6 +76,7 @@ 'tif': 'Tagged Image File Format', 'tiff': 'Tagged Image File Format', 'webp': 'WebP Image Format', + 'avif': 'Av1 Image File Format', } _default_backends = { 'eps': 'matplotlib.backends.backend_ps', @@ -93,6 +94,7 @@ 'tif': 'matplotlib.backends.backend_agg', 'tiff': 'matplotlib.backends.backend_agg', 'webp': 'matplotlib.backends.backend_agg', + 'avif': 'matplotlib.backends.backend_agg', } diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index b435ae565ce4..0e4396e790b9 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -510,7 +510,14 @@ def print_tif(self, filename_or_obj, *, metadata=None, pil_kwargs=None): def print_webp(self, filename_or_obj, *, metadata=None, pil_kwargs=None): self._print_pil(filename_or_obj, "webp", pil_kwargs, metadata) - print_gif.__doc__, print_jpg.__doc__, print_tif.__doc__, print_webp.__doc__ = map( + def print_avif(self, filename_or_obj, *, metadata=None, pil_kwargs=None): + self._print_pil(filename_or_obj, "avif", pil_kwargs, metadata) + + (print_gif.__doc__, + print_jpg.__doc__, + print_tif.__doc__, + print_webp.__doc__, + print_avif.__doc__) = map( """ Write the figure to a {} file. @@ -521,7 +528,7 @@ def print_webp(self, filename_or_obj, *, metadata=None, pil_kwargs=None): pil_kwargs : dict, optional Additional keyword arguments that are passed to `PIL.Image.Image.save` when saving the figure. - """.format, ["GIF", "JPEG", "TIFF", "WebP"]) + """.format, ["GIF", "JPEG", "TIFF", "WebP", "AVIF"]) @_Backend.export From fbd136e2d25e3ffbe70a3ab14a41dd684ad52eca Mon Sep 17 00:00:00 2001 From: Andres Gutierrrez Date: Mon, 28 Jul 2025 09:20:04 -0400 Subject: [PATCH 2/8] Tests for avif format --- lib/matplotlib/tests/test_agg.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/matplotlib/tests/test_agg.py b/lib/matplotlib/tests/test_agg.py index 56b26904d041..100f632a9306 100644 --- a/lib/matplotlib/tests/test_agg.py +++ b/lib/matplotlib/tests/test_agg.py @@ -263,6 +263,20 @@ def test_pil_kwargs_webp(): assert buf_large.getbuffer().nbytes > buf_small.getbuffer().nbytes +@pytest.mark.skipif(not features.check("avif"), reason="AVIF support not available") +def test_pil_kwargs_avif(): + plt.plot([0, 1, 2], [0, 1, 0]) + buf_small = io.BytesIO() + pil_kwargs_low = {"quality": 1} + plt.savefig(buf_small, format="avif", pil_kwargs=pil_kwargs_low) + assert len(pil_kwargs_low) == 1 + buf_large = io.BytesIO() + pil_kwargs_high = {"quality": 100} + plt.savefig(buf_large, format="avif", pil_kwargs=pil_kwargs_high) + assert len(pil_kwargs_high) == 1 + assert buf_large.getbuffer().nbytes > buf_small.getbuffer().nbytes + + def test_gif_no_alpha(): plt.plot([0, 1, 2], [0, 1, 0]) buf = io.BytesIO() @@ -290,6 +304,15 @@ def test_webp_alpha(): assert im.mode == "RGBA" +@pytest.mark.skipif(not features.check("avif"), reason="AVIF support not available") +def test_avif_alpha(): + plt.plot([0, 1, 2], [0, 1, 0]) + buf = io.BytesIO() + plt.savefig(buf, format="avif", transparent=True) + im = Image.open(buf) + assert im.mode == "RGBA" + + def test_draw_path_collection_error_handling(): fig, ax = plt.subplots() ax.scatter([1], [1]).set_paths(Path([(0, 1), (2, 3)])) From 96882b6302612ba0ca3a0b0409502a355d3ca656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Gutierrez?= <12andresgutierrez04@gmail.com> Date: Tue, 29 Jul 2025 15:25:23 -0400 Subject: [PATCH 3/8] Update lib/matplotlib/backends/backend_agg.py Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> --- lib/matplotlib/backends/backend_agg.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index 0e4396e790b9..8c2cc524705d 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -525,6 +525,9 @@ def print_avif(self, filename_or_obj, *, metadata=None, pil_kwargs=None): ---------- filename_or_obj : str or path-like or file-like The file to write to. + metadata : None + Unused for pillow-based writers. All supported options + can be passed via *pil_kwargs*. pil_kwargs : dict, optional Additional keyword arguments that are passed to `PIL.Image.Image.save` when saving the figure. From f3408a3d6d0189937a368a5a1f847281db3769c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Gutierrez?= <12andresgutierrez04@gmail.com> Date: Tue, 29 Jul 2025 15:31:04 -0400 Subject: [PATCH 4/8] Update lib/matplotlib/backends/backend_agg.py Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> --- lib/matplotlib/backends/backend_agg.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index 8c2cc524705d..3d0f21e0aaf1 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -511,6 +511,11 @@ def print_webp(self, filename_or_obj, *, metadata=None, pil_kwargs=None): self._print_pil(filename_or_obj, "webp", pil_kwargs, metadata) def print_avif(self, filename_or_obj, *, metadata=None, pil_kwargs=None): + if not PIL.features.check("avif"): + raise RuntimeError( + "The installed pillow version does not support avif. Full " + "avif support has been added in pillow 11.3." + ) self._print_pil(filename_or_obj, "avif", pil_kwargs, metadata) (print_gif.__doc__, From 724b67a347094bd7d27321ac14c9020f5b0d1919 Mon Sep 17 00:00:00 2001 From: Andres Gutierrrez Date: Tue, 29 Jul 2025 15:43:58 -0400 Subject: [PATCH 5/8] FIX: missing import for PIL --- lib/matplotlib/backends/backend_agg.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index 3d0f21e0aaf1..3f61061b27fd 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -36,6 +36,7 @@ from matplotlib.path import Path from matplotlib.transforms import Bbox, BboxBase from matplotlib.backends._backend_agg import RendererAgg as _RendererAgg +import PIL def get_hinting_flag(): From 051e2d1809c80312e00de495222e5d58fecce258 Mon Sep 17 00:00:00 2001 From: Andres Gutierrrez Date: Tue, 29 Jul 2025 15:43:58 -0400 Subject: [PATCH 6/8] FIX: missing import for PIL --- lib/matplotlib/backends/backend_agg.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index 3d0f21e0aaf1..7019f0bd1d4e 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -36,6 +36,7 @@ from matplotlib.path import Path from matplotlib.transforms import Bbox, BboxBase from matplotlib.backends._backend_agg import RendererAgg as _RendererAgg +from PIL import features def get_hinting_flag(): @@ -511,7 +512,7 @@ def print_webp(self, filename_or_obj, *, metadata=None, pil_kwargs=None): self._print_pil(filename_or_obj, "webp", pil_kwargs, metadata) def print_avif(self, filename_or_obj, *, metadata=None, pil_kwargs=None): - if not PIL.features.check("avif"): + if not features.check("avif"): raise RuntimeError( "The installed pillow version does not support avif. Full " "avif support has been added in pillow 11.3." From 4497bb568f4d7074d98e36902dc76b8d16da74f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Gutierrez?= <12andresgutierrez04@gmail.com> Date: Wed, 30 Jul 2025 12:09:23 -0400 Subject: [PATCH 7/8] Update lib/matplotlib/backend_bases.py Co-authored-by: Elliott Sales de Andrade --- lib/matplotlib/backend_bases.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 23d9f8f8463d..4c8d8266c869 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -76,7 +76,7 @@ 'tif': 'Tagged Image File Format', 'tiff': 'Tagged Image File Format', 'webp': 'WebP Image Format', - 'avif': 'Av1 Image File Format', + 'avif': 'AV1 Image File Format', } _default_backends = { 'eps': 'matplotlib.backends.backend_ps', From 676c4860d3e54b1a3fd721566e5fa7d62bdc5115 Mon Sep 17 00:00:00 2001 From: Andres Gutierrrez Date: Wed, 30 Jul 2025 12:31:53 -0400 Subject: [PATCH 8/8] changing order of dependencies for better readability --- lib/matplotlib/backends/backend_agg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index 7019f0bd1d4e..33b0be18ca2d 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -25,6 +25,7 @@ from math import radians, cos, sin import numpy as np +from PIL import features import matplotlib as mpl from matplotlib import _api, cbook @@ -36,7 +37,6 @@ from matplotlib.path import Path from matplotlib.transforms import Bbox, BboxBase from matplotlib.backends._backend_agg import RendererAgg as _RendererAgg -from PIL import features def get_hinting_flag():