Repository navigation
APNG save_all merges or crops I;16 frames that differ only above 255 #10329
Description
What did you do?
I'm building an image-optimization tool that prepares screenshots and photos for PDFs: it picks the output format, carries the DPI so pages place at the right size, and checks that lossless steps keep every pixel. Its test suite generates each input type Pillow opens and runs it through the tool, which is where this came up. While generating 16-bit grayscale APNG inputs with save_all, a three-frame animation came back as one frame.
import io
from PIL import Image
def roundtrip(frames, **kwargs):
buf = io.BytesIO()
frames[0].save(buf, "PNG", save_all=True, append_images=frames[1:], **kwargs)
with Image.open(buf) as im:
values = []
for i in range(getattr(im, "n_frames", 1)):
im.seek(i)
values.append((im.getpixel((0, 0)), im.getpixel((7, 7))))
return values
# Three distinct frames are written as one
frames = [Image.new("I;16", (8, 8), v) for v in (1000, 2000, 3000)]
print(roundtrip(frames, duration=100))
# The second frame's changed region is cropped to the pixels that differ below 256
first = Image.new("I;16", (8, 8), 1000)
second = Image.new("I;16", (8, 8), 2000)
second.putpixel((0, 0), 50)
print(roundtrip([first, second]))What did you expect to happen?
Three frames, [(1000, 1000), (2000, 2000), (3000, 3000)], and in the second case a second frame of (50, 2000).
What actually happened?
[(1000, 1000)]
[(1000, 1000), (50, 1000)]
The frames are merged, and where a frame differs both below and above 255 only the region that differs below 256 is written. _write_multiple_frames compares im_frame.convert("RGBA") with base_im.convert("RGBA"), and converting I;16 to RGBA clips everything above 255, so those differences disappear. This may be close to what #10273 is changing.
Related: #10330 (16-bit tRNS key in convert("RGBA")).
What are your OS, Python and Pillow versions?
- OS: Ubuntu 24.04 (Linux x86_64)
- Python: 3.13.13
- Pillow: 13.0.0 (also reproduces on 12.3.0)
--------------------------------------------------------------------
Pillow 13.0.0
Python 3.13.13 (main, May 10 2026, 19:26:54) [Clang 22.1.3 ]
--------------------------------------------------------------------
Python executable is ~/.venv/bin/python
Environment Python files loaded from ~/.venv
System Python files loaded from ~/.local/share/uv/python/cpython-3.13.13-linux-x86_64-gnu
--------------------------------------------------------------------
Python Pillow modules loaded from ~/.venv/lib/python3.13/site-packages/PIL
Binary Pillow modules loaded from ~/.venv/lib/python3.13/site-packages/PIL
--------------------------------------------------------------------
--- PIL CORE support ok, compiled for 13.0.0
--- TKINTER support ok, loaded 9.0
--- FREETYPE2 support ok, loaded 2.14.3
--- LITTLECMS2 support ok, loaded 2.19
--- WEBP support ok, loaded 1.6.0
--- AVIF support ok, loaded 1.4.2
--- JPEG support ok, compiled for libjpeg-turbo 3.2.0
--- OPENJPEG (JPEG2000) support ok, loaded 2.5.4
--- ZLIB (PNG/ZIP) support ok, loaded 1.3, compiled for zlib-ng 2.3.3
--- LIBTIFF support ok, loaded 4.7.2
--- RAQM (Bidirectional Text) support ok, loaded 0.11.0, fribidi 1.0.13, harfbuzz 14.5.0
*** LIBIMAGEQUANT (Quantization method) support not installed
--- XCB (X protocol) support ok
--------------------------------------------------------------------
Activity