From 662cb229c2eab6a6407b06298b3bd3989ae13d55 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 21 Nov 2023 20:24:00 +1100 Subject: [PATCH 1/5] Updated variable name to match tile --- src/PIL/DdsImagePlugin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index 30f021b7d..407d33d85 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -379,7 +379,7 @@ class DdsImageFile(ImageFile.ImageFile): self._mode = "P" self.palette = ImagePalette.raw("RGBA", self.fp.read(1024)) elif pfflags & DDPF.FOURCC: - data_offs = header_size + 4 + offset = header_size + 4 if fourcc == D3DFMT.DXT1: self._mode = "RGBA" self.pixel_format = "DXT1" @@ -405,7 +405,7 @@ class DdsImageFile(ImageFile.ImageFile): self.pixel_format = "BC5" n = 5 elif fourcc == D3DFMT.DX10: - data_offs += 20 + offset += 20 # ignoring flags which pertain to volume textures and cubemaps (dxgi_format,) = struct.unpack(" Date: Tue, 21 Nov 2023 22:07:36 +1100 Subject: [PATCH 2/5] args may be a string or None --- src/PIL/Image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 3f0863b18..b8b8fcd81 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -212,7 +212,7 @@ class _Tile(NamedTuple): encoder_name: str extents: tuple[int, int, int, int] offset: int - args: tuple + args: tuple | str | None # -------------------------------------------------------------------- From ab96324c1260d82fb3f654efa8f527bc28800e4b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 2 Dec 2023 00:35:40 +1100 Subject: [PATCH 3/5] Removed support for RGB bitcount 8 --- Tests/images/rgb8.dds | Bin 16512 -> 0 bytes Tests/images/rgb8.png | Bin 861 -> 0 bytes Tests/test_file_dds.py | 8 +------- src/PIL/DdsImagePlugin.py | 4 +--- 4 files changed, 2 insertions(+), 10 deletions(-) delete mode 100644 Tests/images/rgb8.dds delete mode 100644 Tests/images/rgb8.png diff --git a/Tests/images/rgb8.dds b/Tests/images/rgb8.dds deleted file mode 100644 index 8193e8e5ac64c3f6921bb59ba3a4fe33910bf028..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16512 zcmeH}&21Yo5QR54-x7CVsD(6;gE|P@O%ATd#kmEAKVt$jocD$+L0AYH0=7iGCBDax zi^ZPLf8YLX+xGLZAA{&^`M-+$Xy7<63vfY*HW0rSs3U`_*p z!wLe26$E}|1x2qD(^b*y#N>)zCngW36W$b<@TS0o-&Me&D+LZ+DRAg}6af5C0PsTr zz%MH}m=EzH0B&Cd!0oRIAox~*;9CKLUn+1gpW=%Ey8R-6Zht|*lk@4t;7e?I^C@!$UZ=Lb+;8)<=((>pMD?W{4q0ZM*vJ}c1blL9R#0$Lva zg9c7$6p);YLU}G{hlTs3g=g`eL0fLEUTfcft}U`_*p!wLe26$E}|1x2qD(^b*y#N>)zCngW36W$b< z@TS0o-&Me&D+LZ+DRAg}6af5C0PsTrz%MH}m=EzH0B&Cd!0oRIAox~*;9CKLUn+1g zpW=%Ey8R-6Zht|*lk@4t;7e?I^C@!$UZ=Lb+;8)<=( z(>pMD?W{4q0ZM*vJ}c1blL9R#0$Lvag9c7$6p); zYLU}G{hlTs3g=g`ZA+z$;(-fccL;U`_*p!wLe2 z6$E}|1x2qD(^b*y#N>)zCngW36W$b<@TS0o-&Me&D+LZ+DRAg}6af5C0PsTrz%MH} zm=EzH0B&Cd!0oRIAox~*;9CKLUn+1gpW=%Ey8R-6Zht|*lk@4t;7e?I^C@!$UZ=Lb+;8)<=((>pMD?W{4q0ZM*vJ}c1blL9R#0$Lvag9c7$6p);YLU}G{hl vTs3g=g`cDXyizK_Tq?jkgEWu^(m)zW18E=)q=7V$2GT$pNCRo$5Ci`K#A3DB diff --git a/Tests/images/rgb8.png b/Tests/images/rgb8.png deleted file mode 100644 index 9d22a26a446d3dbdfd8f9c931ea466f6c6424e90..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 861 zcmeAS@N?(olHy`uVBq!ia0vp^4Is<`Bp9BB+KDqTFspdFIEGZrc^h?ls*<5Vur)JB zqlg-7P(pKyh(}`Z41qR*8_gT%*K1u(y=E67zCX-RIr5$RbYFRmk9~(3o90{0iTo?q zZokIPVc}P`n#F#$cTRI}Z`b*cHdxb-Qkj7%xN9XC&Z5~v}sUc zQF_LyDDi8i`mdSklfSkId~vT6XS8VuNn~8YHGhS2Q^L#RQrG)UyL@3X&}x&GB>;Tib{;+mb; z9E?oS*}aKX$Uxa5;eK2CjmcMXGaH^hIH47DKd5laQEeT@eua-ZO-D9&R()gjOuir% zvW=}bGBx``)z5EfnVi1_vX=2LOsg_|ey?EeT|36sB|Uh!dQU<13AVi4r5g)!U-!1I-}Y|HguFw3`xCAHw!QqqenxjbTuMSUDiOXag;?62vFzD;OU-N3|GNKs$H|s>F&hgWVD=Cdb6BTa*mH8 zSvtelvqpRLzjllcxGA-Tb?VxK$@jOLdwX91dKc*H#c{mLBht%bs(#FW&Mb`zE`Q|r X-moxli&n>DP`>eW^>bP0l+XkK*RXjv diff --git a/Tests/test_file_dds.py b/Tests/test_file_dds.py index da7260cbf..e0f9fe7de 100644 --- a/Tests/test_file_dds.py +++ b/Tests/test_file_dds.py @@ -41,7 +41,7 @@ TEST_FILE_UNCOMPRESSED_RGB_WITH_ALPHA = "Tests/images/uncompressed_rgb.dds" TEST_FILE_DX10_BC1_TYPELESS, ), ) -def test_sanity_bc1(image_path): +def test_sanity_dxt1_bc1(image_path): """Check DXT1 images can be opened""" with Image.open(TEST_FILE_DXT1.replace(".dds", ".png")) as target: target = target.convert("RGBA") @@ -350,12 +350,6 @@ def test_save_unsupported_mode(tmp_path): 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( ("mode", "test_file"), [ diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index 407d33d85..bdcb8da36 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -351,9 +351,7 @@ class DdsImageFile(ImageFile.ImageFile): # Texture contains uncompressed RGB data masks = struct.unpack("<4I", header.read(16)) masks = {mask: ["R", "G", "B", "A"][i] for i, mask in enumerate(masks)} - if bitcount == 8: - self._mode = "L" - elif bitcount == 24: + if bitcount == 24: self._mode = "RGB" rawmode = masks[0x000000FF] + masks[0x0000FF00] + masks[0x00FF0000] elif bitcount == 32 and pfflags & DDPF.ALPHAPIXELS: From 5aadeb5004588f2b4e9be73e61e978586b5192f5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 2 Dec 2023 01:24:29 +1100 Subject: [PATCH 4/5] Moved _Tile to ImageFile --- src/PIL/DdsImagePlugin.py | 19 ++++++++++++------- src/PIL/Image.py | 10 ---------- src/PIL/ImageFile.py | 10 +++++++++- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index bdcb8da36..e8d5a8b0e 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -9,6 +9,7 @@ The contents of this file are hereby released in the public domain (CC0) Full text of the CC0 license: https://creativecommons.org/publicdomain/zero/1.0/ """ + import io import struct import sys @@ -460,9 +461,11 @@ class DdsImageFile(ImageFile.ImageFile): extents = (0, 0) + self.size if n: - self.tile = [Image._Tile("bcn", extents, offset, (n, self.pixel_format))] + self.tile = [ + ImageFile._Tile("bcn", extents, offset, (n, self.pixel_format)) + ] 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): pass @@ -494,8 +497,8 @@ def _save(im, fp, filename): rgba_mask.append(0xFF000000 if alpha else 0) flags = DDSD.CAPS | DDSD.HEIGHT | DDSD.WIDTH | DDSD.PITCH | DDSD.PIXELFORMAT - bit_count = len(im.getbands()) * 8 - stride = (im.width * bit_count + 7) // 8 + bitcount = len(im.getbands()) * 8 + pitch = (im.width * bitcount + 7) // 8 fp.write( o32(DDS_MAGIC) @@ -505,17 +508,19 @@ def _save(im, fp, filename): flags, # flags im.height, im.width, - stride, # pitch + pitch, 0, # depth 0, # mipmaps ) + struct.pack("11I", *((0,) * 11)) # reserved # 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("<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): diff --git a/src/PIL/Image.py b/src/PIL/Image.py index b8b8fcd81..2853bd596 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -23,7 +23,6 @@ # # See the README file for information on usage and redistribution. # -from __future__ import annotations import atexit import builtins @@ -39,7 +38,6 @@ import warnings from collections.abc import Callable, MutableMapping from enum import IntEnum from pathlib import Path -from typing import NamedTuple try: from defusedxml import ElementTree @@ -208,13 +206,6 @@ if hasattr(core, "DEFAULT_STRATEGY"): FIXED = core.FIXED -class _Tile(NamedTuple): - encoder_name: str - extents: tuple[int, int, int, int] - offset: int - args: tuple | str | None - - # -------------------------------------------------------------------- # Registries @@ -706,7 +697,6 @@ class Image: def __setstate__(self, state): Image.__init__(self) - self.tile: list[_Tile] = [] info, mode, size, palette, data = state self.info = info self._mode = mode diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index e8455d064..8bca19a4b 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -32,6 +32,7 @@ import io import itertools import struct import sys +from typing import NamedTuple from . import Image from ._util import is_path @@ -78,6 +79,13 @@ def _tilesort(t): return t[2] +class _Tile(NamedTuple): + encoder_name: str + extents: tuple[int, int, int, int] + offset: int + args: tuple | str | None + + # # -------------------------------------------------------------------- # ImageFile base class @@ -521,7 +529,7 @@ def _save(im, fp, tile, bufsize=0): 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: if offset > 0: fp.seek(offset) From e072a129743624bbd98c83fbdb863648d051a02d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 2 Dec 2023 11:40:51 +1100 Subject: [PATCH 5/5] Corrected constant values --- src/PIL/DdsImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index e8d5a8b0e..c09a8db3b 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -184,8 +184,8 @@ class DXGI_FORMAT(IntEnum): P208 = 130 V208 = 131 V408 = 132 - SAMPLER_FEEDBACK_MIN_MIP_OPAQUE = 133 - SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE = 134 + SAMPLER_FEEDBACK_MIN_MIP_OPAQUE = 189 + SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE = 190 class D3DFMT(IntEnum):