diff --git a/Tests/images/bgr15.dds b/Tests/images/bgr15.dds new file mode 100644 index 00000000000..ba3bbddcae4 Binary files /dev/null and b/Tests/images/bgr15.dds differ diff --git a/Tests/images/bgr15.png b/Tests/images/bgr15.png new file mode 100644 index 00000000000..a15ab5ad256 Binary files /dev/null and b/Tests/images/bgr15.png differ diff --git a/Tests/test_file_dds.py b/Tests/test_file_dds.py index bb9af7967b7..7772956aff8 100644 --- a/Tests/test_file_dds.py +++ b/Tests/test_file_dds.py @@ -26,6 +26,7 @@ TEST_FILE_UNCOMPRESSED_L = "Tests/images/uncompressed_l.dds" TEST_FILE_UNCOMPRESSED_L_WITH_ALPHA = "Tests/images/uncompressed_la.dds" TEST_FILE_UNCOMPRESSED_RGB = "Tests/images/hopper.dds" +TEST_FILE_UNCOMPRESSED_BGR15 = "Tests/images/bgr15.dds" TEST_FILE_UNCOMPRESSED_RGB_WITH_ALPHA = "Tests/images/uncompressed_rgb.dds" @@ -211,6 +212,7 @@ def test_unimplemented_dxgi_format(): ("L", (128, 128), TEST_FILE_UNCOMPRESSED_L), ("LA", (128, 128), TEST_FILE_UNCOMPRESSED_L_WITH_ALPHA), ("RGB", (128, 128), TEST_FILE_UNCOMPRESSED_RGB), + ("RGB", (128, 128), TEST_FILE_UNCOMPRESSED_BGR15), ("RGBA", (800, 600), TEST_FILE_UNCOMPRESSED_RGB_WITH_ALPHA), ], ) diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index 54f358c7f9a..65d9deaa0d4 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -14,6 +14,7 @@ from io import BytesIO from . import Image, ImageFile, ImagePalette +from ._binary import o8 from ._binary import o32le as o32 # Magic ("DDS ") @@ -137,7 +138,6 @@ def _open(self): pfsize, pfflags = struct.unpack("<2I", header.read(8)) fourcc = header.read(4) (bitcount,) = struct.unpack("> (offset + 1) << (offset + 1) == mask: + offset += 1 + mask_offsets.append(offset) + mask_totals.append(mask >> offset) + + data = bytearray() + bytecount = bitcount // 8 + while len(data) < self.state.xsize * self.state.ysize * len(masks): + value = self.fd.read(bytecount) + int_value = sum(value[i] << i * 8 for i in range(bytecount)) + for i, mask in enumerate(masks): + masked_value = int_value & mask + # Remove the zero padding, and scale it to 8 bits + data += o8( + int(((masked_value >> mask_offsets[i]) / mask_totals[i]) * 255) + ) + self.set_as_raw(bytes(data)) + return -1, 0 + + def _save(im, fp, filename): if im.mode not in ("RGB", "RGBA", "L", "LA"): msg = f"cannot write mode {im.mode} as DDS" @@ -291,5 +326,6 @@ def _accept(prefix): Image.register_open(DdsImageFile.format, DdsImageFile, _accept) +Image.register_decoder("dds_rgb", DdsRgbDecoder) Image.register_save(DdsImageFile.format, _save) Image.register_extension(DdsImageFile.format, ".dds")