diff --git a/Tests/images/bc6h.dds b/Tests/images/bc6h.dds new file mode 100644 index 00000000000..77993a0c1ea Binary files /dev/null and b/Tests/images/bc6h.dds differ diff --git a/Tests/images/bc6h.png b/Tests/images/bc6h.png new file mode 100644 index 00000000000..609f114890c Binary files /dev/null and b/Tests/images/bc6h.png differ diff --git a/Tests/images/bc6h_sf.dds b/Tests/images/bc6h_sf.dds new file mode 100644 index 00000000000..2ab1b195b19 Binary files /dev/null and b/Tests/images/bc6h_sf.dds differ diff --git a/Tests/images/bc6h_sf.png b/Tests/images/bc6h_sf.png new file mode 100644 index 00000000000..6a3b73d5fc9 Binary files /dev/null and b/Tests/images/bc6h_sf.png differ diff --git a/Tests/images/unimplemented_dxgi_format.dds b/Tests/images/unimplemented_dxgi_format.dds index 5ecb42006c4..70860f2fcc4 100644 Binary files a/Tests/images/unimplemented_dxgi_format.dds and b/Tests/images/unimplemented_dxgi_format.dds differ diff --git a/Tests/test_file_dds.py b/Tests/test_file_dds.py index 35100119926..4b9f8949ef5 100644 --- a/Tests/test_file_dds.py +++ b/Tests/test_file_dds.py @@ -16,6 +16,8 @@ TEST_FILE_DX10_BC5_UNORM = "Tests/images/bc5_unorm.dds" TEST_FILE_DX10_BC5_SNORM = "Tests/images/bc5_snorm.dds" TEST_FILE_BC5S = "Tests/images/bc5s.dds" +TEST_FILE_BC6H = "Tests/images/bc6h.dds" +TEST_FILE_BC6HS = "Tests/images/bc6h_sf.dds" TEST_FILE_DX10_BC7 = "Tests/images/bc7-argb-8bpp_MipMaps-1.dds" TEST_FILE_DX10_BC7_UNORM_SRGB = "Tests/images/DXGI_FORMAT_BC7_UNORM_SRGB.dds" TEST_FILE_DX10_R8G8B8A8 = "Tests/images/argb-32bpp_MipMaps-1.dds" @@ -114,6 +116,20 @@ def test_dx10_bc5(image_path, expected_path): assert_image_equal_tofile(im, expected_path.replace(".dds", ".png")) +@pytest.mark.parametrize("image_path", (TEST_FILE_BC6H, TEST_FILE_BC6HS)) +def test_dx10_bc6h(image_path): + """Check DX10 BC6H/BC6HS images can be opened""" + + with Image.open(image_path) as im: + im.load() + + assert im.format == "DDS" + assert im.mode == "RGB" + assert im.size == (128, 128) + + assert_image_equal_tofile(im, image_path.replace(".dds", ".png")) + + def test_dx10_bc7(): """Check DX10 images can be opened""" diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index bba48016140..eea6e31534c 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -101,6 +101,8 @@ DXGI_FORMAT_BC5_TYPELESS = 82 DXGI_FORMAT_BC5_UNORM = 83 DXGI_FORMAT_BC5_SNORM = 84 +DXGI_FORMAT_BC6H_UF16 = 95 +DXGI_FORMAT_BC6H_SF16 = 96 DXGI_FORMAT_BC7_TYPELESS = 97 DXGI_FORMAT_BC7_UNORM = 98 DXGI_FORMAT_BC7_UNORM_SRGB = 99 @@ -181,6 +183,14 @@ def _open(self): self.pixel_format = "BC5S" n = 5 self.mode = "RGB" + elif dxgi_format == DXGI_FORMAT_BC6H_UF16: + self.pixel_format = "BC6H" + n = 6 + self.mode = "RGB" + elif dxgi_format == DXGI_FORMAT_BC6H_SF16: + self.pixel_format = "BC6HS" + n = 6 + self.mode = "RGB" elif dxgi_format in (DXGI_FORMAT_BC7_TYPELESS, DXGI_FORMAT_BC7_UNORM): self.pixel_format = "BC7" n = 7 diff --git a/src/decode.c b/src/decode.c index cb018a4e706..7a9b956c559 100644 --- a/src/decode.c +++ b/src/decode.c @@ -376,11 +376,8 @@ PyImaging_BcnDecoderNew(PyObject *self, PyObject *args) { actual = "L"; break; case 5: /* BC5: 2-channel 8-bit via 2 BC3 alpha blocks */ - actual = "RGB"; - break; case 6: /* BC6: 3-channel 16-bit float */ - /* TODO: support 4-channel floating point images */ - actual = "RGBAF"; + actual = "RGB"; break; default: PyErr_SetString(PyExc_ValueError, "block compression type unknown"); diff --git a/src/libImaging/BcnDecode.c b/src/libImaging/BcnDecode.c index 22b36eb7acc..a57b74b61ac 100644 --- a/src/libImaging/BcnDecode.c +++ b/src/libImaging/BcnDecode.c @@ -23,10 +23,6 @@ typedef struct { UINT8 l; } lum; -typedef struct { - FLOAT32 r, g, b; -} rgb32f; - typedef struct { UINT16 c0, c1; UINT32 lut; @@ -536,53 +532,53 @@ static const bc6_mode_info bc6_modes[] = { /* Table.F, encoded as a sequence of bit indices */ static const UINT8 bc6_bit_packings[][75] = { - {116, 132, 176, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, + {116, 132, 180, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 52, 164, 112, 113, 114, 115, 64, 65, - 66, 67, 68, 172, 160, 161, 162, 163, 80, 81, 82, 83, 84, 173, 128, - 129, 130, 131, 96, 97, 98, 99, 100, 174, 144, 145, 146, 147, 148, 175}, - {117, 164, 165, 0, 1, 2, 3, 4, 5, 6, 172, 173, 132, 16, 17, - 18, 19, 20, 21, 22, 133, 174, 116, 32, 33, 34, 35, 36, 37, 38, - 175, 177, 176, 48, 49, 50, 51, 52, 53, 112, 113, 114, 115, 64, 65, + 66, 67, 68, 176, 160, 161, 162, 163, 80, 81, 82, 83, 84, 177, 128, + 129, 130, 131, 96, 97, 98, 99, 100, 178, 144, 145, 146, 147, 148, 179}, + {117, 164, 165, 0, 1, 2, 3, 4, 5, 6, 176, 177, 132, 16, 17, + 18, 19, 20, 21, 22, 133, 178, 116, 32, 33, 34, 35, 36, 37, 38, + 179, 181, 180, 48, 49, 50, 51, 52, 53, 112, 113, 114, 115, 64, 65, 66, 67, 68, 69, 160, 161, 162, 163, 80, 81, 82, 83, 84, 85, 128, 129, 130, 131, 96, 97, 98, 99, 100, 101, 144, 145, 146, 147, 148, 149}, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 52, 10, 112, 113, 114, 115, 64, 65, 66, 67, 26, - 172, 160, 161, 162, 163, 80, 81, 82, 83, 42, 173, 128, 129, 130, 131, - 96, 97, 98, 99, 100, 174, 144, 145, 146, 147, 148, 175}, + 176, 160, 161, 162, 163, 80, 81, 82, 83, 42, 177, 128, 129, 130, 131, + 96, 97, 98, 99, 100, 178, 144, 145, 146, 147, 148, 179}, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 10, 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, - 26, 160, 161, 162, 163, 80, 81, 82, 83, 42, 173, 128, 129, 130, 131, - 96, 97, 98, 99, 172, 174, 144, 145, 146, 147, 116, 175}, + 26, 160, 161, 162, 163, 80, 81, 82, 83, 42, 177, 128, 129, 130, 131, + 96, 97, 98, 99, 176, 178, 144, 145, 146, 147, 116, 179}, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 10, 132, 112, 113, 114, 115, 64, 65, 66, 67, 26, - 172, 160, 161, 162, 163, 80, 81, 82, 83, 84, 42, 128, 129, 130, 131, - 96, 97, 98, 99, 173, 174, 144, 145, 146, 147, 176, 175}, + 176, 160, 161, 162, 163, 80, 81, 82, 83, 84, 42, 128, 129, 130, 131, + 96, 97, 98, 99, 177, 178, 144, 145, 146, 147, 180, 179}, {0, 1, 2, 3, 4, 5, 6, 7, 8, 132, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 116, 32, 33, 34, 35, 36, 37, 38, 39, 40, 176, + 21, 22, 23, 24, 116, 32, 33, 34, 35, 36, 37, 38, 39, 40, 180, 48, 49, 50, 51, 52, 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, - 172, 160, 161, 162, 163, 80, 81, 82, 83, 84, 173, 128, 129, 130, 131, - 96, 97, 98, 99, 100, 174, 144, 145, 146, 147, 148, 175}, + 176, 160, 161, 162, 163, 80, 81, 82, 83, 84, 177, 128, 129, 130, 131, + 96, 97, 98, 99, 100, 178, 144, 145, 146, 147, 148, 179}, {0, 1, 2, 3, 4, 5, 6, 7, 164, 132, 16, 17, 18, 19, 20, - 21, 22, 23, 174, 116, 32, 33, 34, 35, 36, 37, 38, 39, 175, 176, + 21, 22, 23, 178, 116, 32, 33, 34, 35, 36, 37, 38, 39, 179, 180, 48, 49, 50, 51, 52, 53, 112, 113, 114, 115, 64, 65, 66, 67, 68, - 172, 160, 161, 162, 163, 80, 81, 82, 83, 84, 173, 128, 129, 130, 131, + 176, 160, 161, 162, 163, 80, 81, 82, 83, 84, 177, 128, 129, 130, 131, 96, 97, 98, 99, 100, 101, 144, 145, 146, 147, 148, 149}, - {0, 1, 2, 3, 4, 5, 6, 7, 172, 132, 16, 17, 18, 19, 20, - 21, 22, 23, 117, 116, 32, 33, 34, 35, 36, 37, 38, 39, 165, 176, + {0, 1, 2, 3, 4, 5, 6, 7, 176, 132, 16, 17, 18, 19, 20, + 21, 22, 23, 117, 116, 32, 33, 34, 35, 36, 37, 38, 39, 165, 180, 48, 49, 50, 51, 52, 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, - 69, 160, 161, 162, 163, 80, 81, 82, 83, 84, 173, 128, 129, 130, 131, - 96, 97, 98, 99, 100, 174, 144, 145, 146, 147, 148, 175}, - {0, 1, 2, 3, 4, 5, 6, 7, 173, 132, 16, 17, 18, 19, 20, - 21, 22, 23, 133, 116, 32, 33, 34, 35, 36, 37, 38, 39, 177, 176, + 69, 160, 161, 162, 163, 80, 81, 82, 83, 84, 177, 128, 129, 130, 131, + 96, 97, 98, 99, 100, 178, 144, 145, 146, 147, 148, 179}, + {0, 1, 2, 3, 4, 5, 6, 7, 177, 132, 16, 17, 18, 19, 20, + 21, 22, 23, 133, 116, 32, 33, 34, 35, 36, 37, 38, 39, 181, 180, 48, 49, 50, 51, 52, 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, - 172, 160, 161, 162, 163, 80, 81, 82, 83, 84, 85, 128, 129, 130, 131, - 96, 97, 98, 99, 100, 174, 144, 145, 146, 147, 148, 175}, - {0, 1, 2, 3, 4, 5, 164, 172, 173, 132, 16, 17, 18, 19, 20, - 21, 117, 133, 174, 116, 32, 33, 34, 35, 36, 37, 165, 175, 177, 176, + 176, 160, 161, 162, 163, 80, 81, 82, 83, 84, 85, 128, 129, 130, 131, + 96, 97, 98, 99, 100, 178, 144, 145, 146, 147, 148, 179}, + {0, 1, 2, 3, 4, 5, 164, 176, 177, 132, 16, 17, 18, 19, 20, + 21, 117, 133, 178, 116, 32, 33, 34, 35, 36, 37, 165, 179, 181, 180, 48, 49, 50, 51, 52, 53, 112, 113, 114, 115, 64, 65, 66, 67, 68, 69, 160, 161, 162, 163, 80, 81, 82, 83, 84, 85, 128, 129, 130, 131, 96, 97, 98, 99, 100, 101, 144, 145, 146, 147, 148, 149}, @@ -681,20 +677,31 @@ bc6_finalize(int v, int sign) { } } +static UINT8 +bc6_clamp(float value) { + if (value < 0.0f) { + return 0; + } else if (value > 1.0f) { + return 255; + } else { + return (UINT8) (value * 255.0f); + } +} + static void -bc6_lerp(rgb32f *col, int *e0, int *e1, int s, int sign) { +bc6_lerp(rgba *col, int *e0, int *e1, int s, int sign) { int r, g, b; int t = 64 - s; r = (e0[0] * t + e1[0] * s) >> 6; g = (e0[1] * t + e1[1] * s) >> 6; b = (e0[2] * t + e1[2] * s) >> 6; - col->r = bc6_finalize(r, sign); - col->g = bc6_finalize(g, sign); - col->b = bc6_finalize(b, sign); + col->r = bc6_clamp(bc6_finalize(r, sign)); + col->g = bc6_clamp(bc6_finalize(g, sign)); + col->b = bc6_clamp(bc6_finalize(b, sign)); } static void -decode_bc6_block(rgb32f *col, const UINT8 *src, int sign) { +decode_bc6_block(rgba *col, const UINT8 *src, int sign) { UINT16 endpoints[12]; /* storage for r0, g0, b0, r1, ... */ int ueps[12]; int i, i0, ib2, di, dw, mask, numep, s; @@ -744,21 +751,16 @@ decode_bc6_block(rgb32f *col, const UINT8 *src, int sign) { } if (sign || info->tr) { /* sign-extend e1,2,3 if signed or deltas */ for (i = 3; i < numep; i += 3) { - bc6_sign_extend(&endpoints[i + 0], info->rb); + bc6_sign_extend(&endpoints[i], info->rb); bc6_sign_extend(&endpoints[i + 1], info->gb); bc6_sign_extend(&endpoints[i + 2], info->bb); } } if (info->tr) { /* apply deltas */ - for (i = 3; i < numep; i++) { + for (i = 3; i < numep; i += 3) { endpoints[i] = (endpoints[i] + endpoints[0]) & mask; - } - if (sign) { - for (i = 3; i < numep; i += 3) { - bc6_sign_extend(&endpoints[i + 0], info->rb); - bc6_sign_extend(&endpoints[i + 1], info->gb); - bc6_sign_extend(&endpoints[i + 2], info->bb); - } + endpoints[i + 1] = (endpoints[i + 1] + endpoints[1]) & mask; + endpoints[i + 2] = (endpoints[i + 2] + endpoints[2]) & mask; } } for (i = 0; i < numep; i++) { @@ -862,8 +864,8 @@ decode_bcn( break; case 6: while (bytes >= 16) { - rgb32f col[16]; - decode_bc6_block(col, ptr, (state->state >> 4) & 1); + rgba col[16]; + decode_bc6_block(col, ptr, strcmp(pixel_format, "BC6HS") == 0 ? 1 : 0); put_block(im, state, (const char *)col, sizeof(col[0]), C); ptr += 16; bytes -= 16;