From e45477e507c87c65947aea07bc8b081043444517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Noel?= Date: Fri, 16 Feb 2024 15:47:51 +0100 Subject: [PATCH] fix FLI/FLC decoder for files with a prefix chunk --- Tests/images/2422.flc | Bin 0 -> 14572 bytes Tests/test_file_fli.py | 25 +++++++++++++++++++++++-- src/PIL/FliImagePlugin.py | 1 + 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 Tests/images/2422.flc diff --git a/Tests/images/2422.flc b/Tests/images/2422.flc new file mode 100644 index 0000000000000000000000000000000000000000..eed5fb59e1b8793964575df338c68f78c4b23f06 GIT binary patch literal 14572 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 hopper.fli, default options. static_test_file = "Tests/images/hopper.fli" -# From https://samples.libav.org/fli-flc/ +# From https://samples.ffmpeg.org/fli-flc/ animated_test_file = "Tests/images/a.fli" +# From https://samples.ffmpeg.org/fli-flc/ +animated_test_file_with_prefix_chunk = "Tests/images/2422.flc" + def test_sanity() -> None: with Image.open(static_test_file) as im: @@ -32,6 +35,24 @@ def test_sanity() -> None: assert im.is_animated +def test_prefix_chunk() -> None: + ImageFile.LOAD_TRUNCATED_IMAGES = True + try: + with Image.open(animated_test_file_with_prefix_chunk) as im: + assert im.mode == "P" + assert im.size == (320, 200) + assert im.format == "FLI" + assert im.info["duration"] == 171 + assert im.is_animated + + palette = im.getpalette() + assert palette[3:6] == [255, 255, 255] + assert palette[381:384] == [204, 204, 12] + assert palette[765:] == [252, 0, 0] + finally: + ImageFile.LOAD_TRUNCATED_IMAGES = False + + @pytest.mark.skipif(is_pypy(), reason="Requires CPython") def test_unclosed_file() -> None: def open() -> None: diff --git a/src/PIL/FliImagePlugin.py b/src/PIL/FliImagePlugin.py index 9769761fc..f9e4c731c 100644 --- a/src/PIL/FliImagePlugin.py +++ b/src/PIL/FliImagePlugin.py @@ -77,6 +77,7 @@ class FliImageFile(ImageFile.ImageFile): if i16(s, 4) == 0xF100: # prefix chunk; ignore it self.__offset = self.__offset + i32(s) + self.fp.seek(self.__offset) s = self.fp.read(16) if i16(s, 4) == 0xF1FA: