Apply suggestion for PR

This commit is contained in:
REDxEYE 2023-10-12 19:09:28 +03:00
parent 777c54a4d1
commit a5dde8b1c4
15 changed files with 123 additions and 37 deletions

View File

Before

Width:  |  Height:  |  Size: 969 B

After

Width:  |  Height:  |  Size: 969 B

View File

Before

Width:  |  Height:  |  Size: 861 B

After

Width:  |  Height:  |  Size: 861 B

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 862 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -10,8 +10,8 @@ from .helper import assert_image_equal, assert_image_equal_tofile, hopper
TEST_FILE_DXT1 = "Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.dds" TEST_FILE_DXT1 = "Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.dds"
TEST_FILE_DXT3 = "Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.dds" TEST_FILE_DXT3 = "Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.dds"
TEST_FILE_DXT5 = "Tests/images/dxt5-argb-8bbp-interpolatedalpha_MipMaps-1.dds" TEST_FILE_DXT5 = "Tests/images/dxt5-argb-8bbp-interpolatedalpha_MipMaps-1.dds"
TEST_FILE_ATI1 = "Tests/images/ati1.dds" TEST_FILE_ATI1 = "Tests/images/mode-ati1.dds"
TEST_FILE_ATI2 = "Tests/images/ati2.dds" TEST_FILE_ATI2 = "Tests/images/mode-ati2.dds"
TEST_FILE_DX10_BC5_TYPELESS = "Tests/images/bc5_typeless.dds" TEST_FILE_DX10_BC5_TYPELESS = "Tests/images/bc5_typeless.dds"
TEST_FILE_DX10_BC5_UNORM = "Tests/images/bc5_unorm.dds" TEST_FILE_DX10_BC5_UNORM = "Tests/images/bc5_unorm.dds"
TEST_FILE_DX10_BC5_SNORM = "Tests/images/bc5_snorm.dds" TEST_FILE_DX10_BC5_SNORM = "Tests/images/bc5_snorm.dds"
@ -314,10 +314,10 @@ def test_save_unsupported_mode(tmp_path):
@pytest.mark.parametrize( @pytest.mark.parametrize(
("mode", "test_file"), ("mode", "test_file"),
( (
("L", "Tests/images/l.dds"), ("L", "Tests/images/mode-l.dds"),
("LA", "Tests/images/la.dds"), ("LA", "Tests/images/mode-la.dds"),
("RGB", "Tests/images/rgb.dds"), ("RGB", "Tests/images/mode-rgb.dds"),
("RGBA", "Tests/images/rgba.dds"), ("RGBA", "Tests/images/mode-rgba.dds"),
), ),
) )
def test_open(mode, test_file): def test_open(mode, test_file):
@ -333,7 +333,7 @@ def test_open(mode, test_file):
("LA", "Tests/images/uncompressed_la.png"), ("LA", "Tests/images/uncompressed_la.png"),
("RGB", "Tests/images/hopper.png"), ("RGB", "Tests/images/hopper.png"),
("RGBA", "Tests/images/pil123rgba.png"), ("RGBA", "Tests/images/pil123rgba.png"),
("L", "Tests/images/ati1.dds"), ("L", "Tests/images/mode-ati1.dds"),
], ],
) )
def test_save(mode, test_file, tmp_path): def test_save(mode, test_file, tmp_path):

View File

