diff --git a/Tests/images/tbbn0g04.png b/Tests/images/tbbn0g04.png new file mode 100644 index 00000000000..39a7050d27a Binary files /dev/null and b/Tests/images/tbbn0g04.png differ diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index e767600b5d4..4b5d9424e29 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -355,6 +355,17 @@ def test_save_grayscale_transparency(self, tmp_path: Path) -> None: assert colors is not None assert colors[0][0] == num_transparent + def test_low_bit_grayscale_transparency(self) -> None: + with Image.open("Tests/images/tbbn0g04.png") as im: + assert im.mode == "L" + assert im.info["transparency"] == 255 + assert im.convert("LA").getpixel((0, 0)) == (255, 0) + + for bits in (2, 4): + im2 = Image.new("L", (1, 1)) + with roundtrip(im2, bits=bits, transparency=255) as reloaded: + assert reloaded.info["transparency"] == 255 + def test_save_1_transparency(self, tmp_path: Path) -> None: out = tmp_path / "temp.png" diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index fdbd0bb659c..f36690e0098 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -529,7 +529,12 @@ def chunk_tRNS(self, pos: int, length: int) -> bytes: if self.im_mode == "1": self.im_info["transparency"] = 255 if i16(s) else 0 elif self.im_mode in ("L", "I;16"): - self.im_info["transparency"] = i16(s) + value = i16(s) + if self.im_rawmode in {"L;2", "L;4"}: + # Scale the grey level to match the unpacked 8-bit pixels + max_value = (1 << int(self.im_rawmode[-1])) - 1 + value = int(min(1, value / max_value) * 255) + self.im_info["transparency"] = value elif self.im_mode == "RGB": self.im_info["transparency"] = i16(s), i16(s, 2), i16(s, 4) return s