diff --git a/Tests/images/p_16.png b/Tests/images/p_16.png index e3588641277..458f7138e80 100644 Binary files a/Tests/images/p_16.png and b/Tests/images/p_16.png differ diff --git a/Tests/images/rgba16.tga b/Tests/images/rgba16.tga new file mode 100644 index 00000000000..3918647a23c Binary files /dev/null and b/Tests/images/rgba16.tga differ diff --git a/Tests/test_file_tga.py b/Tests/test_file_tga.py index ff6dab00dca..a03a6a6e10a 100644 --- a/Tests/test_file_tga.py +++ b/Tests/test_file_tga.py @@ -72,12 +72,21 @@ def test_palette_depth_8(tmp_path: Path) -> None: def test_palette_depth_16(tmp_path: Path) -> None: with Image.open("Tests/images/p_16.tga") as im: - assert_image_equal_tofile(im.convert("RGB"), "Tests/images/p_16.png") + assert im.palette.mode == "RGBA" + assert_image_equal_tofile(im.convert("RGBA"), "Tests/images/p_16.png") out = str(tmp_path / "temp.png") im.save(out) with Image.open(out) as reloaded: - assert_image_equal_tofile(reloaded.convert("RGB"), "Tests/images/p_16.png") + assert_image_equal_tofile(reloaded.convert("RGBA"), "Tests/images/p_16.png") + + +def test_rgba_16() -> None: + with Image.open("Tests/images/rgba16.tga") as im: + assert im.mode == "RGBA" + + assert im.getpixel((0, 0)) == (172, 0, 255, 255) + assert im.getpixel((1, 0)) == (0, 255, 82, 0) def test_id_field() -> None: diff --git a/src/PIL/TgaImagePlugin.py b/src/PIL/TgaImagePlugin.py index f16f075df05..39104aeced9 100644 --- a/src/PIL/TgaImagePlugin.py +++ b/src/PIL/TgaImagePlugin.py @@ -36,7 +36,7 @@ (3, 1): "1", (3, 8): "L", (3, 16): "LA", - (2, 16): "BGR;5", + (2, 16): "BGRA;15Z", (2, 24): "BGR", (2, 32): "BGRA", } @@ -87,9 +87,7 @@ def _open(self) -> None: elif imagetype in (1, 9): self._mode = "P" if colormaptype else "L" elif imagetype in (2, 10): - self._mode = "RGB" - if depth == 32: - self._mode = "RGBA" + self._mode = "RGB" if depth == 24 else "RGBA" else: msg = "unknown TGA mode" raise SyntaxError(msg) @@ -118,15 +116,16 @@ def _open(self) -> None: start, size, mapdepth = i16(s, 3), i16(s, 5), s[7] if mapdepth == 16: self.palette = ImagePalette.raw( - "BGR;15", b"\0" * 2 * start + self.fp.read(2 * size) + "BGRA;15Z", bytes(2 * start) + self.fp.read(2 * size) ) + self.palette.mode = "RGBA" elif mapdepth == 24: self.palette = ImagePalette.raw( - "BGR", b"\0" * 3 * start + self.fp.read(3 * size) + "BGR", bytes(3 * start) + self.fp.read(3 * size) ) elif mapdepth == 32: self.palette = ImagePalette.raw( - "BGRA", b"\0" * 4 * start + self.fp.read(4 * size) + "BGRA", bytes(4 * start) + self.fp.read(4 * size) ) else: msg = "unknown TGA map depth" diff --git a/src/libImaging/Unpack.c b/src/libImaging/Unpack.c index 063bcbbcc48..eaa4374e3e9 100644 --- a/src/libImaging/Unpack.c +++ b/src/libImaging/Unpack.c @@ -718,6 +718,21 @@ ImagingUnpackBGRA15(UINT8 *out, const UINT8 *in, int pixels) { } } +void +ImagingUnpackBGRA15Z(UINT8 *out, const UINT8 *in, int pixels) { + int i, pixel; + /* RGB, rearranged channels, 5/5/5/1 bits per pixel, inverted alpha */ + for (i = 0; i < pixels; i++) { + pixel = in[0] + (in[1] << 8); + out[B] = (pixel & 31) * 255 / 31; + out[G] = ((pixel >> 5) & 31) * 255 / 31; + out[R] = ((pixel >> 10) & 31) * 255 / 31; + out[A] = ~((pixel >> 15) * 255); + out += 4; + in += 2; + } +} + void ImagingUnpackRGB16(UINT8 *out, const UINT8 *in, int pixels) { int i, pixel; @@ -1538,7 +1553,7 @@ static struct { /* flags: "I" inverted data; "R" reversed bit order; "B" big endian byte order (default is little endian); "L" line - interleave, "S" signed, "F" floating point */ + interleave, "S" signed, "F" floating point, "Z" inverted alpha */ /* exception: rawmodes "I" and "F" are always native endian byte order */ @@ -1646,6 +1661,7 @@ static struct { {"RGBA", "RGBA;L", 32, unpackRGBAL}, {"RGBA", "RGBA;15", 16, ImagingUnpackRGBA15}, {"RGBA", "BGRA;15", 16, ImagingUnpackBGRA15}, + {"RGBA", "BGRA;15Z", 16, ImagingUnpackBGRA15Z}, {"RGBA", "RGBA;4B", 16, ImagingUnpackRGBA4B}, {"RGBA", "RGBA;16L", 64, unpackRGBA16L}, {"RGBA", "RGBA;16B", 64, unpackRGBA16B},