@ -253,6 +253,8 @@ class D3DFMT(IntEnum):
DXT4 = i32(b"DXT4") DXT4 = i32(b"DXT4")
DXT5 = i32(b"DXT5") DXT5 = i32(b"DXT5")
DX10 = i32(b"DX10") DX10 = i32(b"DX10")
BC4S = i32(b"BC4S")
BC4U = i32(b"BC4U")
BC5S = i32(b"BC5S") BC5S = i32(b"BC5S")
BC5U = i32(b"BC5U") BC5U = i32(b"BC5U")
ATI1 = i32(b"ATI1") ATI1 = i32(b"ATI1")
@ -260,6 +262,81 @@ class D3DFMT(IntEnum):
MULTI2_ARGB8 = i32(b"MET1") MULTI2_ARGB8 = i32(b"MET1")
# Backward compat layer
DDSD_CAPS = DDSD.CAPS
DDSD_HEIGHT = DDSD.HEIGHT
DDSD_WIDTH = DDSD.WIDTH
DDSD_PITCH = DDSD.PITCH
DDSD_PIXELFORMAT = DDSD.PIXELFORMAT
DDSD_MIPMAPCOUNT = DDSD.MIPMAPCOUNT
DDSD_LINEARSIZE = DDSD.LINEARSIZE
DDSD_DEPTH = DDSD.DEPTH
DDSCAPS_COMPLEX = DDSCAPS.COMPLEX
DDSCAPS_TEXTURE = DDSCAPS.TEXTURE
DDSCAPS_MIPMAP = DDSCAPS.MIPMAP
DDSCAPS2_CUBEMAP = DDSCAPS2.CUBEMAP
DDSCAPS2_CUBEMAP_POSITIVEX = DDSCAPS2.CUBEMAP_POSITIVEX
DDSCAPS2_CUBEMAP_NEGATIVEX = DDSCAPS2.CUBEMAP_NEGATIVEX
DDSCAPS2_CUBEMAP_POSITIVEY = DDSCAPS2.CUBEMAP_POSITIVEY
DDSCAPS2_CUBEMAP_NEGATIVEY = DDSCAPS2.CUBEMAP_NEGATIVEY
DDSCAPS2_CUBEMAP_POSITIVEZ = DDSCAPS2.CUBEMAP_POSITIVEZ
DDSCAPS2_CUBEMAP_NEGATIVEZ = DDSCAPS2.CUBEMAP_NEGATIVEZ
DDSCAPS2_VOLUME = DDSCAPS2.VOLUME
DDPF_ALPHAPIXELS = DDPF.ALPHAPIXELS
DDPF_ALPHA = DDPF.ALPHA
DDPF_FOURCC = DDPF.FOURCC
DDPF_PALETTEINDEXED8 = DDPF.PALETTEINDEXED8
DDPF_RGB = DDPF.RGB
DDPF_LUMINANCE = DDPF.LUMINANCE
DDS_FOURCC = DDPF_FOURCC
DDS_RGB = DDPF_RGB
DDS_RGBA = DDPF_RGB | DDPF_ALPHAPIXELS
DDS_LUMINANCE = DDPF_LUMINANCE
DDS_LUMINANCEA = DDPF_LUMINANCE | DDPF_ALPHAPIXELS
DDS_ALPHA = DDPF_ALPHA
DDS_PAL8 = DDPF_PALETTEINDEXED8
DDS_HEADER_FLAGS_TEXTURE = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT
DDS_HEADER_FLAGS_MIPMAP = DDSD_MIPMAPCOUNT
DDS_HEADER_FLAGS_VOLUME = DDSD_DEPTH
DDS_HEADER_FLAGS_PITCH = DDSD_PITCH
DDS_HEADER_FLAGS_LINEARSIZE = DDSD_LINEARSIZE
DDS_HEIGHT = DDSD_HEIGHT
DDS_WIDTH = DDSD_WIDTH
DDS_SURFACE_FLAGS_TEXTURE = DDSCAPS_TEXTURE
DDS_SURFACE_FLAGS_MIPMAP = DDSCAPS_COMPLEX | DDSCAPS_MIPMAP
DDS_SURFACE_FLAGS_CUBEMAP = DDSCAPS_COMPLEX
DDS_CUBEMAP_POSITIVEX = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX
DDS_CUBEMAP_NEGATIVEX = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX
DDS_CUBEMAP_POSITIVEY = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY
DDS_CUBEMAP_NEGATIVEY = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY
DDS_CUBEMAP_POSITIVEZ = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ
DDS_CUBEMAP_NEGATIVEZ = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ
DXT1_FOURCC = D3DFMT.DXT1
DXT3_FOURCC = D3DFMT.DXT3
DXT5_FOURCC = D3DFMT.DXT5
DXGI_FORMAT_R8G8B8A8_TYPELESS = DXGI_FORMAT.R8G8B8A8_TYPELESS
DXGI_FORMAT_R8G8B8A8_UNORM = DXGI_FORMAT.R8G8B8A8_UNORM
DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = DXGI_FORMAT.R8G8B8A8_UNORM_SRGB
DXGI_FORMAT_BC5_TYPELESS = DXGI_FORMAT.BC5_TYPELESS
DXGI_FORMAT_BC5_UNORM = DXGI_FORMAT.BC5_UNORM
DXGI_FORMAT_BC5_SNORM = DXGI_FORMAT.BC5_SNORM
DXGI_FORMAT_BC6H_UF16 = DXGI_FORMAT.BC6H_UF16
DXGI_FORMAT_BC6H_SF16 = DXGI_FORMAT.BC6H_SF16
DXGI_FORMAT_BC7_TYPELESS = DXGI_FORMAT.BC7_TYPELESS
DXGI_FORMAT_BC7_UNORM = DXGI_FORMAT.BC7_UNORM
DXGI_FORMAT_BC7_UNORM_SRGB = DXGI_FORMAT.BC7_UNORM_SRGB
class DdsImageFile(ImageFile.ImageFile): class DdsImageFile(ImageFile.ImageFile):
format = "DDS" format = "DDS"
format_description = "DirectDraw Surface" format_description = "DirectDraw Surface"
@ -311,6 +388,13 @@ class DdsImageFile(ImageFile.ImageFile):
else: else:
msg = f"Unsupported bitcount {bitcount} for {pfflags_}" msg = f"Unsupported bitcount {bitcount} for {pfflags_}"
raise OSError(msg) raise OSError(msg)
elif pfflags & DDPF.ALPHA:
if bitcount == 8:
self.mode = "L"
self.tile = [("raw", extents, 0, ("L", 0, 1))]
else:
msg = f"Unsupported bitcount {bitcount} for {pfflags_}"
raise OSError(msg)
elif pfflags & DDPF.LUMINANCE: elif pfflags & DDPF.LUMINANCE:
if bitcount == 8: if bitcount == 8:
self.mode = "L" self.mode = "L"
@ -331,68 +415,72 @@ class DdsImageFile(ImageFile.ImageFile):
if fourcc == D3DFMT.DXT1: if fourcc == D3DFMT.DXT1:
self.mode = "RGBA" self.mode = "RGBA"
self.pixel_format = "DXT1" self.pixel_format = "DXT1"
tile = Image.Tile("bcn", extents, data_offs, (1, self.pixel_format)) tile = Image._Tile("bcn", extents, data_offs, (1, self.pixel_format))
elif fourcc == D3DFMT.DXT3: elif fourcc == D3DFMT.DXT3:
self.mode = "RGBA" self.mode = "RGBA"
self.pixel_format = "DXT3" self.pixel_format = "DXT3"
tile = Image.Tile("bcn", extents, data_offs, (2, self.pixel_format)) tile = Image._Tile("bcn", extents, data_offs, (2, self.pixel_format))
elif fourcc == D3DFMT.DXT5: elif fourcc == D3DFMT.DXT5:
self.mode = "RGBA" self.mode = "RGBA"
self.pixel_format = "DXT5" self.pixel_format = "DXT5"
tile = Image.Tile("bcn", extents, data_offs, (3, self.pixel_format)) tile = Image._Tile("bcn", extents, data_offs, (3, self.pixel_format))
elif fourcc == D3DFMT.ATI1: elif fourcc == D3DFMT.ATI1 or fourcc == D3DFMT.BC4U:
self.mode = "L" self.mode = "L"
self.pixel_format = "BC4" self.pixel_format = "BC4"
tile = Image.Tile("bcn", extents, data_offs, (4, self.pixel_format)) tile = Image._Tile("bcn", extents, data_offs, (4, self.pixel_format))
elif fourcc == D3DFMT.BC5S: elif fourcc == D3DFMT.BC5S:
self.mode = "RGB" self.mode = "RGB"
self.pixel_format = "BC5S" self.pixel_format = "BC5S"
tile = Image.Tile("bcn", extents, data_offs, (5, self.pixel_format)) tile = Image._Tile("bcn", extents, data_offs, (5, self.pixel_format))
elif fourcc == D3DFMT.BC5U: elif fourcc == D3DFMT.BC5U:
self.mode = "RGB" self.mode = "RGB"
self.pixel_format = "BC5U" self.pixel_format = "BC5U"
tile = Image.Tile("bcn", extents, data_offs, (5, self.pixel_format)) tile = Image._Tile("bcn", extents, data_offs, (5, self.pixel_format))
elif fourcc == D3DFMT.ATI2: elif fourcc == D3DFMT.ATI2:
self.mode = "RGB" self.mode = "RGB"
self.pixel_format = "BC5" self.pixel_format = "BC5"
tile = Image.Tile("bcn", extents, data_offs, (5, self.pixel_format)) tile = Image._Tile("bcn", extents, data_offs, (5, self.pixel_format))
elif fourcc == D3DFMT.DX10: elif fourcc == D3DFMT.DX10:
data_offs += 20 data_offs += 20
# ignoring flags which pertain to volume textures and cubemaps # ignoring flags which pertain to volume textures and cubemaps
(dxgi_format,) = struct.unpack("<I", self.fp.read(4)) (dxgi_format,) = struct.unpack("<I", self.fp.read(4))
self.fp.read(16) self.fp.read(16)
if dxgi_format in (DXGI_FORMAT.BC5_TYPELESS, DXGI_FORMAT.BC5_UNORM): if dxgi_format in (DXGI_FORMAT.BC1_UNORM, DXGI_FORMAT.BC1_UNORM_SRGB, DXGI_FORMAT.BC1_TYPELESS):
self.mode = "RGBA"
self.pixel_format = "BC1"
tile = Image._Tile("bcn", extents, data_offs, (1, self.pixel_format))
elif dxgi_format in (DXGI_FORMAT.BC5_TYPELESS, DXGI_FORMAT.BC5_UNORM):
self.mode = "RGB" self.mode = "RGB"
self.pixel_format = "BC5" self.pixel_format = "BC5"
tile = Image.Tile("bcn", extents, data_offs, (5, self.pixel_format)) tile = Image._Tile("bcn", extents, data_offs, (5, self.pixel_format))
elif dxgi_format == DXGI_FORMAT.BC5_SNORM: elif dxgi_format == DXGI_FORMAT.BC5_SNORM:
self.mode = "RGB" self.mode = "RGB"
self.pixel_format = "BC5S" self.pixel_format = "BC5S"
tile = Image.Tile("bcn", extents, data_offs, (5, self.pixel_format)) tile = Image._Tile("bcn", extents, data_offs, (5, self.pixel_format))
elif dxgi_format == DXGI_FORMAT.BC6H_UF16: elif dxgi_format == DXGI_FORMAT.BC6H_UF16:
self.mode = "RGB" self.mode = "RGB"
self.pixel_format = "BC6H" self.pixel_format = "BC6H"
tile = Image.Tile("bcn", extents, data_offs, (6, self.pixel_format)) tile = Image._Tile("bcn", extents, data_offs, (6, self.pixel_format))
elif dxgi_format == DXGI_FORMAT.BC6H_SF16: elif dxgi_format == DXGI_FORMAT.BC6H_SF16:
self.mode = "RGB" self.mode = "RGB"
self.pixel_format = "BC6HS" self.pixel_format = "BC6HS"
tile = Image.Tile("bcn", extents, data_offs, (6, self.pixel_format)) tile = Image._Tile("bcn", extents, data_offs, (6, self.pixel_format))
elif dxgi_format in (DXGI_FORMAT.BC7_TYPELESS, DXGI_FORMAT.BC7_UNORM): elif dxgi_format in (DXGI_FORMAT.BC7_TYPELESS, DXGI_FORMAT.BC7_UNORM):
self.mode = "RGBA" self.mode = "RGBA"
self.pixel_format = "BC7" self.pixel_format = "BC7"
tile = Image.Tile("bcn", extents, data_offs, (7, self.pixel_format)) tile = Image._Tile("bcn", extents, data_offs, (7, self.pixel_format))
elif dxgi_format == DXGI_FORMAT.BC7_UNORM_SRGB: elif dxgi_format == DXGI_FORMAT.BC7_UNORM_SRGB:
self.mode = "RGBA" self.mode = "RGBA"
self.pixel_format = "BC7" self.pixel_format = "BC7"
self.info["gamma"] = 1 / 2.2 self.info["gamma"] = 1 / 2.2
tile = Image.Tile("bcn", extents, data_offs, (7, self.pixel_format)) tile = Image._Tile("bcn", extents, data_offs, (7, self.pixel_format))
elif dxgi_format in ( elif dxgi_format in (
DXGI_FORMAT.R8G8B8A8_TYPELESS, DXGI_FORMAT.R8G8B8A8_TYPELESS,
DXGI_FORMAT.R8G8B8A8_UNORM, DXGI_FORMAT.R8G8B8A8_UNORM,
DXGI_FORMAT.R8G8B8A8_UNORM_SRGB, DXGI_FORMAT.R8G8B8A8_UNORM_SRGB,
): ):
self.mode = "RGBA" self.mode = "RGBA"
tile = Image.Tile("raw", extents, 0, ("RGBA", 0, 1)) tile = Image._Tile("raw", extents, 0, ("RGBA", 0, 1))
if dxgi_format == DXGI_FORMAT.R8G8B8A8_UNORM_SRGB: if dxgi_format == DXGI_FORMAT.R8G8B8A8_UNORM_SRGB:
self.info["gamma"] = 1 / 2.2 self.info["gamma"] = 1 / 2.2
else: else:
@ -457,7 +545,7 @@ def _save(im, fp, filename):
+ struct.pack("<IIIII", DDSCAPS.TEXTURE, 0, 0, 0, 0) + struct.pack("<IIIII", DDSCAPS.TEXTURE, 0, 0, 0, 0)
) )
mode = "LA" if im.mode == "LA" else im.mode[::-1] mode = "LA" if im.mode == "LA" else im.mode[::-1]
ImageFile._save(im, fp, [Image.Tile("raw", (0, 0) + im.size, 0, (mode, 0, 1))]) ImageFile._save(im, fp, [Image._Tile("raw", (0, 0) + im.size, 0, (mode, 0, 1))])
def _accept(prefix): def _accept(prefix):

