From 4dfaed893822e5e05e6ce8bff51cb58982869e32 Mon Sep 17 00:00:00 2001 From: sbin Date: Thu, 15 Oct 2026 16:25:39 +0900 Subject: [PATCH 1/2] Fix reading TGA images with a 32-bit colour map --- Tests/test_file_tga.py | 20 ++++++++++++++++++++ src/PIL/TgaImagePlugin.py | 1 + 2 files changed, 21 insertions(+) diff --git a/Tests/test_file_tga.py b/Tests/test_file_tga.py index 03ab96e05a1..21cc93bacd9 100644 --- a/Tests/test_file_tga.py +++ b/Tests/test_file_tga.py @@ -88,6 +88,26 @@ def test_palette_depth_16(tmp_path: Path) -> None: assert_image_equal_tofile(reloaded.convert("RGBA"), "Tests/images/p_16.png") +def test_palette_depth_32() -> None: + data = ( + bytes((0, 1, 1, 0, 0, 2, 0, 32, 0, 0, 0, 0)) + + _binary.o16le(2) + + _binary.o16le(1) + + bytes((8, 0x28)) + # BGRA colour map + + bytes((0, 0, 255, 255, 255, 0, 0, 0)) + + bytes((0, 1)) + ) + with Image.open(BytesIO(data)) as im: + assert im.mode == "P" + assert im.palette is not None + assert im.palette.mode == "RGBA" + + rgba = im.convert("RGBA") + assert rgba.getpixel((0, 0)) == (255, 0, 0, 255) + assert rgba.getpixel((1, 0)) == (0, 0, 255, 0) + + def test_rgba_16() -> None: with Image.open("Tests/images/rgba16.tga") as im: assert im.mode == "RGBA" diff --git a/src/PIL/TgaImagePlugin.py b/src/PIL/TgaImagePlugin.py index 56df65718b5..c1b10f9c50f 100644 --- a/src/PIL/TgaImagePlugin.py +++ b/src/PIL/TgaImagePlugin.py @@ -134,6 +134,7 @@ def _open(self) -> None: self.palette = ImagePalette.raw( "BGRA", bytes(4 * start) + self.fp.read(4 * size) ) + self.palette.mode = "RGBA" else: msg = "unknown TGA map depth" raise SyntaxError(msg) From a60da6d19c6b5a41c574f18f143a1397cd27240b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 16 Oct 2026 08:38:50 +1100 Subject: [PATCH 2/2] Support saving P mode with RGBA palette as TGA images --- Tests/test_file_tga.py | 32 +++++++++++++++----------------- src/PIL/TgaImagePlugin.py | 9 +++++++-- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/Tests/test_file_tga.py b/Tests/test_file_tga.py index 21cc93bacd9..4b55878c917 100644 --- a/Tests/test_file_tga.py +++ b/Tests/test_file_tga.py @@ -88,24 +88,22 @@ def test_palette_depth_16(tmp_path: Path) -> None: assert_image_equal_tofile(reloaded.convert("RGBA"), "Tests/images/p_16.png") -def test_palette_depth_32() -> None: - data = ( - bytes((0, 1, 1, 0, 0, 2, 0, 32, 0, 0, 0, 0)) - + _binary.o16le(2) - + _binary.o16le(1) - + bytes((8, 0x28)) - # BGRA colour map - + bytes((0, 0, 255, 255, 255, 0, 0, 0)) - + bytes((0, 1)) - ) - with Image.open(BytesIO(data)) as im: - assert im.mode == "P" - assert im.palette is not None - assert im.palette.mode == "RGBA" +def test_palette_depth_32(tmp_path: Path) -> None: + im = Image.frombytes("L", (1, 2), bytes((0, 1))) + data = [255, 0, 0, 255, 0, 0, 255, 0] + im.putpalette(data, "RGBA") + + out = tmp_path / "temp.tga" + im.save(out) + + with Image.open(out) as reloaded: + assert reloaded.palette is not None + assert reloaded.palette.mode == "RGBA" + assert reloaded.getpalette(None) == data - rgba = im.convert("RGBA") - assert rgba.getpixel((0, 0)) == (255, 0, 0, 255) - assert rgba.getpixel((1, 0)) == (0, 0, 255, 0) + reloaded_rgba = reloaded.convert("RGBA") + assert reloaded_rgba.getpixel((0, 0)) == (255, 0, 0, 255) + assert reloaded_rgba.getpixel((0, 1)) == (0, 0, 255, 0) def test_rgba_16() -> None: diff --git a/src/PIL/TgaImagePlugin.py b/src/PIL/TgaImagePlugin.py index c1b10f9c50f..8a112d50558 100644 --- a/src/PIL/TgaImagePlugin.py +++ b/src/PIL/TgaImagePlugin.py @@ -225,8 +225,13 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: warnings.warn("id_section has been trimmed to 255 characters") if colormaptype: - palette = im.im.getpalette("RGB", "BGR") - colormaplength, colormapentry = len(palette) // 3, 24 + assert im.palette is not None + if im.palette.mode == "RGBA": + palette = im.im.getpalette("RGBA", "BGRA") + colormaplength, colormapentry = len(palette) // 4, 32 + else: + palette = im.im.getpalette("RGB", "BGR") + colormaplength, colormapentry = len(palette) // 3, 24 else: colormaplength, colormapentry = 0, 0