Skip to content

Commit 70535eb

Browse files
committed
Added BC5 saving
1 parent b8ee15c commit 70535eb

File tree

3 files changed

+53
-18
lines changed

3 files changed

+53
-18
lines changed

Tests/test_file_dds.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ def test_not_implemented(test_file: str) -> None:
402402
def test_save_unsupported_mode(tmp_path: Path) -> None:
403403
out = str(tmp_path / "temp.dds")
404404
im = hopper("HSV")
405-
with pytest.raises(OSError):
405+
with pytest.raises(OSError, match="cannot write mode HSV as DDS"):
406406
im.save(out)
407407

408408

@@ -424,6 +424,13 @@ def test_save(mode: str, test_file: str, tmp_path: Path) -> None:
424424
assert_image_equal_tofile(im, out)
425425

426426

427+
def test_save_unsupported_pixel_format(tmp_path: Path) -> None:
428+
out = str(tmp_path / "temp.dds")
429+
im = hopper()
430+
with pytest.raises(OSError, match="cannot write pixel format UNKNOWN"):
431+
im.save(out, pixel_format="UNKNOWN")
432+
433+
427434
def test_save_dxt1(tmp_path: Path) -> None:
428435
# RGB
429436
out = str(tmp_path / "temp.dds")
@@ -493,3 +500,14 @@ def test_save_dxt5(tmp_path: Path) -> None:
493500
im_la = im_rgba.convert("LA")
494501
im_la.save(out, pixel_format="DXT5")
495502
assert_image_similar_tofile(im_la.convert("RGBA"), out, 8.32)
503+
504+
505+
def test_save_dx10_bc5(tmp_path: Path) -> None:
506+
out = str(tmp_path / "temp.dds")
507+
with Image.open(TEST_FILE_DX10_BC5_TYPELESS) as im:
508+
im.save(out, pixel_format="BC5")
509+
assert_image_similar_tofile(im, out, 9.56)
510+
511+
im = hopper("L")
512+
with pytest.raises(OSError, match="only RGB mode can be written as BC5"):
513+
im.save(out, pixel_format="BC5")

src/PIL/DdsImagePlugin.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
530530
bitcount = len(im.getbands()) * 8
531531
pixel_format = im.encoderinfo.get("pixel_format")
532532
args: tuple[int] | str
533-
if pixel_format in ("DXT1", "BC2", "DXT3", "BC3", "DXT5"):
533+
if pixel_format:
534534
codec_name = "bcn"
535535
flags |= DDSD.LINEARSIZE
536536
pitch = (im.width + 3) * 4
@@ -550,9 +550,18 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
550550
if pixel_format == "BC2":
551551
args = (2,)
552552
dxgi_format = DXGI_FORMAT.BC2_TYPELESS
553-
else:
553+
elif pixel_format == "BC3":
554554
args = (3,)
555555
dxgi_format = DXGI_FORMAT.BC3_TYPELESS
556+
elif pixel_format == "BC5":
557+
args = (5,)
558+
dxgi_format = DXGI_FORMAT.BC5_TYPELESS
559+
if im.mode != "RGB":
560+
msg = "only RGB mode can be written as BC5"
561+
raise OSError(msg)
562+
else:
563+
msg = f"cannot write pixel format {pixel_format}"
564+
raise OSError(msg)
556565
else:
557566
codec_name = "raw"
558567
flags |= DDSD.PITCH

src/libImaging/BcnEncode.c

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ encode_bc2_block(Imaging im, ImagingCodecState state, UINT8 *dst) {
185185
}
186186

187187
static void
188-
encode_bc3_alpha(Imaging im, ImagingCodecState state, UINT8 *dst) {
188+
encode_bc3_alpha(Imaging im, ImagingCodecState state, UINT8 *dst, int o) {
189189
int i, j;
190190
UINT8 alpha_min = 0, alpha_max = 0;
191191
UINT8 block[16], current_alpha;
@@ -202,7 +202,7 @@ encode_bc3_alpha(Imaging im, ImagingCodecState state, UINT8 *dst) {
202202
continue;
203203
}
204204

205-
current_alpha = (UINT8)im->image[y][x + 3];
205+
current_alpha = (UINT8)im->image[y][x + o];
206206
block[i + j * 4] = current_alpha;
207207

208208
if (first || current_alpha < alpha_min) {
@@ -226,12 +226,13 @@ encode_bc3_alpha(Imaging im, ImagingCodecState state, UINT8 *dst) {
226226
if (!current_alpha) {
227227
l |= 6 << (j * 3);
228228
continue;
229-
} else if (current_alpha == 255 || denom == 0) {
229+
} else if (current_alpha == 255) {
230230
l |= 7 << (j * 3);
231231
continue;
232232
}
233233

234-
float distance = abs(current_alpha - alpha_min) / denom * 10;
234+
float distance =
235+
denom == 0 ? 0 : abs(current_alpha - alpha_min) / denom * 10;
235236
if (distance < 3) {
236237
l |= 2 << (j * 3); // 4/5 * alpha_min + 1/5 * alpha_max
237238
} else if (distance < 5) {
@@ -257,21 +258,28 @@ ImagingBcnEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) {
257258
UINT8 *dst = buf;
258259

259260
for (;;) {
260-
if (n == 2 || n == 3) {
261-
if (has_alpha_channel) {
262-
if (n == 2) {
263-
encode_bc2_block(im, state, dst);
261+
if (n == 5) {
262+
encode_bc3_alpha(im, state, dst, 0);
263+
dst += 8;
264+
265+
encode_bc3_alpha(im, state, dst, 1);
266+
} else {
267+
if (n == 2 || n == 3) {
268+
if (has_alpha_channel) {
269+
if (n == 2) {
270+
encode_bc2_block(im, state, dst);
271+
} else {
272+
encode_bc3_alpha(im, state, dst, 3);
273+
}
274+
dst += 8;
264275
} else {
265-
encode_bc3_alpha(im, state, dst);
266-
}
267-
dst += 8;
268-
} else {
269-
for (int i = 0; i < 8; i++) {
270-
*dst++ = 0xff;
276+
for (int i = 0; i < 8; i++) {
277+
*dst++ = 0xff;
278+
}
271279
}
272280
}
281+
encode_bc1_color(im, state, dst, n == 1 && has_alpha_channel);
273282
}
274-
encode_bc1_color(im, state, dst, n == 1 && has_alpha_channel);
275283
dst += 8;
276284

277285
state->x += im->pixelsize * 4;

0 commit comments

Comments
 (0)