Skip to content

Commit 072f1e9

Browse files
chuenchen309claude
andcommitted
Handle the premultiplied La and RGBa modes in ImageColor.getcolor()
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>
1 parent be48bcb commit 072f1e9

2 files changed

Lines changed: 22 additions & 0 deletions

File tree

Tests/test_imagecolor.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,19 @@ def test_color_hsv() -> None:
199199
assert (170, 255, 255) == ImageColor.getcolor("hsv(240, 100%, 100%)", "HSV")
200200

201201

202+
@pytest.mark.parametrize("mode, premultiplied_mode", (("LA", "La"), ("RGBA", "RGBa")))
203+
@pytest.mark.parametrize("color", ("red", "#ff000080"))
204+
def test_color_premultiplied(mode: str, premultiplied_mode: str, color: str) -> None:
205+
# The premultiplied modes are spelled with a lowercase "a", so an
206+
# uppercase-only check drops their alpha. Compare against convert().
207+
expected = (
208+
Image.new(mode, (1, 1), color).convert(premultiplied_mode).getpixel((0, 0))
209+
)
210+
211+
assert ImageColor.getcolor(color, premultiplied_mode) == expected
212+
assert Image.new(premultiplied_mode, (1, 1), color).getpixel((0, 0)) == expected
213+
214+
202215
def test_color_too_long() -> None:
203216
# Arrange
204217
color_too_long = "hsl(" + "1" * 40 + "," + "1" * 40 + "%," + "1" * 40 + "%)"

src/PIL/ImageColor.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,21 @@ def getcolor(color: str, mode: str) -> int | tuple[int, ...]:
159159
graylevel = (r * 19595 + g * 38470 + b * 7471 + 0x8000) >> 16
160160
if mode[-1] == "A":
161161
return graylevel, alpha
162+
if mode[-1] == "a":
163+
return _premultiply(graylevel, alpha), alpha
162164
return graylevel
163165
elif mode[-1] == "A":
164166
return rgb + (alpha,)
167+
elif mode[-1] == "a":
168+
return tuple(_premultiply(band, alpha) for band in rgb) + (alpha,)
165169
return rgb
166170

167171

172+
def _premultiply(band: int, alpha: int) -> int:
173+
# Rounded, to match convert()'s premultiplication.
174+
return (band * alpha + 127) // 255
175+
176+
168177
colormap: dict[str, str | tuple[int, int, int]] = {
169178
# X11 colour table from https://drafts.csswg.org/css-color-4/, with
170179
# gray/grey spelling issues fixed. This is a superset of HTML 4.0

0 commit comments

Comments
 (0)