Repository navigation
Fix ColorMap length when saving TIFF images with libtiff - #10178
Merged
radarhere merged 2 commits intoOct 11, 2026
Merged
Conversation
BitsPerSample was only read for the ColorMap stride when its type was TIFF_SHORT, but it is passed as an int and autodetected as TIFF_LONG. The stride then stayed at 2, so libtiff stored green and blue as copies of red, shifted by 2 and 4 entries.
radarhere
reviewed
Oct 11, 2026
Member
|
Thanks very much for catching this before the release! |
radarhere
enabled auto-merge (squash)
October 11, 2026 11:36
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
On main, saving a P or PA image through libtiff writes a ColorMap whose green and blue planes are copies of the red plane, shifted by 2 and 4 entries. This is a regression from #10136, which has not been released yet, so I have not added a release note.
Pillow 12.3.0 and this PR print
(255, 0, 0), but main prints(255, 255, 255). Re-saving an LZW palette TIFF without acompressionargument is affected too, since the compression is kept.#10136 calculates the ColorMap stride from
bits_per_sample, which is only set when BitsPerSample has the typeTIFF_SHORT.Pillow/src/encode.c
Lines 987 to 990 in b01a706
However, BitsPerSample is a core tag, so its type is not looked up, and since
_save()passes it as an int, it is autodetected asTIFF_LONG.Pillow/src/encode.c
Lines 792 to 796 in b01a706
So
bits_per_samplestays at 1, and the stride is 2 instead of 256.Changes proposed in this pull request:
TIFF_SHORT.test_palette_save, compare the RGB content, and also test a PA image. Previously, it only checked that the ColorMap had 768 items.test_colormap_size_mismatch, use a non-zero ColorMap and compare its values. Previously, the ColorMap was all zeros and only its length was checked, so the shift was not visible.Without the
src/encode.cchange, the P and PA cases oftest_palette_saveand the libtiff case oftest_colormap_size_mismatchfail.