mirror of
https://github.com/python-pillow/Pillow.git
synced 2026-01-09 02:01:26 +03:00
Support saving APNG float durations (#9365)
This commit is contained in:
commit
b2d9bc3c76
|
|
@ -518,6 +518,24 @@ def test_apng_save_duration_loop(tmp_path: Path) -> None:
|
|||
assert im.info["duration"] == 600
|
||||
|
||||
|
||||
def test_apng_save_duration_float(tmp_path: Path) -> None:
|
||||
test_file = tmp_path / "temp.png"
|
||||
im = Image.new("1", (1, 1))
|
||||
im2 = Image.new("1", (1, 1), 1)
|
||||
im.save(test_file, save_all=True, append_images=[im2], duration=0.5)
|
||||
|
||||
with Image.open(test_file) as reloaded:
|
||||
assert reloaded.info["duration"] == 0.5
|
||||
|
||||
|
||||
def test_apng_save_large_duration(tmp_path: Path) -> None:
|
||||
test_file = tmp_path / "temp.png"
|
||||
im = Image.new("1", (1, 1))
|
||||
im2 = Image.new("1", (1, 1), 1)
|
||||
with pytest.raises(ValueError, match="cannot write duration"):
|
||||
im.save(test_file, save_all=True, append_images=[im2], duration=65536000)
|
||||
|
||||
|
||||
def test_apng_save_disposal(tmp_path: Path) -> None:
|
||||
test_file = tmp_path / "temp.png"
|
||||
size = (128, 64)
|
||||
|
|
|
|||
|
|
@ -1041,9 +1041,8 @@ following parameters can also be set:
|
|||
Defaults to 0.
|
||||
|
||||
**duration**
|
||||
Integer (or list or tuple of integers) length of time to display this APNG frame
|
||||
(in milliseconds).
|
||||
Defaults to 0.
|
||||
The length of time (or list or tuple of lengths of time) to display this APNG frame
|
||||
(in milliseconds). Defaults to 0.
|
||||
|
||||
**disposal**
|
||||
An integer (or list or tuple of integers) specifying the APNG disposal
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ import struct
|
|||
import warnings
|
||||
import zlib
|
||||
from enum import IntEnum
|
||||
from fractions import Fraction
|
||||
from typing import IO, NamedTuple, cast
|
||||
|
||||
from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence
|
||||
|
|
@ -1275,7 +1276,11 @@ def _write_multiple_frames(
|
|||
im_frame = im_frame.crop(bbox)
|
||||
size = im_frame.size
|
||||
encoderinfo = frame_data.encoderinfo
|
||||
frame_duration = int(round(encoderinfo.get("duration", 0)))
|
||||
frame_duration = encoderinfo.get("duration", 0)
|
||||
delay = Fraction(frame_duration / 1000).limit_denominator(65535)
|
||||
if delay.numerator > 65535:
|
||||
msg = "cannot write duration"
|
||||
raise ValueError(msg)
|
||||
frame_disposal = encoderinfo.get("disposal", disposal)
|
||||
frame_blend = encoderinfo.get("blend", blend)
|
||||
# frame control
|
||||
|
|
@ -1287,8 +1292,8 @@ def _write_multiple_frames(
|
|||
o32(size[1]), # height
|
||||
o32(bbox[0]), # x_offset
|
||||
o32(bbox[1]), # y_offset
|
||||
o16(frame_duration), # delay_numerator
|
||||
o16(1000), # delay_denominator
|
||||
o16(delay.numerator), # delay_numerator
|
||||
o16(delay.denominator), # delay_denominator
|
||||
o8(frame_disposal), # dispose_op
|
||||
o8(frame_blend), # blend_op
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user