View File

@ -38,7 +38,7 @@ import warnings
from collections.abc import Callable, MutableMapping from collections.abc import Callable, MutableMapping
from enum import IntEnum from enum import IntEnum
from pathlib import Path from pathlib import Path
from typing import List, NamedTuple, Tuple from typing import NamedTuple
try: try:
import defusedxml.ElementTree as ElementTree import defusedxml.ElementTree as ElementTree
@ -207,15 +207,13 @@ if hasattr(core, "DEFAULT_STRATEGY"):
RLE = core.RLE RLE = core.RLE
FIXED = core.FIXED FIXED = core.FIXED
Tile = NamedTuple(
"Tile", class _Tile(NamedTuple):
[ encoder_name: str
("encoder_name", str), extents: tuple[int, int, int, int]
("extents", Tuple[int, int, int, int]), offset: int
("offset", int), tile_args: tuple
("tile_args", Tuple),
],
)
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# Registries # Registries
@ -702,7 +700,7 @@ class Image:
def __setstate__(self, state): def __setstate__(self, state):
Image.__init__(self) Image.__init__(self)
self.tile: List[Tile] = [] self.tile: list[_Tile] = []
info, mode, size, palette, data = state info, mode, size, palette, data = state
self.info = info self.info = info
self.mode = mode self.mode = mode

View File

@ -26,12 +26,12 @@
# #
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from __future__ import annotations
import io import io
import itertools import itertools
import struct import struct
import sys import sys
from typing import List
from . import Image from . import Image
from ._util import is_path from ._util import is_path
@ -522,7 +522,7 @@ def _save(im, fp, tile, bufsize=0):
fp.flush() fp.flush()
def _encode_tile(im, fp, tile: List[Image.Tile], bufsize, fh, exc=None): def _encode_tile(im, fp, tile: list[Image._Tile], bufsize, fh, exc=None):
for encoder_name, extents, offset, tile_args in tile: for encoder_name, extents, offset, tile_args in tile:
if offset > 0: if offset > 0:
fp.seek(offset) fp.seek(offset)