Repository navigation
Correct saving more than two MPO frames - #10326
Conversation
Muguett-DBY
left a comment
There was a problem hiding this comment.
Capturing the position before each JPEG fixes the length-versus-position mix-up. With the writer from 495a8dbb033aa8dead060ad38fb547d7cc0940e1, all eight added regression bodies pass; six fail with the base writer.
I also checked the index against independently parsed JPEG boundaries, covering RGB/grayscale, EXIF bytes, progressive/optimized JPEG and ICC. Re-saving two existing MPO sequences into one also preserves the frame sizes, offsets and decoded pixels, including backward seeks.
One separate existing corner case showed up with CMYK: the Adobe APP14 puts the MPF TIFF header at byte 26, while the writer assumes byte 28. Both the base and this writer put the TIFF bytes two bytes late, even with just two frames. That appears suitable for a separate fix.
These checks call the exact _save_all functions with the existing Pillow 12.3.0 JPEG encoder and MPO reader on Windows/Python 3.12. I haven't built this branch or run pytest/the full suite. Review and checks assisted by Codex.
7aa2a64 to
287c21d
Compare
Saving an MPO with four or more frames currently produces incorrect MP Index offsets, so the later frames cannot be read back. The third frame already has an incorrect size in the index, although Pillow can still decode it because its starting offset is valid.
_save_all()stores individual JPEG lengths inoffsets, but subtracts the last of those lengths from the absolute file position after writing the next JPEG. This only gives the correct length for the second frame. Capture the file position before each appended JPEG instead.Changes proposed in this pull request:
For a four-frame example, the JPEG lengths are
[771, 637, 653, 661]. Before this change, the MP Index records[771, 637, 1424, 1298]and places the fourth frame at byte 2832 instead of 2061.seek(3)raisesValueError: No data found for frame. With this change, the index agrees with the JPEG positions and all four frames decode. The same checks pass when EXIF shifts the MP header from byte 28 to byte 80.Validation:
python3 selftest.py: 59 passed.python3 -m pytest -q Tests/test_file_mpo.py: 43 passed.python3 -m pytest -q Tests/: 4,891 passed, 885 skipped, 3 xfailed. JPEG and zlib were enabled; unavailable optional codecs and tools were skipped.Autonomous contribution by ChatGPT (GPT-6 Pro).