Skip to content

Commit 9f619b8

Browse files
committed
Added BC3 loading and saving
1 parent 9430bbe commit 9f619b8

3 files changed

Lines changed: 30 additions & 3 deletions

File tree

Tests/test_file_dds.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from .helper import (
1313
assert_image_equal,
1414
assert_image_equal_tofile,
15+
assert_image_similar,
1516
assert_image_similar_tofile,
1617
hopper,
1718
)
@@ -114,6 +115,19 @@ def test_sanity_ati1_bc4u(image_path: str) -> None:
114115
assert_image_equal_tofile(im, TEST_FILE_ATI1.replace(".dds", ".png"))
115116

116117

118+
def test_dx10_bc3(tmp_path: Path) -> None:
119+
out = str(tmp_path / "temp.dds")
120+
with Image.open(TEST_FILE_DXT5) as im:
121+
im.save(out, pixel_format="BC3")
122+
123+
with Image.open(out) as reloaded:
124+
assert reloaded.format == "DDS"
125+
assert reloaded.mode == "RGBA"
126+
assert reloaded.size == (256, 256)
127+
128+
assert_image_similar(im, reloaded, 3.69)
129+
130+
117131
@pytest.mark.parametrize(
118132
"image_path",
119133
(

src/PIL/DdsImagePlugin.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,10 @@ def _open(self) -> None:
419419
self._mode = "RGBA"
420420
self.pixel_format = "BC1"
421421
n = 1
422+
elif dxgi_format in (DXGI_FORMAT.BC3_TYPELESS, DXGI_FORMAT.BC3_UNORM):
423+
self._mode = "RGBA"
424+
self.pixel_format = "BC3"
425+
n = 3
422426
elif dxgi_format in (DXGI_FORMAT.BC4_TYPELESS, DXGI_FORMAT.BC4_UNORM):
423427
self._mode = "L"
424428
self.pixel_format = "BC4"
@@ -521,14 +525,18 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
521525
flags = DDSD.CAPS | DDSD.HEIGHT | DDSD.WIDTH | DDSD.PIXELFORMAT
522526
bitcount = len(im.getbands()) * 8
523527
pixel_format = im.encoderinfo.get("pixel_format")
524-
if pixel_format in ("DXT1", "DXT5"):
528+
if pixel_format in ("DXT1", "BC3", "DXT5"):
525529
codec_name = "bcn"
526530
flags |= DDSD.LINEARSIZE
527531
pitch = (im.width + 3) * 4
528532
args = pixel_format
529533
rgba_mask = [0, 0, 0, 0]
530534
pixel_flags = DDPF.FOURCC
531-
fourcc = D3DFMT.DXT1 if pixel_format == "DXT1" else D3DFMT.DXT5
535+
fourcc = {"DXT1": D3DFMT.DXT1, "BC3": D3DFMT.DX10, "DXT5": D3DFMT.DXT5}[
536+
pixel_format
537+
]
538+
if fourcc == D3DFMT.DX10:
539+
dxgi_format = DXGI_FORMAT.BC3_TYPELESS
532540
else:
533541
codec_name = "raw"
534542
flags |= DDSD.PITCH
@@ -573,6 +581,11 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
573581
+ struct.pack("<4I", *rgba_mask) # dwRGBABitMask
574582
+ struct.pack("<5I", DDSCAPS.TEXTURE, 0, 0, 0, 0)
575583
)
584+
if fourcc == D3DFMT.DX10:
585+
fp.write(
586+
# dxgi_format, 2D resource, misc, array size, straight alpha
587+
struct.pack("<5I", dxgi_format, 3, 0, 0, 1)
588+
)
576589
ImageFile._save(im, fp, [ImageFile._Tile(codec_name, (0, 0) + im.size, 0, args)])
577590

578591

src/libImaging/BcnEncode.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ encode_bc3_alpha(Imaging im, ImagingCodecState state, UINT8 *dst) {
218218
int
219219
ImagingBcnEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) {
220220
char *pixel_format = ((BCNSTATE *)state->context)->pixel_format;
221-
int n = strcmp(pixel_format, "DXT5") == 0 ? 3 : 1;
221+
int n = strcmp(pixel_format, "DXT1") == 0 ? 1 : 3;
222222
int has_alpha_channel =
223223
strcmp(im->mode, "RGBA") == 0 || strcmp(im->mode, "LA") == 0;
224224

0 commit comments

Comments
 (0)