diff --git a/Tests/images/iss634.apng b/Tests/images/iss634.apng new file mode 100644 index 000000000..89e025906 Binary files /dev/null and b/Tests/images/iss634.apng differ diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 1a2602957..be52b3bd1 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -6,6 +6,12 @@ from io import BytesIO import zlib import sys +try: + from PIL import _webp + HAVE_WEBP = True +except ImportError: + HAVE_WEBP = False + codecs = dir(Image.core) @@ -579,6 +585,13 @@ class TestFilePng(PillowTestCase): self.assertIsInstance(im.text, dict) ImageFile.LOAD_TRUNCATED_IMAGES = False + @unittest.skipUnless(HAVE_WEBP and _webp.HAVE_WEBPANIM, + "WebP support not installed with animation") + def test_apng(self): + im = Image.open("Tests/images/iss634.apng") + expected = Image.open("Tests/images/iss634.webp") + self.assert_image_similar(im, expected, 0.23) + @unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or macOS") class TestTruncatedPngPLeaks(PillowLeakTestCase): diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index dcd4b437f..04161a56c 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -526,6 +526,19 @@ class PngStream(ChunkStream): return s + # APNG chunks + def chunk_acTL(self, pos, length): + s = ImageFile._safe_read(self.fp, length) + return s + + def chunk_fcTL(self, pos, length): + s = ImageFile._safe_read(self.fp, length) + return s + + def chunk_fdAT(self, pos, length): + s = ImageFile._safe_read(self.fp, length) + return s + # -------------------------------------------------------------------- # PNG reader @@ -892,6 +905,7 @@ def getchunks(im, **params): Image.register_open(PngImageFile.format, PngImageFile, _accept) Image.register_save(PngImageFile.format, _save) -Image.register_extension(PngImageFile.format, ".png") +Image.register_extensions(PngImageFile.format, [".png", ".apng"]) Image.register_mime(PngImageFile.format, "image/png") +Image.register_mime(PngImageFile.format, "image/apng")