Handle premultiplied La and RGBa modes in ImageColor.getcolor()#9797
Handle premultiplied La and RGBa modes in ImageColor.getcolor()#9797chuenchen309 wants to merge 4 commits into
Conversation
Both alpha checks test mode[-1] == "A", uppercase only, so the premultiplied
modes La and RGBa fall through and their alpha is dropped: Image.new("La", s,
"red") comes out fully transparent, and RGBa silently forces alpha to 255.
Premultiply with rounding to match convert(); verified against convert() over
all 256 alpha values.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
test_fill[LA] asserted (76, 0): a transparent fill for an opaque "red"
fillcolor. That was a side effect of getcolor("red", "La") returning a
bare graylevel int, so the transform's internal La fill defaulted alpha
to 0. With getcolor now returning (76, 255), the LA fill matches both the
RGBA case and Image.new("LA", ..., "red").
|
CI caught a case I'd missed: For With This follow-up was written by the same AI coding agent as the PR; the human account holder reviews it and is accountable. |
| ("RGB", (255, 0, 0)), | ||
| ("RGBA", (255, 0, 0, 255)), | ||
| ("LA", (76, 0)), | ||
| ("LA", (76, 255)), |
1915e0f to
89beb8f
Compare
| value = rgb | ||
| if mode[-1] == "a": | ||
| # Rounded, to match convert()'s premultiplication. | ||
| value = tuple((band * alpha + 127) // 255 for band in value) |
There was a problem hiding this comment.
I'm not sure what the comment is referring to. This exact calculation method doesn't seem to be in convert() anywhere, and the reason to round the values isn't because of convert() as much as it is because L and RGB related modes in Pillow use integer pixel values.
Changes proposed in this pull request
ImageColor.getcolor()decides whether to append the alpha channel withmode[-1] == "A"(uppercase) in both branches (ImageColor.py:160and:163). The premultiplied modes are spelledLaandRGBa(lowercasea), so they fall through and their alpha is silently dropped:Downstream,
Image.new("La", size, "red")comes out(76, 0)— fully transparent — andImage.new("RGBa", size, "#ff000080")loses the alpha entirely. The uppercase siblingsLA/RGBAhandle this correctly, andLa/RGBaare first-class modes, so this is a gap rather than intentional.mode[-1] == "a"in both branches, premultiplying each band by alpha (rounded, to matchconvert()).test_color_premultiplied, with expected values derived fromconvert()rather than hard-coded:LA/RGBA/L/RGBare unchanged. The change is pure insertion (+9 / +13 test), keeping code and reformatting separate.test_color_premultipliedfails before this change and passes after; theImageColor/ImageDraw/ImageOps/Image/ImagePalettesuites stay green (515 passed);ruffandblackare clean.La/RGBaare uncommon (mostly internalconvert/resize intermediates), so this is a correctness fix for a low-frequency path, not a hot path.Disclosure: this contribution is fully AI-authored and autonomous (Claude Code, acting on this account). An AI found the bug, ran the repro, wrote the fix and the test (expected values derived from
convert()), and wrote this description; the human account holder reviews every change and is accountable for it. The verification above is real and re-runnable from the diff. If this isn't the kind of contribution you want, say so and I'll close it.