Added BC5 saving

This commit is contained in:
Andrew Murray 2025-03-15 20:16:33 +11:00
parent b0315cc603
commit cd11792c15
3 changed files with 53 additions and 18 deletions

View File

@ -402,7 +402,7 @@ def test_not_implemented(test_file: str) -> None:
def test_save_unsupported_mode(tmp_path: Path) -> None:
out = str(tmp_path / "temp.dds")
im = hopper("HSV")
with pytest.raises(OSError):
with pytest.raises(OSError, match="cannot write mode HSV as DDS"):
im.save(out)
@ -424,6 +424,13 @@ def test_save(mode: str, test_file: str, tmp_path: Path) -> None:
assert_image_equal_tofile(im, out)
def test_save_unsupported_pixel_format(tmp_path: Path) -> None:
out = str(tmp_path / "temp.dds")
im = hopper()
with pytest.raises(OSError, match="cannot write pixel format UNKNOWN"):
im.save(out, pixel_format="UNKNOWN")
def test_save_dxt1(tmp_path: Path) -> None:
# RGB
out = str(tmp_path / "temp.dds")
@ -493,3 +500,14 @@ def test_save_dxt5(tmp_path: Path) -> None:
im_la = im_rgba.convert("LA")
im_la.save(out, pixel_format="DXT5")
assert_image_similar_tofile(im_la.convert("RGBA"), out, 8.32)
def test_save_dx10_bc5(tmp_path: Path) -> None:
out = str(tmp_path / "temp.dds")
with Image.open(TEST_FILE_DX10_BC5_TYPELESS) as im:
im.save(out, pixel_format="BC5")
assert_image_similar_tofile(im, out, 9.56)
im = hopper("L")
with pytest.raises(OSError, match="only RGB mode can be written as BC5"):
im.save(out, pixel_format="BC5")

View File

@ -530,7 +530,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
bitcount = len(im.getbands()) * 8
pixel_format = im.encoderinfo.get("pixel_format")
args: tuple[int] | str
if pixel_format in ("DXT1", "BC2", "DXT3", "BC3", "DXT5"):
if pixel_format:
codec_name = "bcn"
flags |= DDSD.LINEARSIZE
pitch = (im.width + 3) * 4
@ -550,9 +550,18 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
if pixel_format == "BC2":
args = (2,)
dxgi_format = DXGI_FORMAT.BC2_TYPELESS
else:
elif pixel_format == "BC3":
args = (3,)
dxgi_format = DXGI_FORMAT.BC3_TYPELESS
elif pixel_format == "BC5":
args = (5,)
dxgi_format = DXGI_FORMAT.BC5_TYPELESS
if im.mode != "RGB":
msg = "only RGB mode can be written as BC5"
raise OSError(msg)
else:
msg = f"cannot write pixel format {pixel_format}"
raise OSError(msg)
else:
codec_name = "raw"
flags |= DDSD.PITCH

View File

@ -185,7 +185,7 @@ encode_bc2_block(Imaging im, ImagingCodecState state, UINT8 *dst) {
}
static void
encode_bc3_alpha(Imaging im, ImagingCodecState state, UINT8 *dst) {
encode_bc3_alpha(Imaging im, ImagingCodecState state, UINT8 *dst, int o) {
int i, j;
UINT8 alpha_min = 0, alpha_max = 0;
UINT8 block[16], current_alpha;
@ -202,7 +202,7 @@ encode_bc3_alpha(Imaging im, ImagingCodecState state, UINT8 *dst) {
continue;
}
current_alpha = (UINT8)im->image[y][x + 3];
current_alpha = (UINT8)im->image[y][x + o];
block[i + j * 4] = current_alpha;
if (first || current_alpha < alpha_min) {
@ -226,12 +226,13 @@ encode_bc3_alpha(Imaging im, ImagingCodecState state, UINT8 *dst) {
if (!current_alpha) {
l |= 6 << (j * 3);
continue;
} else if (current_alpha == 255 || denom == 0) {
} else if (current_alpha == 255) {
l |= 7 << (j * 3);
continue;
}
float distance = abs(current_alpha - alpha_min) / denom * 10;
float distance =
denom == 0 ? 0 : abs(current_alpha - alpha_min) / denom * 10;
if (distance < 3) {
l |= 2 << (j * 3); // 4/5 * alpha_min + 1/5 * alpha_max
} else if (distance < 5) {
@ -257,21 +258,28 @@ ImagingBcnEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) {
UINT8 *dst = buf;
for (;;) {
if (n == 2 || n == 3) {
if (has_alpha_channel) {
if (n == 2) {
encode_bc2_block(im, state, dst);
if (n == 5) {
encode_bc3_alpha(im, state, dst, 0);
dst += 8;
encode_bc3_alpha(im, state, dst, 1);
} else {
if (n == 2 || n == 3) {
if (has_alpha_channel) {
if (n == 2) {
encode_bc2_block(im, state, dst);
} else {
encode_bc3_alpha(im, state, dst, 3);
}
dst += 8;
} else {
encode_bc3_alpha(im, state, dst);
}
dst += 8;
} else {
for (int i = 0; i < 8; i++) {
*dst++ = 0xff;
for (int i = 0; i < 8; i++) {
*dst++ = 0xff;
}
}
}
encode_bc1_color(im, state, dst, n == 1 && has_alpha_channel);
}
encode_bc1_color(im, state, dst, n == 1 && has_alpha_channel);
dst += 8;
state->x += im->pixelsize * 4;