When saving multiple frames, convert to mode rather than raw mode

This commit is contained in:
Andrew Murray 2024-05-27 18:09:46 +10:00
parent bbe1effd63
commit f34360d1e3
2 changed files with 12 additions and 10 deletions

View File

@ -655,11 +655,12 @@ class TestFilePng:
png.call(cid, 0, 0)
ImageFile.LOAD_TRUNCATED_IMAGES = False
def test_specify_bits(self, tmp_path: Path) -> None:
@pytest.mark.parametrize("save_all", (True, False))
def test_specify_bits(self, save_all: bool, tmp_path: Path) -> None:
im = hopper("P")
out = str(tmp_path / "temp.png")
im.save(out, bits=4)
im.save(out, bits=4, save_all=save_all)
with Image.open(out) as reloaded:
assert len(reloaded.png.im_palette[1]) == 48

View File

@ -1104,7 +1104,7 @@ class _fdat:
self.seq_num += 1
def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images):
def _write_multiple_frames(im, fp, chunk, mode, 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))
@ -1119,10 +1119,10 @@ def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images)
frame_count = 0
for im_seq in chain:
for im_frame in ImageSequence.Iterator(im_seq):
if im_frame.mode == rawmode:
if im_frame.mode == mode:
im_frame = im_frame.copy()
else:
im_frame = im_frame.convert(rawmode)
im_frame = im_frame.convert(mode)
encoderinfo = im.encoderinfo.copy()
if isinstance(duration, (list, tuple)):
encoderinfo["duration"] = duration[frame_count]
@ -1184,8 +1184,8 @@ def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images)
# default image IDAT (if it exists)
if default_image:
if im.mode != rawmode:
im = im.convert(rawmode)
if im.mode != mode:
im = im.convert(mode)
ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)])
seq_num = 0
@ -1262,6 +1262,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False):
size = im.size
mode = im.mode
outmode = mode
if mode == "P":
#
# attempt to minimize storage requirements for palette images
@ -1282,7 +1283,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False):
bits = 2
else:
bits = 4
mode = f"{mode};{bits}"
outmode += f";{bits}"
# encoder options
im.encoderconfig = (
@ -1294,7 +1295,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False):
# get the corresponding PNG mode
try:
rawmode, bit_depth, color_type = _OUTMODES[mode]
rawmode, bit_depth, color_type = _OUTMODES[outmode]
except KeyError as e:
msg = f"cannot write mode {mode} as PNG"
raise OSError(msg) from e
@ -1415,7 +1416,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False):
if save_all:
im = _write_multiple_frames(
im, fp, chunk, rawmode, default_image, append_images
im, fp, chunk, mode, rawmode, default_image, append_images
)
if im:
ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)])