mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-13 18:56:17 +03:00
Merge pull request #10 from radarhere/improved_dds
Moved _Tile from Image to ImageFile
This commit is contained in:
commit
9e5ff7600e
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 861 B |
|
@ -41,7 +41,7 @@ TEST_FILE_UNCOMPRESSED_RGB_WITH_ALPHA = "Tests/images/uncompressed_rgb.dds"
|
||||||
TEST_FILE_DX10_BC1_TYPELESS,
|
TEST_FILE_DX10_BC1_TYPELESS,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
def test_sanity_bc1(image_path):
|
def test_sanity_dxt1_bc1(image_path):
|
||||||
"""Check DXT1 images can be opened"""
|
"""Check DXT1 images can be opened"""
|
||||||
with Image.open(TEST_FILE_DXT1.replace(".dds", ".png")) as target:
|
with Image.open(TEST_FILE_DXT1.replace(".dds", ".png")) as target:
|
||||||
target = target.convert("RGBA")
|
target = target.convert("RGBA")
|
||||||
|
@ -350,12 +350,6 @@ def test_save_unsupported_mode(tmp_path):
|
||||||
im.save(out)
|
im.save(out)
|
||||||
|
|
||||||
|
|
||||||
def test_open_rgb8():
|
|
||||||
with Image.open("Tests/images/rgb8.dds") as im:
|
|
||||||
assert im.mode == "L"
|
|
||||||
assert_image_equal_tofile(im, "Tests/images/rgb8.png")
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("mode", "test_file"),
|
("mode", "test_file"),
|
||||||
[
|
[
|
||||||
|
|
|
@ -9,6 +9,7 @@ The contents of this file are hereby released in the public domain (CC0)
|
||||||
Full text of the CC0 license:
|
Full text of the CC0 license:
|
||||||
https://creativecommons.org/publicdomain/zero/1.0/
|
https://creativecommons.org/publicdomain/zero/1.0/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import io
|
import io
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
|
@ -183,8 +184,8 @@ class DXGI_FORMAT(IntEnum):
|
||||||
P208 = 130
|
P208 = 130
|
||||||
V208 = 131
|
V208 = 131
|
||||||
V408 = 132
|
V408 = 132
|
||||||
SAMPLER_FEEDBACK_MIN_MIP_OPAQUE = 133
|
SAMPLER_FEEDBACK_MIN_MIP_OPAQUE = 189
|
||||||
SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE = 134
|
SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE = 190
|
||||||
|
|
||||||
|
|
||||||
class D3DFMT(IntEnum):
|
class D3DFMT(IntEnum):
|
||||||
|
@ -351,9 +352,7 @@ class DdsImageFile(ImageFile.ImageFile):
|
||||||
# Texture contains uncompressed RGB data
|
# Texture contains uncompressed RGB data
|
||||||
masks = struct.unpack("<4I", header.read(16))
|
masks = struct.unpack("<4I", header.read(16))
|
||||||
masks = {mask: ["R", "G", "B", "A"][i] for i, mask in enumerate(masks)}
|
masks = {mask: ["R", "G", "B", "A"][i] for i, mask in enumerate(masks)}
|
||||||
if bitcount == 8:
|
if bitcount == 24:
|
||||||
self._mode = "L"
|
|
||||||
elif bitcount == 24:
|
|
||||||
self._mode = "RGB"
|
self._mode = "RGB"
|
||||||
rawmode = masks[0x000000FF] + masks[0x0000FF00] + masks[0x00FF0000]
|
rawmode = masks[0x000000FF] + masks[0x0000FF00] + masks[0x00FF0000]
|
||||||
elif bitcount == 32 and pfflags & DDPF.ALPHAPIXELS:
|
elif bitcount == 32 and pfflags & DDPF.ALPHAPIXELS:
|
||||||
|
@ -379,7 +378,7 @@ class DdsImageFile(ImageFile.ImageFile):
|
||||||
self._mode = "P"
|
self._mode = "P"
|
||||||
self.palette = ImagePalette.raw("RGBA", self.fp.read(1024))
|
self.palette = ImagePalette.raw("RGBA", self.fp.read(1024))
|
||||||
elif pfflags & DDPF.FOURCC:
|
elif pfflags & DDPF.FOURCC:
|
||||||
data_offs = header_size + 4
|
offset = header_size + 4
|
||||||
if fourcc == D3DFMT.DXT1:
|
if fourcc == D3DFMT.DXT1:
|
||||||
self._mode = "RGBA"
|
self._mode = "RGBA"
|
||||||
self.pixel_format = "DXT1"
|
self.pixel_format = "DXT1"
|
||||||
|
@ -405,7 +404,7 @@ class DdsImageFile(ImageFile.ImageFile):
|
||||||
self.pixel_format = "BC5"
|
self.pixel_format = "BC5"
|
||||||
n = 5
|
n = 5
|
||||||
elif fourcc == D3DFMT.DX10:
|
elif fourcc == D3DFMT.DX10:
|
||||||
data_offs += 20
|
offset += 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)
|
||||||
|
@ -462,9 +461,11 @@ class DdsImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
extents = (0, 0) + self.size
|
extents = (0, 0) + self.size
|
||||||
if n:
|
if n:
|
||||||
self.tile = [Image._Tile("bcn", extents, data_offs, (n, self.pixel_format))]
|
self.tile = [
|
||||||
|
ImageFile._Tile("bcn", extents, offset, (n, self.pixel_format))
|
||||||
|
]
|
||||||
else:
|
else:
|
||||||
self.tile = [Image._Tile("raw", extents, 0, rawmode or self.mode)]
|
self.tile = [ImageFile._Tile("raw", extents, 0, rawmode or self.mode)]
|
||||||
|
|
||||||
def load_seek(self, pos):
|
def load_seek(self, pos):
|
||||||
pass
|
pass
|
||||||
|
@ -496,8 +497,8 @@ def _save(im, fp, filename):
|
||||||
rgba_mask.append(0xFF000000 if alpha else 0)
|
rgba_mask.append(0xFF000000 if alpha else 0)
|
||||||
|
|
||||||
flags = DDSD.CAPS | DDSD.HEIGHT | DDSD.WIDTH | DDSD.PITCH | DDSD.PIXELFORMAT
|
flags = DDSD.CAPS | DDSD.HEIGHT | DDSD.WIDTH | DDSD.PITCH | DDSD.PIXELFORMAT
|
||||||
bit_count = len(im.getbands()) * 8
|
bitcount = len(im.getbands()) * 8
|
||||||
stride = (im.width * bit_count + 7) // 8
|
pitch = (im.width * bitcount + 7) // 8
|
||||||
|
|
||||||
fp.write(
|
fp.write(
|
||||||
o32(DDS_MAGIC)
|
o32(DDS_MAGIC)
|
||||||
|
@ -507,17 +508,19 @@ def _save(im, fp, filename):
|
||||||
flags, # flags
|
flags, # flags
|
||||||
im.height,
|
im.height,
|
||||||
im.width,
|
im.width,
|
||||||
stride, # pitch
|
pitch,
|
||||||
0, # depth
|
0, # depth
|
||||||
0, # mipmaps
|
0, # mipmaps
|
||||||
)
|
)
|
||||||
+ struct.pack("11I", *((0,) * 11)) # reserved
|
+ struct.pack("11I", *((0,) * 11)) # reserved
|
||||||
# pfsize, pfflags, fourcc, bitcount
|
# pfsize, pfflags, fourcc, bitcount
|
||||||
+ struct.pack("<4I", 32, pixel_flags, 0, bit_count)
|
+ struct.pack("<4I", 32, pixel_flags, 0, bitcount)
|
||||||
+ struct.pack("<4I", *rgba_mask) # dwRGBABitMask
|
+ struct.pack("<4I", *rgba_mask) # dwRGBABitMask
|
||||||
+ struct.pack("<5I", DDSCAPS.TEXTURE, 0, 0, 0, 0)
|
+ struct.pack("<5I", DDSCAPS.TEXTURE, 0, 0, 0, 0)
|
||||||
)
|
)
|
||||||
ImageFile._save(im, fp, [Image._Tile("raw", (0, 0) + im.size, 0, (rawmode, 0, 1))])
|
ImageFile._save(
|
||||||
|
im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, (rawmode, 0, 1))]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix):
|
def _accept(prefix):
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
#
|
#
|
||||||
# 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 atexit
|
import atexit
|
||||||
import builtins
|
import builtins
|
||||||
|
@ -39,7 +38,6 @@ 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 NamedTuple
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from defusedxml import ElementTree
|
from defusedxml import ElementTree
|
||||||
|
@ -208,13 +206,6 @@ if hasattr(core, "DEFAULT_STRATEGY"):
|
||||||
FIXED = core.FIXED
|
FIXED = core.FIXED
|
||||||
|
|
||||||
|
|
||||||
class _Tile(NamedTuple):
|
|
||||||
encoder_name: str
|
|
||||||
extents: tuple[int, int, int, int]
|
|
||||||
offset: int
|
|
||||||
args: tuple
|
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Registries
|
# Registries
|
||||||
|
|
||||||
|
@ -706,7 +697,6 @@ class Image:
|
||||||
|
|
||||||
def __setstate__(self, state):
|
def __setstate__(self, state):
|
||||||
Image.__init__(self)
|
Image.__init__(self)
|
||||||
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
|
||||||
|
|
|
@ -32,6 +32,7 @@ import io
|
||||||
import itertools
|
import itertools
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
|
from typing import NamedTuple
|
||||||
|
|
||||||
from . import Image
|
from . import Image
|
||||||
from ._util import is_path
|
from ._util import is_path
|
||||||
|
@ -78,6 +79,13 @@ def _tilesort(t):
|
||||||
return t[2]
|
return t[2]
|
||||||
|
|
||||||
|
|
||||||
|
class _Tile(NamedTuple):
|
||||||
|
encoder_name: str
|
||||||
|
extents: tuple[int, int, int, int]
|
||||||
|
offset: int
|
||||||
|
args: tuple | str | None
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# ImageFile base class
|
# ImageFile base class
|
||||||
|
@ -521,7 +529,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[_Tile], bufsize, fh, exc=None):
|
||||||
for encoder_name, extents, offset, args in tile:
|
for encoder_name, extents, offset, args in tile:
|
||||||
if offset > 0:
|
if offset > 0:
|
||||||
fp.seek(offset)
|
fp.seek(offset)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user