Skip to content

Commit b741b77

Browse files
chuenchen309clauderadarhere
authored
Handle premultiplied alpha modes in ImageColor.getcolor() (#9797)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Andrew Murray <radarhere@users.noreply.github.com>
1 parent e7a7ea8 commit b741b77

3 files changed

Lines changed: 22 additions & 8 deletions

File tree

Tests/test_image_transform.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def test_quad(self) -> None:
9494
(
9595
("RGB", (255, 0, 0)),
9696
("RGBA", (255, 0, 0, 255)),
97-
("LA", (76, 0)),
97+
("LA", (76, 255)),
9898
),
9999
)
100100
def test_fill(self, mode: str, expected_pixel: tuple[int, ...]) -> None:

Tests/test_imagecolor.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,18 @@ 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", ("#f00", "#ff000080", "#ff000050"))
204+
def test_color_premultiplied_alpha(
205+
mode: str, premultiplied_mode: str, color: str
206+
) -> None:
207+
im = Image.new(mode, (1, 1), color)
208+
im_premultiplied = im.convert(premultiplied_mode)
209+
expected = im_premultiplied.getpixel((0, 0))
210+
211+
assert ImageColor.getcolor(color, premultiplied_mode) == expected
212+
213+
202214
def test_color_too_long() -> None:
203215
# Arrange
204216
color_too_long = "hsl(" + "1" * 40 + "," + "1" * 40 + "%," + "1" * 40 + "%)"

src/PIL/ImageColor.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -152,17 +152,19 @@ def getcolor(color: str, mode: str) -> int | tuple[int, ...]:
152152
r, g, b = rgb
153153
h, s, v = rgb_to_hsv(r / 255, g / 255, b / 255)
154154
return int(h * 255), int(s * 255), int(v * 255)
155-
elif Image.getmodebase(mode) == "L":
155+
if Image.getmodebase(mode) == "L":
156156
r, g, b = rgb
157157
# ITU-R Recommendation 601-2 for nonlinear RGB
158158
# scaled to 24 bits to match the convert's implementation.
159159
graylevel = (r * 19595 + g * 38470 + b * 7471 + 0x8000) >> 16
160-
if mode[-1] == "A":
161-
return graylevel, alpha
162-
return graylevel
163-
elif mode[-1] == "A":
164-
return rgb + (alpha,)
165-
return rgb
160+
value: tuple[int, ...] = (graylevel,)
161+
else:
162+
value = rgb
163+
if mode[-1] == "a":
164+
value = tuple(round(band * alpha / 255) for band in value)
165+
if mode[-1] in ("A", "a"):
166+
return value + (alpha,)
167+
return value[0] if len(value) == 1 else value
166168

167169

168170
colormap: dict[str, str | tuple[int, int, int]] = {

0 commit comments

Comments
 (0)