diff --git a/Tests/images/duplicate_number_of_loops.gif b/Tests/images/duplicate_number_of_loops.gif new file mode 100644 index 00000000000..ac315ee99f3 Binary files /dev/null and b/Tests/images/duplicate_number_of_loops.gif differ diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index c4f634faea2..dcdf2179afa 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -772,9 +772,16 @@ def test_number_of_loops(tmp_path): im = Image.new("L", (100, 100), "#000") im.save(out, loop=number_of_loops) with Image.open(out) as reread: - assert reread.info["loop"] == number_of_loops + # Check that even if a subsequent GIF frame has the number of loops specified, + # only the value from the first frame is used + with Image.open("Tests/images/duplicate_number_of_loops.gif") as im: + assert im.info["loop"] == 2 + + im.seek(1) + assert im.info["loop"] == 2 + def test_background(tmp_path): out = str(tmp_path / "temp.gif") diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index c91c1fbffb0..33b968a8f3f 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -244,7 +244,7 @@ def _seek(self, frame, update_image=True): info["comment"] = comment s = None continue - elif s[0] == 255: + elif s[0] == 255 and frame == 0: # # application extension # @@ -252,7 +252,7 @@ def _seek(self, frame, update_image=True): if block[:11] == b"NETSCAPE2.0": block = self.data() if len(block) >= 3 and block[0] == 1: - info["loop"] = i16(block, 1) + self.info["loop"] = i16(block, 1) while self.data(): pass @@ -399,7 +399,7 @@ def _rgb(color): if info.get("comment"): self.info["comment"] = info["comment"] - for k in ["duration", "extension", "loop"]: + for k in ["duration", "extension"]: if k in info: self.info[k] = info[k] elif k in self.info: @@ -716,18 +716,6 @@ def _write_local_header(fp, im, offset, flags): + o8(0) ) - if "loop" in im.encoderinfo: - number_of_loops = im.encoderinfo["loop"] - fp.write( - b"!" - + o8(255) # extension intro - + o8(11) - + b"NETSCAPE2.0" - + o8(3) - + o8(1) - + o16(number_of_loops) # number of loops - + o8(0) - ) include_color_table = im.encoderinfo.get("include_color_table") if include_color_table: palette_bytes = _get_palette_bytes(im) @@ -933,6 +921,17 @@ def _get_global_header(im, info): # Global Color Table _get_header_palette(palette_bytes), ] + if "loop" in info: + header.append( + b"!" + + o8(255) # extension intro + + o8(11) + + b"NETSCAPE2.0" + + o8(3) + + o8(1) + + o16(info["loop"]) # number of loops + + o8(0) + ) if info.get("comment"): comment_block = b"!" + o8(254) # extension intro