Skip to content

Commit

Permalink
Merge pull request #6610 from radarhere/png_save_all
Browse files Browse the repository at this point in the history
  • Loading branch information
hugovk committed Oct 18, 2022
2 parents 1f5be89 + b2b3b62 commit 3d49166
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 11 deletions.
10 changes: 10 additions & 0 deletions Tests/test_file_apng.py
Expand Up @@ -647,6 +647,16 @@ def test_seek_after_close():
im.seek(0)


@pytest.mark.parametrize("mode", ("RGBA", "RGB", "P"))
def test_different_modes_in_later_frames(mode, tmp_path):
test_file = str(tmp_path / "temp.png")

im = Image.new("L", (1, 1))
im.save(test_file, save_all=True, append_images=[Image.new(mode, (1, 1))])
with Image.open(test_file) as reloaded:
assert reloaded.mode == mode


def test_constants_deprecation():
for enum, prefix in {
PngImagePlugin.Disposal: "APNG_DISPOSE_",
Expand Down
41 changes: 30 additions & 11 deletions src/PIL/PngImagePlugin.py
Expand Up @@ -1089,28 +1089,28 @@ def write(self, data):
self.seq_num += 1


def _write_multiple_frames(im, fp, chunk, rawmode):
default_image = im.encoderinfo.get("default_image", im.info.get("default_image"))
def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images):
duration = im.encoderinfo.get("duration", im.info.get("duration", 0))
loop = im.encoderinfo.get("loop", im.info.get("loop", 0))
disposal = im.encoderinfo.get("disposal", im.info.get("disposal", Disposal.OP_NONE))
blend = im.encoderinfo.get("blend", im.info.get("blend", Blend.OP_SOURCE))

if default_image:
chain = itertools.chain(im.encoderinfo.get("append_images", []))
chain = itertools.chain(append_images)
else:
chain = itertools.chain([im], im.encoderinfo.get("append_images", []))
chain = itertools.chain([im], append_images)

im_frames = []
frame_count = 0
for im_seq in chain:
for im_frame in ImageSequence.Iterator(im_seq):
im_frame = im_frame.copy()
if im_frame.mode != im.mode:
if im.mode == "P":
im_frame = im_frame.convert(im.mode, palette=im.palette)
if im_frame.mode == rawmode:
im_frame = im_frame.copy()
else:
if rawmode == "P":
im_frame = im_frame.convert(rawmode, palette=im.palette)
else:
im_frame = im_frame.convert(im.mode)
im_frame = im_frame.convert(rawmode)
encoderinfo = im.encoderinfo.copy()
if isinstance(duration, (list, tuple)):
encoderinfo["duration"] = duration[frame_count]
Expand Down Expand Up @@ -1221,7 +1221,26 @@ def _save_all(im, fp, filename):
def _save(im, fp, filename, chunk=putchunk, save_all=False):
# save an image to disk (called by the save method)

mode = im.mode
if save_all:
default_image = im.encoderinfo.get(
"default_image", im.info.get("default_image")
)
modes = set()
append_images = im.encoderinfo.get("append_images", [])
if default_image:
chain = itertools.chain(append_images)
else:
chain = itertools.chain([im], append_images)
for im_seq in chain:
for im_frame in ImageSequence.Iterator(im_seq):
modes.add(im_frame.mode)
for mode in ("RGBA", "RGB", "P"):
if mode in modes:
break
else:
mode = modes.pop()
else:
mode = im.mode

if mode == "P":

Expand Down Expand Up @@ -1373,7 +1392,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False):
chunk(fp, b"eXIf", exif)

if save_all:
_write_multiple_frames(im, fp, chunk, rawmode)
_write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images)
else:
ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)])

Expand Down

0 comments on commit 3d49166

Please sign in to comment.