mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-27 02:16:19 +03:00
Merge pull request #7589 from radarhere/dds_rgb
This commit is contained in:
commit
119885aa66
BIN
Tests/images/bgr15.dds
Normal file
BIN
Tests/images/bgr15.dds
Normal file
Binary file not shown.
BIN
Tests/images/bgr15.png
Normal file
BIN
Tests/images/bgr15.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
@ -32,6 +32,7 @@ TEST_FILE_DX10_R8G8B8A8_UNORM_SRGB = "Tests/images/DXGI_FORMAT_R8G8B8A8_UNORM_SR
|
||||||
TEST_FILE_UNCOMPRESSED_L = "Tests/images/uncompressed_l.dds"
|
TEST_FILE_UNCOMPRESSED_L = "Tests/images/uncompressed_l.dds"
|
||||||
TEST_FILE_UNCOMPRESSED_L_WITH_ALPHA = "Tests/images/uncompressed_la.dds"
|
TEST_FILE_UNCOMPRESSED_L_WITH_ALPHA = "Tests/images/uncompressed_la.dds"
|
||||||
TEST_FILE_UNCOMPRESSED_RGB = "Tests/images/hopper.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"
|
TEST_FILE_UNCOMPRESSED_RGB_WITH_ALPHA = "Tests/images/uncompressed_rgb.dds"
|
||||||
|
|
||||||
|
|
||||||
|
@ -249,6 +250,7 @@ def test_dx10_r8g8b8a8_unorm_srgb():
|
||||||
("L", (128, 128), TEST_FILE_UNCOMPRESSED_L),
|
("L", (128, 128), TEST_FILE_UNCOMPRESSED_L),
|
||||||
("LA", (128, 128), TEST_FILE_UNCOMPRESSED_L_WITH_ALPHA),
|
("LA", (128, 128), TEST_FILE_UNCOMPRESSED_L_WITH_ALPHA),
|
||||||
("RGB", (128, 128), TEST_FILE_UNCOMPRESSED_RGB),
|
("RGB", (128, 128), TEST_FILE_UNCOMPRESSED_RGB),
|
||||||
|
("RGB", (128, 128), TEST_FILE_UNCOMPRESSED_BGR15),
|
||||||
("RGBA", (800, 600), TEST_FILE_UNCOMPRESSED_RGB_WITH_ALPHA),
|
("RGBA", (800, 600), TEST_FILE_UNCOMPRESSED_RGB_WITH_ALPHA),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -341,16 +343,9 @@ def test_palette():
|
||||||
assert_image_equal_tofile(im, "Tests/images/transparent.gif")
|
assert_image_equal_tofile(im, "Tests/images/transparent.gif")
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
def test_unsupported_bitcount():
|
||||||
"test_file",
|
|
||||||
(
|
|
||||||
"Tests/images/unsupported_bitcount_rgb.dds",
|
|
||||||
"Tests/images/unsupported_bitcount_luminance.dds",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
def test_unsupported_bitcount(test_file):
|
|
||||||
with pytest.raises(OSError):
|
with pytest.raises(OSError):
|
||||||
with Image.open(test_file):
|
with Image.open("Tests/images/unsupported_bitcount.dds"):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ from enum import IntEnum, IntFlag
|
||||||
|
|
||||||
from . import Image, ImageFile, ImagePalette
|
from . import Image, ImageFile, ImagePalette
|
||||||
from ._binary import i32le as i32
|
from ._binary import i32le as i32
|
||||||
|
from ._binary import o8
|
||||||
from ._binary import o32le as o32
|
from ._binary import o32le as o32
|
||||||
|
|
||||||
# Magic ("DDS ")
|
# Magic ("DDS ")
|
||||||
|
@ -341,6 +342,7 @@ class DdsImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
flags, height, width = struct.unpack("<3I", header.read(12))
|
flags, height, width = struct.unpack("<3I", header.read(12))
|
||||||
self._size = (width, height)
|
self._size = (width, height)
|
||||||
|
extents = (0, 0) + self.size
|
||||||
|
|
||||||
pitch, depth, mipmaps = struct.unpack("<3I", header.read(12))
|
pitch, depth, mipmaps = struct.unpack("<3I", header.read(12))
|
||||||
struct.unpack("<11I", header.read(44)) # reserved
|
struct.unpack("<11I", header.read(44)) # reserved
|
||||||
|
@ -351,22 +353,16 @@ class DdsImageFile(ImageFile.ImageFile):
|
||||||
rawmode = None
|
rawmode = None
|
||||||
if pfflags & DDPF.RGB:
|
if pfflags & DDPF.RGB:
|
||||||
# Texture contains uncompressed RGB data
|
# Texture contains uncompressed RGB data
|
||||||
masks = struct.unpack("<4I", header.read(16))
|
if pfflags & DDPF.ALPHAPIXELS:
|
||||||
masks = {mask: ["R", "G", "B", "A"][i] for i, mask in enumerate(masks)}
|
|
||||||
if bitcount == 24:
|
|
||||||
self._mode = "RGB"
|
|
||||||
rawmode = masks[0x000000FF] + masks[0x0000FF00] + masks[0x00FF0000]
|
|
||||||
elif bitcount == 32 and pfflags & DDPF.ALPHAPIXELS:
|
|
||||||
self._mode = "RGBA"
|
self._mode = "RGBA"
|
||||||
rawmode = (
|
mask_count = 4
|
||||||
masks[0x000000FF]
|
|
||||||
+ masks[0x0000FF00]
|
|
||||||
+ masks[0x00FF0000]
|
|
||||||
+ masks[0xFF000000]
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
msg = f"Unsupported bitcount {bitcount} for {pfflags}"
|
self._mode = "RGB"
|
||||||
raise OSError(msg)
|
mask_count = 3
|
||||||
|
|
||||||
|
masks = struct.unpack(f"<{mask_count}I", header.read(mask_count * 4))
|
||||||
|
self.tile = [("dds_rgb", extents, 0, (bitcount, masks))]
|
||||||
|
return
|
||||||
elif pfflags & DDPF.LUMINANCE:
|
elif pfflags & DDPF.LUMINANCE:
|
||||||
if bitcount == 8:
|
if bitcount == 8:
|
||||||
self._mode = "L"
|
self._mode = "L"
|
||||||
|
@ -464,7 +460,6 @@ class DdsImageFile(ImageFile.ImageFile):
|
||||||
msg = f"Unknown pixel format flags {pfflags}"
|
msg = f"Unknown pixel format flags {pfflags}"
|
||||||
raise NotImplementedError(msg)
|
raise NotImplementedError(msg)
|
||||||
|
|
||||||
extents = (0, 0) + self.size
|
|
||||||
if n:
|
if n:
|
||||||
self.tile = [
|
self.tile = [
|
||||||
ImageFile._Tile("bcn", extents, offset, (n, self.pixel_format))
|
ImageFile._Tile("bcn", extents, offset, (n, self.pixel_format))
|
||||||
|
@ -476,6 +471,39 @@ class DdsImageFile(ImageFile.ImageFile):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DdsRgbDecoder(ImageFile.PyDecoder):
|
||||||
|
_pulls_fd = True
|
||||||
|
|
||||||
|
def decode(self, buffer):
|
||||||
|
bitcount, masks = self.args
|
||||||
|
|
||||||
|
# Some masks will be padded with zeros, e.g. R 0b11 G 0b1100
|
||||||
|
# Calculate how many zeros each mask is padded with
|
||||||
|
mask_offsets = []
|
||||||
|
# And the maximum value of each channel without the padding
|
||||||
|
mask_totals = []
|
||||||
|
for mask in masks:
|
||||||
|
offset = 0
|
||||||
|
if mask != 0:
|
||||||
|
while mask >> (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 = int.from_bytes(self.fd.read(bytecount), "little")
|
||||||
|
for i, mask in enumerate(masks):
|
||||||
|
masked_value = 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):
|
def _save(im, fp, filename):
|
||||||
if im.mode not in ("RGB", "RGBA", "L", "LA"):
|
if im.mode not in ("RGB", "RGBA", "L", "LA"):
|
||||||
msg = f"cannot write mode {im.mode} as DDS"
|
msg = f"cannot write mode {im.mode} as DDS"
|
||||||
|
@ -533,5 +561,6 @@ def _accept(prefix):
|
||||||
|
|
||||||
|
|
||||||
Image.register_open(DdsImageFile.format, DdsImageFile, _accept)
|
Image.register_open(DdsImageFile.format, DdsImageFile, _accept)
|
||||||
|
Image.register_decoder("dds_rgb", DdsRgbDecoder)
|
||||||
Image.register_save(DdsImageFile.format, _save)
|
Image.register_save(DdsImageFile.format, _save)
|
||||||
Image.register_extension(DdsImageFile.format, ".dds")
|
Image.register_extension(DdsImageFile.format, ".dds")
|
||||||
|
|
Loading…
Reference in New Issue
Block a user