Merge pull request #5611 from radarhere/apng_info

This commit is contained in:
Hugo van Kemenade 2021-08-24 11:59:38 +03:00 committed by GitHub
commit d5d6c3ba8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 37 additions and 14 deletions

View File

@ -441,6 +441,12 @@ def test_apng_save_duration_loop(tmp_path):
assert im.n_frames == 1 assert im.n_frames == 1
assert im.info.get("duration") == 750 assert im.info.get("duration") == 750
# test info duration
frame.info["duration"] = 750
frame.save(test_file, save_all=True)
with Image.open(test_file) as im:
assert im.info.get("duration") == 750
def test_apng_save_disposal(tmp_path): def test_apng_save_disposal(tmp_path):
test_file = str(tmp_path / "temp.png") test_file = str(tmp_path / "temp.png")
@ -531,6 +537,17 @@ def test_apng_save_disposal(tmp_path):
assert im.getpixel((0, 0)) == (0, 255, 0, 255) assert im.getpixel((0, 0)) == (0, 255, 0, 255)
assert im.getpixel((64, 32)) == (0, 255, 0, 255) assert im.getpixel((64, 32)) == (0, 255, 0, 255)
# test info disposal
red.info["disposal"] = PngImagePlugin.APNG_DISPOSE_OP_BACKGROUND
red.save(
test_file,
save_all=True,
append_images=[Image.new("RGBA", (10, 10), (0, 255, 0, 255))],
)
with Image.open(test_file) as im:
im.seek(1)
assert im.getpixel((64, 32)) == (0, 0, 0, 0)
def test_apng_save_disposal_previous(tmp_path): def test_apng_save_disposal_previous(tmp_path):
test_file = str(tmp_path / "temp.png") test_file = str(tmp_path / "temp.png")
@ -611,3 +628,10 @@ def test_apng_save_blend(tmp_path):
im.seek(2) im.seek(2)
assert im.getpixel((0, 0)) == (0, 255, 0, 255) assert im.getpixel((0, 0)) == (0, 255, 0, 255)
assert im.getpixel((64, 32)) == (0, 255, 0, 255) assert im.getpixel((64, 32)) == (0, 255, 0, 255)
# test info blend
red.info["blend"] = PngImagePlugin.APNG_BLEND_OP_OVER
red.save(test_file, save_all=True, append_images=[green, transparent])
with Image.open(test_file) as im:
im.seek(2)
assert im.getpixel((0, 0)) == (0, 255, 0, 255)

View File

@ -1061,8 +1061,10 @@ def _write_multiple_frames(im, fp, chunk, rawmode):
default_image = im.encoderinfo.get("default_image", im.info.get("default_image")) default_image = im.encoderinfo.get("default_image", im.info.get("default_image"))
duration = im.encoderinfo.get("duration", im.info.get("duration", 0)) duration = im.encoderinfo.get("duration", im.info.get("duration", 0))
loop = im.encoderinfo.get("loop", im.info.get("loop", 0)) loop = im.encoderinfo.get("loop", im.info.get("loop", 0))
disposal = im.encoderinfo.get("disposal", im.info.get("disposal")) disposal = im.encoderinfo.get(
blend = im.encoderinfo.get("blend", im.info.get("blend")) "disposal", im.info.get("disposal", APNG_DISPOSE_OP_NONE)
)
blend = im.encoderinfo.get("blend", im.info.get("blend", APNG_BLEND_OP_SOURCE))
if default_image: if default_image:
chain = itertools.chain(im.encoderinfo.get("append_images", [])) chain = itertools.chain(im.encoderinfo.get("append_images", []))
@ -1117,12 +1119,8 @@ def _write_multiple_frames(im, fp, chunk, rawmode):
and prev_disposal == encoderinfo.get("disposal") and prev_disposal == encoderinfo.get("disposal")
and prev_blend == encoderinfo.get("blend") and prev_blend == encoderinfo.get("blend")
): ):
frame_duration = encoderinfo.get("duration", 0) if isinstance(duration, (list, tuple)):
if frame_duration: previous["encoderinfo"]["duration"] += encoderinfo["duration"]
if "duration" in previous["encoderinfo"]:
previous["encoderinfo"]["duration"] += frame_duration
else:
previous["encoderinfo"]["duration"] = frame_duration
continue continue
else: else:
bbox = None bbox = None
@ -1149,9 +1147,10 @@ def _write_multiple_frames(im, fp, chunk, rawmode):
bbox = frame_data["bbox"] bbox = frame_data["bbox"]
im_frame = im_frame.crop(bbox) im_frame = im_frame.crop(bbox)
size = im_frame.size size = im_frame.size
duration = int(round(frame_data["encoderinfo"].get("duration", 0))) encoderinfo = frame_data["encoderinfo"]
disposal = frame_data["encoderinfo"].get("disposal", APNG_DISPOSE_OP_NONE) frame_duration = int(round(encoderinfo.get("duration", duration)))
blend = frame_data["encoderinfo"].get("blend", APNG_BLEND_OP_SOURCE) frame_disposal = encoderinfo.get("disposal", disposal)
frame_blend = encoderinfo.get("blend", blend)
# frame control # frame control
chunk( chunk(
fp, fp,
@ -1161,10 +1160,10 @@ def _write_multiple_frames(im, fp, chunk, rawmode):
o32(size[1]), # height o32(size[1]), # height
o32(bbox[0]), # x_offset o32(bbox[0]), # x_offset
o32(bbox[1]), # y_offset o32(bbox[1]), # y_offset
o16(duration), # delay_numerator o16(frame_duration), # delay_numerator
o16(1000), # delay_denominator o16(1000), # delay_denominator
o8(disposal), # dispose_op o8(frame_disposal), # dispose_op
o8(blend), # blend_op o8(frame_blend), # blend_op
) )
seq_num += 1 seq_num += 1
# frame data # frame data