mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-13 18:56:17 +03:00
Merge pull request #5501 from radarhere/dds_bc5
This commit is contained in:
commit
101887360c
BIN
Tests/images/bc5_snorm.dds
Normal file
BIN
Tests/images/bc5_snorm.dds
Normal file
Binary file not shown.
BIN
Tests/images/bc5_typeless.dds
Normal file
BIN
Tests/images/bc5_typeless.dds
Normal file
Binary file not shown.
BIN
Tests/images/bc5_unorm.dds
Normal file
BIN
Tests/images/bc5_unorm.dds
Normal file
Binary file not shown.
BIN
Tests/images/bc5_unorm.png
Normal file
BIN
Tests/images/bc5_unorm.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 94 KiB |
BIN
Tests/images/bc5s.dds
Normal file
BIN
Tests/images/bc5s.dds
Normal file
Binary file not shown.
BIN
Tests/images/bc5s.png
Normal file
BIN
Tests/images/bc5s.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 82 KiB |
|
@ -10,6 +10,10 @@ from .helper import assert_image_equal, assert_image_equal_tofile
|
|||
TEST_FILE_DXT1 = "Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.dds"
|
||||
TEST_FILE_DXT3 = "Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.dds"
|
||||
TEST_FILE_DXT5 = "Tests/images/dxt5-argb-8bbp-interpolatedalpha_MipMaps-1.dds"
|
||||
TEST_FILE_DX10_BC5_TYPELESS = "Tests/images/bc5_typeless.dds"
|
||||
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_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"
|
||||
|
@ -32,6 +36,19 @@ def test_sanity_dxt1():
|
|||
assert_image_equal(im, target)
|
||||
|
||||
|
||||
def test_sanity_dxt3():
|
||||
"""Check DXT3 images can be opened"""
|
||||
|
||||
with Image.open(TEST_FILE_DXT3) as im:
|
||||
im.load()
|
||||
|
||||
assert im.format == "DDS"
|
||||
assert im.mode == "RGBA"
|
||||
assert im.size == (256, 256)
|
||||
|
||||
assert_image_equal_tofile(im, TEST_FILE_DXT3.replace(".dds", ".png"))
|
||||
|
||||
|
||||
def test_sanity_dxt5():
|
||||
"""Check DXT5 images can be opened"""
|
||||
|
||||
|
@ -45,17 +62,28 @@ def test_sanity_dxt5():
|
|||
assert_image_equal_tofile(im, TEST_FILE_DXT5.replace(".dds", ".png"))
|
||||
|
||||
|
||||
def test_sanity_dxt3():
|
||||
"""Check DXT3 images can be opened"""
|
||||
@pytest.mark.parametrize(
|
||||
("image_path", "expected_path"),
|
||||
(
|
||||
# hexeditted to be typeless
|
||||
(TEST_FILE_DX10_BC5_TYPELESS, TEST_FILE_DX10_BC5_UNORM),
|
||||
(TEST_FILE_DX10_BC5_UNORM, TEST_FILE_DX10_BC5_UNORM),
|
||||
# hexeditted to use DX10 FourCC
|
||||
(TEST_FILE_DX10_BC5_SNORM, TEST_FILE_BC5S),
|
||||
(TEST_FILE_BC5S, TEST_FILE_BC5S),
|
||||
),
|
||||
)
|
||||
def test_dx10_bc5(image_path, expected_path):
|
||||
"""Check DX10 BC5 images can be opened"""
|
||||
|
||||
with Image.open(TEST_FILE_DXT3) as im:
|
||||
with Image.open(image_path) as im:
|
||||
im.load()
|
||||
|
||||
assert im.format == "DDS"
|
||||
assert im.mode == "RGBA"
|
||||
assert im.mode == "RGB"
|
||||
assert im.size == (256, 256)
|
||||
|
||||
assert_image_equal_tofile(im, TEST_FILE_DXT3.replace(".dds", ".png"))
|
||||
assert_image_equal_tofile(im, expected_path.replace(".dds", ".png"))
|
||||
|
||||
|
||||
def test_dx10_bc7():
|
||||
|
|
|
@ -66,7 +66,8 @@ TODO
|
|||
Other Changes
|
||||
=============
|
||||
|
||||
TODO
|
||||
^^^^
|
||||
Added DDS BC5 reading
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
TODO
|
||||
Support has been added to read the BC5 format of DDS images, whether UNORM, SNORM or
|
||||
TYPELESS.
|
||||
|
|
|
@ -97,6 +97,9 @@ DXT5_FOURCC = 0x35545844
|
|||
DXGI_FORMAT_R8G8B8A8_TYPELESS = 27
|
||||
DXGI_FORMAT_R8G8B8A8_UNORM = 28
|
||||
DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29
|
||||
DXGI_FORMAT_BC5_TYPELESS = 82
|
||||
DXGI_FORMAT_BC5_UNORM = 83
|
||||
DXGI_FORMAT_BC5_SNORM = 84
|
||||
DXGI_FORMAT_BC7_TYPELESS = 97
|
||||
DXGI_FORMAT_BC7_UNORM = 98
|
||||
DXGI_FORMAT_BC7_UNORM_SRGB = 99
|
||||
|
@ -150,12 +153,24 @@ class DdsImageFile(ImageFile.ImageFile):
|
|||
elif fourcc == b"DXT5":
|
||||
self.pixel_format = "DXT5"
|
||||
n = 3
|
||||
elif fourcc == b"BC5S":
|
||||
self.pixel_format = "BC5S"
|
||||
n = 5
|
||||
self.mode = "RGB"
|
||||
elif fourcc == b"DX10":
|
||||
data_start += 20
|
||||
# ignoring flags which pertain to volume textures and cubemaps
|
||||
dxt10 = BytesIO(self.fp.read(20))
|
||||
dxgi_format, dimension = struct.unpack("<II", dxt10.read(8))
|
||||
if dxgi_format in (DXGI_FORMAT_BC7_TYPELESS, DXGI_FORMAT_BC7_UNORM):
|
||||
(dxgi_format,) = struct.unpack("<I", self.fp.read(4))
|
||||
self.fp.read(16)
|
||||
if dxgi_format in (DXGI_FORMAT_BC5_TYPELESS, DXGI_FORMAT_BC5_UNORM):
|
||||
self.pixel_format = "BC5"
|
||||
n = 5
|
||||
self.mode = "RGB"
|
||||
elif dxgi_format == DXGI_FORMAT_BC5_SNORM:
|
||||
self.pixel_format = "BC5S"
|
||||
n = 5
|
||||
self.mode = "RGB"
|
||||
elif dxgi_format in (DXGI_FORMAT_BC7_TYPELESS, DXGI_FORMAT_BC7_UNORM):
|
||||
self.pixel_format = "BC7"
|
||||
n = 7
|
||||
elif dxgi_format == DXGI_FORMAT_BC7_UNORM_SRGB:
|
||||
|
@ -178,7 +193,9 @@ class DdsImageFile(ImageFile.ImageFile):
|
|||
else:
|
||||
raise NotImplementedError(f"Unimplemented pixel format {repr(fourcc)}")
|
||||
|
||||
self.tile = [("bcn", (0, 0) + self.size, data_start, (n))]
|
||||
self.tile = [
|
||||
("bcn", (0, 0) + self.size, data_start, (n, self.pixel_format))
|
||||
]
|
||||
|
||||
def load_seek(self, pos):
|
||||
pass
|
||||
|
|
15
src/decode.c
15
src/decode.c
|
@ -34,9 +34,10 @@
|
|||
|
||||
#include "libImaging/Imaging.h"
|
||||
|
||||
#include "libImaging/Bit.h"
|
||||
#include "libImaging/Bcn.h"
|
||||
#include "libImaging/Gif.h"
|
||||
#include "libImaging/Raw.h"
|
||||
#include "libImaging/Bit.h"
|
||||
#include "libImaging/Sgi.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -359,8 +360,8 @@ PyImaging_BcnDecoderNew(PyObject *self, PyObject *args) {
|
|||
char *mode;
|
||||
char *actual;
|
||||
int n = 0;
|
||||
int ystep = 1;
|
||||
if (!PyArg_ParseTuple(args, "s|ii", &mode, &n, &ystep)) {
|
||||
char *pixel_format = "";
|
||||
if (!PyArg_ParseTuple(args, "si|s", &mode, &n, &pixel_format)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -368,13 +369,15 @@ PyImaging_BcnDecoderNew(PyObject *self, PyObject *args) {
|
|||
case 1: /* BC1: 565 color, 1-bit alpha */
|
||||
case 2: /* BC2: 565 color, 4-bit alpha */
|
||||
case 3: /* BC3: 565 color, 2-endpoint 8-bit interpolated alpha */
|
||||
case 5: /* BC5: 2-channel 8-bit via 2 BC3 alpha blocks */
|
||||
case 7: /* BC7: 4-channel 8-bit via everything */
|
||||
actual = "RGBA";
|
||||
break;
|
||||
case 4: /* BC4: 1-channel 8-bit via 1 BC3 alpha block */
|
||||
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";
|
||||
|
@ -389,14 +392,14 @@ PyImaging_BcnDecoderNew(PyObject *self, PyObject *args) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
decoder = PyImaging_DecoderNew(0);
|
||||
decoder = PyImaging_DecoderNew(sizeof(char *));
|
||||
if (decoder == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
decoder->decode = ImagingBcnDecode;
|
||||
decoder->state.state = n;
|
||||
decoder->state.ystep = ystep;
|
||||
((BCNSTATE *)decoder->state.context)->pixel_format = pixel_format;
|
||||
|
||||
return (PyObject *)decoder;
|
||||
}
|
||||
|
|
3
src/libImaging/Bcn.h
Normal file
3
src/libImaging/Bcn.h
Normal file
|
@ -0,0 +1,3 @@
|
|||
typedef struct {
|
||||
char *pixel_format;
|
||||
} BCNSTATE;
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
#include "Imaging.h"
|
||||
|
||||
#include "Bcn.h"
|
||||
|
||||
typedef struct {
|
||||
UINT8 r, g, b, a;
|
||||
} rgba;
|
||||
|
@ -35,6 +37,11 @@ typedef struct {
|
|||
UINT8 lut[6];
|
||||
} bc3_alpha;
|
||||
|
||||
typedef struct {
|
||||
INT8 a0, a1;
|
||||
UINT8 lut[6];
|
||||
} bc5s_alpha;
|
||||
|
||||
#define LOAD16(p) (p)[0] | ((p)[1] << 8)
|
||||
|
||||
#define LOAD32(p) (p)[0] | ((p)[1] << 8) | ((p)[2] << 16) | ((p)[3] << 24)
|
||||
|
@ -46,11 +53,6 @@ bc1_color_load(bc1_color *dst, const UINT8 *src) {
|
|||
dst->lut = LOAD32(src + 4);
|
||||
}
|
||||
|
||||
static void
|
||||
bc3_alpha_load(bc3_alpha *dst, const UINT8 *src) {
|
||||
memcpy(dst, src, sizeof(bc3_alpha));
|
||||
}
|
||||
|
||||
static rgba
|
||||
decode_565(UINT16 x) {
|
||||
rgba c;
|
||||
|
@ -113,15 +115,26 @@ decode_bc1_color(rgba *dst, const UINT8 *src, int separate_alpha) {
|
|||
}
|
||||
|
||||
static void
|
||||
decode_bc3_alpha(char *dst, const UINT8 *src, int stride, int o) {
|
||||
bc3_alpha b;
|
||||
decode_bc3_alpha(char *dst, const UINT8 *src, int stride, int o, int sign) {
|
||||
UINT16 a0, a1;
|
||||
UINT8 a[8];
|
||||
int n, lut, aw;
|
||||
bc3_alpha_load(&b, src);
|
||||
|
||||
int n, lut1, lut2, aw;
|
||||
if (sign == 1) {
|
||||
bc5s_alpha b;
|
||||
memcpy(&b, src, sizeof(bc5s_alpha));
|
||||
a0 = (b.a0 + 255) / 2;
|
||||
a1 = (b.a1 + 255) / 2;
|
||||
lut1 = b.lut[0] | (b.lut[1] << 8) | (b.lut[2] << 16);
|
||||
lut2 = b.lut[3] | (b.lut[4] << 8) | (b.lut[5] << 16);
|
||||
} else {
|
||||
bc3_alpha b;
|
||||
memcpy(&b, src, sizeof(bc3_alpha));
|
||||
a0 = b.a0;
|
||||
a1 = b.a1;
|
||||
lut1 = b.lut[0] | (b.lut[1] << 8) | (b.lut[2] << 16);
|
||||
lut2 = b.lut[3] | (b.lut[4] << 8) | (b.lut[5] << 16);
|
||||
}
|
||||
|
||||
a[0] = (UINT8)a0;
|
||||
a[1] = (UINT8)a1;
|
||||
if (a0 > a1) {
|
||||
|
@ -139,14 +152,12 @@ decode_bc3_alpha(char *dst, const UINT8 *src, int stride, int o) {
|
|||
a[6] = 0;
|
||||
a[7] = 0xff;
|
||||
}
|
||||
lut = b.lut[0] | (b.lut[1] << 8) | (b.lut[2] << 16);
|
||||
for (n = 0; n < 8; n++) {
|
||||
aw = 7 & (lut >> (3 * n));
|
||||
aw = 7 & (lut1 >> (3 * n));
|
||||
dst[stride * n + o] = a[aw];
|
||||
}
|
||||
lut = b.lut[3] | (b.lut[4] << 8) | (b.lut[5] << 16);
|
||||
for (n = 0; n < 8; n++) {
|
||||
aw = 7 & (lut >> (3 * n));
|
||||
aw = 7 & (lut2 >> (3 * n));
|
||||
dst[stride * (8 + n) + o] = a[aw];
|
||||
}
|
||||
}
|
||||
|
@ -172,18 +183,18 @@ decode_bc2_block(rgba *col, const UINT8 *src) {
|
|||
static void
|
||||
decode_bc3_block(rgba *col, const UINT8 *src) {
|
||||
decode_bc1_color(col, src + 8, 1);
|
||||
decode_bc3_alpha((char *)col, src, sizeof(col[0]), 3);
|
||||
decode_bc3_alpha((char *)col, src, sizeof(col[0]), 3, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
decode_bc4_block(lum *col, const UINT8 *src) {
|
||||
decode_bc3_alpha((char *)col, src, sizeof(col[0]), 0);
|
||||
decode_bc3_alpha((char *)col, src, sizeof(col[0]), 0, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
decode_bc5_block(rgba *col, const UINT8 *src) {
|
||||
decode_bc3_alpha((char *)col, src, sizeof(col[0]), 0);
|
||||
decode_bc3_alpha((char *)col, src + 8, sizeof(col[0]), 1);
|
||||
decode_bc5_block(rgba *col, const UINT8 *src, int sign) {
|
||||
decode_bc3_alpha((char *)col, src, sizeof(col[0]), 0, sign);
|
||||
decode_bc3_alpha((char *)col, src + 8, sizeof(col[0]), 1, sign);
|
||||
}
|
||||
|
||||
/* BC6 and BC7 are described here:
|
||||
|
@ -813,7 +824,7 @@ put_block(Imaging im, ImagingCodecState state, const char *col, int sz, int C) {
|
|||
|
||||
static int
|
||||
decode_bcn(
|
||||
Imaging im, ImagingCodecState state, const UINT8 *src, int bytes, int N, int C) {
|
||||
Imaging im, ImagingCodecState state, const UINT8 *src, int bytes, int N, int C, char *pixel_format) {
|
||||
int ymax = state->ysize + state->yoff;
|
||||
const UINT8 *ptr = src;
|
||||
switch (N) {
|
||||
|
@ -836,7 +847,19 @@ decode_bcn(
|
|||
DECODE_LOOP(2, 16, rgba);
|
||||
DECODE_LOOP(3, 16, rgba);
|
||||
DECODE_LOOP(4, 8, lum);
|
||||
DECODE_LOOP(5, 16, rgba);
|
||||
case 5:
|
||||
while (bytes >= 16) {
|
||||
rgba col[16];
|
||||
memset(col, 0, 16 * sizeof(col[0]));
|
||||
decode_bc5_block(col, ptr, strcmp(pixel_format, "BC5S") == 0 ? 1 : 0);
|
||||
put_block(im, state, (const char *)col, sizeof(col[0]), C);
|
||||
ptr += 16;
|
||||
bytes -= 16;
|
||||
if (state->y >= ymax) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
while (bytes >= 16) {
|
||||
rgb32f col[16];
|
||||
|
@ -860,9 +883,7 @@ ImagingBcnDecode(Imaging im, ImagingCodecState state, UINT8 *buf, Py_ssize_t byt
|
|||
int N = state->state & 0xf;
|
||||
int width = state->xsize;
|
||||
int height = state->ysize;
|
||||
if ((width & 3) | (height & 3)) {
|
||||
return decode_bcn(im, state, buf, bytes, N, 1);
|
||||
} else {
|
||||
return decode_bcn(im, state, buf, bytes, N, 0);
|
||||
}
|
||||
int C = (width & 3) | (height & 3) ? 1 : 0;
|
||||
char *pixel_format = ((BCNSTATE *)state->context)->pixel_format;
|
||||
return decode_bcn(im, state, buf, bytes, N, C, pixel_format);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user