Repository: python-pillow/Pillow (python)

Issue:
Transparent images don't seem to be handled correctly

I'm using Pillow 12.1.1, Python 3.14.
I was trying to do some image manipulation steps with some VTF textures. I noticed that some of the transparent ones had erroneous results. For example, this is just reading the raw pixel buffer with `Image.frombytes()` and showing it:

<img width="49%" height="49%" alt="Image" src="https://github.com/user-attachments/assets/05f9b948-087a-4e62-bd7b-1ec1182ac267" />

<img width="49%" height="49%" alt="Image" src="https://github.com/user-attachments/assets/f9d0989c-54e3-40fc-bd51-642276ddfaa5" />

Even when I tried to export the texture to TGA format and open it, Pillow was still showing the same result. However, the texture above was displayed fine when I was using RGB mode.

I have attached the .tga image but I have to add a .txt suffix so github can accept to upload it. (Change the extension to .tga if you want to view it)

[door_a.tga.txt](https://github.com/user-attachments/files/26116101/door_a.tga.txt)


Patch:
diff --git a/src/PIL/TgaImagePlugin.py b/src/PIL/TgaImagePlugin.py
index 90d5b5cf4ee..b2989a4b764 100644
--- a/src/PIL/TgaImagePlugin.py
+++ b/src/PIL/TgaImagePlugin.py
@@ -17,11 +17,13 @@
 #
 from __future__ import annotations
 
+import os
 import warnings
 from typing import IO
 
 from . import Image, ImageFile, ImagePalette
 from ._binary import i16le as i16
+from ._binary import i32le as i32
 from ._binary import o8
 from ._binary import o16le as o16
 
@@ -157,6 +159,20 @@ def _open(self) -> None:
             pass  # cannot decode
 
     def load_end(self) -> None:
+        if self.mode == "RGBA":
+            assert self.fp is not None
+            self.fp.seek(-26, os.SEEK_END)
+            footer = self.fp.read(26)
+            if footer.endswith(b"TRUEVISION-XFILE.\x00"):
+                # version 2
+                extension_offset = i32(footer)
+                if extension_offset:
+                    self.fp.seek(extension_offset + 494)
+                    attributes_type = self.fp.read(1)
+                    if attributes_type == b"\x00":
+                        # No alpha
+                        self.im.fillband(3, 255)
+
         if self._flip_horizontally:
             self.im = self.im.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
 


Test patch:
diff --git a/Tests/test_file_tga.py b/Tests/test_file_tga.py
index 277515fd425..7ec562342e1 100644
--- a/Tests/test_file_tga.py
+++ b/Tests/test_file_tga.py
@@ -1,11 +1,12 @@
 from __future__ import annotations
 
 import os
+from io import BytesIO
 from pathlib import Path
 
 import pytest
 
-from PIL import Image, UnidentifiedImageError
+from PIL import Image, UnidentifiedImageError, _binary
 
 from .helper import assert_image_equal, assert_image_equal_tofile, hopper
 
@@ -92,6 +93,25 @@ def test_rgba_16() -> None:
         assert im.getpixel((1, 0)) == (0, 255, 82, 0)
 
 
+def test_v2_no_alpha() -> None:
+    test_file = "Tests/images/tga/common/200x32_rgba_tl_rle.tga"
+    with open(test_file, "rb") as fp:
+        data = fp.read()
+        data += (
+            b"\x00" * 495
+            + _binary.o32le(len(data))
+            + _binary.o32le(0)
+            + b"TRUEVISION-XFILE.\x00"
+        )
+    with Image.open(BytesIO(data)) as im:
+        with Image.open(test_file) as im2:
+            r, g, b = im2.split()[:3]
+        a = Image.new("L", im2.size, 255)
+        expected = Image.merge("RGBA", (r, g, b, a))
+
+        assert_image_equal(im, expected)
+
+
 def test_id_field() -> None:
     # tga file with id field
     test_file = "Tests/images/tga_id_field.tga"