2020-02-12 19:29:19 +03:00
|
|
|
"""Test DdsImagePlugin"""
|
2016-01-16 00:34:36 +03:00
|
|
|
from io import BytesIO
|
2016-01-06 11:59:37 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
import pytest
|
2020-08-07 13:28:33 +03:00
|
|
|
|
2019-07-06 23:40:53 +03:00
|
|
|
from PIL import DdsImagePlugin, Image
|
|
|
|
|
2021-05-17 13:48:05 +03:00
|
|
|
from .helper import assert_image_equal, assert_image_equal_tofile, hopper
|
2016-01-06 11:59:37 +03:00
|
|
|
|
|
|
|
TEST_FILE_DXT1 = "Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.dds"
|
2016-01-06 12:59:47 +03:00
|
|
|
TEST_FILE_DXT3 = "Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.dds"
|
2016-01-06 11:59:37 +03:00
|
|
|
TEST_FILE_DXT5 = "Tests/images/dxt5-argb-8bbp-interpolatedalpha_MipMaps-1.dds"
|
2022-07-23 01:02:06 +03:00
|
|
|
TEST_FILE_ATI1 = "Tests/images/ati1.dds"
|
2022-07-23 00:30:27 +03:00
|
|
|
TEST_FILE_ATI2 = "Tests/images/ati2.dds"
|
2021-05-19 14:16:12 +03:00
|
|
|
TEST_FILE_DX10_BC5_TYPELESS = "Tests/images/bc5_typeless.dds"
|
2021-05-18 10:43:50 +03:00
|
|
|
TEST_FILE_DX10_BC5_UNORM = "Tests/images/bc5_unorm.dds"
|
|
|
|
TEST_FILE_DX10_BC5_SNORM = "Tests/images/bc5_snorm.dds"
|
2021-05-19 15:19:19 +03:00
|
|
|
TEST_FILE_BC5S = "Tests/images/bc5s.dds"
|
2023-08-31 05:13:35 +03:00
|
|
|
TEST_FILE_BC5U = "Tests/images/bc5u.dds"
|
2022-07-23 07:16:40 +03:00
|
|
|
TEST_FILE_BC6H = "Tests/images/bc6h.dds"
|
|
|
|
TEST_FILE_BC6HS = "Tests/images/bc6h_sf.dds"
|
2016-08-26 10:26:03 +03:00
|
|
|
TEST_FILE_DX10_BC7 = "Tests/images/bc7-argb-8bpp_MipMaps-1.dds"
|
2019-10-26 09:53:50 +03:00
|
|
|
TEST_FILE_DX10_BC7_UNORM_SRGB = "Tests/images/DXGI_FORMAT_BC7_UNORM_SRGB.dds"
|
2020-07-06 11:55:06 +03:00
|
|
|
TEST_FILE_DX10_R8G8B8A8 = "Tests/images/argb-32bpp_MipMaps-1.dds"
|
|
|
|
TEST_FILE_DX10_R8G8B8A8_UNORM_SRGB = "Tests/images/DXGI_FORMAT_R8G8B8A8_UNORM_SRGB.dds"
|
2022-12-23 09:43:18 +03:00
|
|
|
TEST_FILE_UNCOMPRESSED_L = "Tests/images/uncompressed_l.dds"
|
2022-12-23 11:07:45 +03:00
|
|
|
TEST_FILE_UNCOMPRESSED_L_WITH_ALPHA = "Tests/images/uncompressed_la.dds"
|
2021-04-05 10:58:02 +03:00
|
|
|
TEST_FILE_UNCOMPRESSED_RGB = "Tests/images/hopper.dds"
|
|
|
|
TEST_FILE_UNCOMPRESSED_RGB_WITH_ALPHA = "Tests/images/uncompressed_rgb.dds"
|
2016-01-06 11:59:37 +03:00
|
|
|
|
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
def test_sanity_dxt1():
|
|
|
|
"""Check DXT1 images can be opened"""
|
|
|
|
with Image.open(TEST_FILE_DXT1.replace(".dds", ".png")) as target:
|
|
|
|
target = target.convert("RGBA")
|
|
|
|
with Image.open(TEST_FILE_DXT1) as im:
|
|
|
|
im.load()
|
2016-01-06 11:59:37 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
assert im.format == "DDS"
|
|
|
|
assert im.mode == "RGBA"
|
|
|
|
assert im.size == (256, 256)
|
|
|
|
|
|
|
|
assert_image_equal(im, target)
|
|
|
|
|
|
|
|
|
|
|
|
def test_sanity_dxt3():
|
|
|
|
"""Check DXT3 images can be opened"""
|
|
|
|
|
2021-02-21 14:15:56 +03:00
|
|
|
with Image.open(TEST_FILE_DXT3) as im:
|
|
|
|
im.load()
|
2016-01-06 11:59:37 +03:00
|
|
|
|
2021-02-21 14:15:56 +03:00
|
|
|
assert im.format == "DDS"
|
|
|
|
assert im.mode == "RGBA"
|
|
|
|
assert im.size == (256, 256)
|
2016-01-09 00:58:19 +03:00
|
|
|
|
2021-02-21 14:15:56 +03:00
|
|
|
assert_image_equal_tofile(im, TEST_FILE_DXT3.replace(".dds", ".png"))
|
2016-01-08 18:59:18 +03:00
|
|
|
|
2016-08-26 04:12:44 +03:00
|
|
|
|
2021-05-19 14:14:51 +03:00
|
|
|
def test_sanity_dxt5():
|
|
|
|
"""Check DXT5 images can be opened"""
|
|
|
|
|
|
|
|
with Image.open(TEST_FILE_DXT5) as im:
|
|
|
|
im.load()
|
|
|
|
|
|
|
|
assert im.format == "DDS"
|
|
|
|
assert im.mode == "RGBA"
|
|
|
|
assert im.size == (256, 256)
|
|
|
|
|
|
|
|
assert_image_equal_tofile(im, TEST_FILE_DXT5.replace(".dds", ".png"))
|
|
|
|
|
|
|
|
|
2022-07-23 01:02:06 +03:00
|
|
|
def test_sanity_ati1():
|
|
|
|
"""Check ATI1 images can be opened"""
|
|
|
|
|
|
|
|
with Image.open(TEST_FILE_ATI1) as im:
|
|
|
|
im.load()
|
|
|
|
|
|
|
|
assert im.format == "DDS"
|
|
|
|
assert im.mode == "L"
|
2022-07-23 12:44:03 +03:00
|
|
|
assert im.size == (64, 64)
|
2022-07-23 01:02:06 +03:00
|
|
|
|
|
|
|
assert_image_equal_tofile(im, TEST_FILE_ATI1.replace(".dds", ".png"))
|
|
|
|
|
2022-07-23 01:02:55 +03:00
|
|
|
|
2023-08-31 05:13:35 +03:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"image_path",
|
|
|
|
(
|
|
|
|
TEST_FILE_ATI2,
|
|
|
|
# hexeditted to use BC5U FourCC
|
|
|
|
TEST_FILE_BC5U,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
def test_sanity_ati2_bc5u(image_path):
|
|
|
|
"""Check ATI2 and BC5U images can be opened"""
|
2022-07-23 00:30:27 +03:00
|
|
|
|
2023-08-31 05:13:35 +03:00
|
|
|
with Image.open(image_path) as im:
|
2022-07-23 00:30:27 +03:00
|
|
|
im.load()
|
|
|
|
|
|
|
|
assert im.format == "DDS"
|
|
|
|
assert im.mode == "RGB"
|
2022-07-23 12:44:03 +03:00
|
|
|
assert im.size == (256, 256)
|
2022-07-23 00:30:27 +03:00
|
|
|
|
2022-07-23 12:44:03 +03:00
|
|
|
assert_image_equal_tofile(im, TEST_FILE_DX10_BC5_UNORM.replace(".dds", ".png"))
|
2022-07-23 00:30:27 +03:00
|
|
|
|
|
|
|
|
2021-05-19 14:16:12 +03:00
|
|
|
@pytest.mark.parametrize(
|
2021-05-19 15:19:19 +03:00
|
|
|
("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),
|
|
|
|
),
|
2021-05-19 14:16:12 +03:00
|
|
|
)
|
2021-05-19 15:19:19 +03:00
|
|
|
def test_dx10_bc5(image_path, expected_path):
|
2021-05-19 14:16:12 +03:00
|
|
|
"""Check DX10 BC5 images can be opened"""
|
2021-05-17 11:57:23 +03:00
|
|
|
|
2021-05-19 14:16:12 +03:00
|
|
|
with Image.open(image_path) as im:
|
2021-05-17 11:57:23 +03:00
|
|
|
im.load()
|
|
|
|
|
|
|
|
assert im.format == "DDS"
|
|
|
|
assert im.mode == "RGB"
|
|
|
|
assert im.size == (256, 256)
|
|
|
|
|
2021-05-19 15:19:19 +03:00
|
|
|
assert_image_equal_tofile(im, expected_path.replace(".dds", ".png"))
|
2021-05-17 11:57:23 +03:00
|
|
|
|
|
|
|
|
2022-07-23 07:19:28 +03:00
|
|
|
@pytest.mark.parametrize("image_path", (TEST_FILE_BC6H, TEST_FILE_BC6HS))
|
|
|
|
def test_dx10_bc6h(image_path):
|
2022-07-23 07:16:40 +03:00
|
|
|
"""Check DX10 BC6H/BC6HS images can be opened"""
|
2022-07-18 22:12:48 +03:00
|
|
|
|
|
|
|
with Image.open(image_path) as im:
|
|
|
|
im.load()
|
|
|
|
|
|
|
|
assert im.format == "DDS"
|
|
|
|
assert im.mode == "RGB"
|
2022-10-08 13:20:31 +03:00
|
|
|
assert im.size == (128, 128)
|
2022-07-18 22:12:48 +03:00
|
|
|
|
2022-07-23 07:19:28 +03:00
|
|
|
assert_image_equal_tofile(im, image_path.replace(".dds", ".png"))
|
2022-07-18 22:12:48 +03:00
|
|
|
|
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
def test_dx10_bc7():
|
|
|
|
"""Check DX10 images can be opened"""
|
2016-08-26 04:12:44 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
with Image.open(TEST_FILE_DX10_BC7) as im:
|
|
|
|
im.load()
|
2016-08-26 04:12:44 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
assert im.format == "DDS"
|
|
|
|
assert im.mode == "RGBA"
|
|
|
|
assert im.size == (256, 256)
|
2016-01-06 12:59:47 +03:00
|
|
|
|
2021-02-21 14:15:56 +03:00
|
|
|
assert_image_equal_tofile(im, TEST_FILE_DX10_BC7.replace(".dds", ".png"))
|
2016-08-26 10:26:03 +03:00
|
|
|
|
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
def test_dx10_bc7_unorm_srgb():
|
|
|
|
"""Check DX10 unsigned normalized integer images can be opened"""
|
2016-08-26 10:26:03 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
with Image.open(TEST_FILE_DX10_BC7_UNORM_SRGB) as im:
|
|
|
|
im.load()
|
2016-08-26 10:26:03 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
assert im.format == "DDS"
|
|
|
|
assert im.mode == "RGBA"
|
|
|
|
assert im.size == (16, 16)
|
|
|
|
assert im.info["gamma"] == 1 / 2.2
|
2019-10-26 09:53:50 +03:00
|
|
|
|
2021-02-21 14:15:56 +03:00
|
|
|
assert_image_equal_tofile(
|
|
|
|
im, TEST_FILE_DX10_BC7_UNORM_SRGB.replace(".dds", ".png")
|
|
|
|
)
|
2019-10-26 09:53:50 +03:00
|
|
|
|
|
|
|
|
2020-07-06 11:55:06 +03:00
|
|
|
def test_dx10_r8g8b8a8():
|
|
|
|
"""Check DX10 images can be opened"""
|
|
|
|
|
|
|
|
with Image.open(TEST_FILE_DX10_R8G8B8A8) as im:
|
|
|
|
im.load()
|
|
|
|
|
|
|
|
assert im.format == "DDS"
|
|
|
|
assert im.mode == "RGBA"
|
|
|
|
assert im.size == (256, 256)
|
|
|
|
|
2021-02-21 14:15:56 +03:00
|
|
|
assert_image_equal_tofile(im, TEST_FILE_DX10_R8G8B8A8.replace(".dds", ".png"))
|
2020-07-06 11:55:06 +03:00
|
|
|
|
|
|
|
|
|
|
|
def test_dx10_r8g8b8a8_unorm_srgb():
|
|
|
|
"""Check DX10 unsigned normalized integer images can be opened"""
|
|
|
|
|
|
|
|
with Image.open(TEST_FILE_DX10_R8G8B8A8_UNORM_SRGB) as im:
|
|
|
|
im.load()
|
|
|
|
|
|
|
|
assert im.format == "DDS"
|
|
|
|
assert im.mode == "RGBA"
|
|
|
|
assert im.size == (16, 16)
|
|
|
|
assert im.info["gamma"] == 1 / 2.2
|
|
|
|
|
2021-02-21 14:15:56 +03:00
|
|
|
assert_image_equal_tofile(
|
|
|
|
im, TEST_FILE_DX10_R8G8B8A8_UNORM_SRGB.replace(".dds", ".png")
|
|
|
|
)
|
2020-07-06 11:55:06 +03:00
|
|
|
|
|
|
|
|
2022-07-19 00:18:48 +03:00
|
|
|
def test_unimplemented_dxgi_format():
|
|
|
|
with pytest.raises(NotImplementedError):
|
|
|
|
with Image.open("Tests/images/unimplemented_dxgi_format.dds"):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2022-12-23 11:07:45 +03:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
("mode", "size", "test_file"),
|
|
|
|
[
|
|
|
|
("L", (128, 128), TEST_FILE_UNCOMPRESSED_L),
|
|
|
|
("LA", (128, 128), TEST_FILE_UNCOMPRESSED_L_WITH_ALPHA),
|
|
|
|
("RGB", (128, 128), TEST_FILE_UNCOMPRESSED_RGB),
|
|
|
|
("RGBA", (800, 600), TEST_FILE_UNCOMPRESSED_RGB_WITH_ALPHA),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_uncompressed(mode, size, test_file):
|
2022-12-23 09:43:18 +03:00
|
|
|
"""Check uncompressed images can be opened"""
|
2021-04-05 10:58:02 +03:00
|
|
|
|
2022-12-23 11:07:45 +03:00
|
|
|
with Image.open(test_file) as im:
|
2020-02-12 19:29:19 +03:00
|
|
|
assert im.format == "DDS"
|
2022-12-23 11:07:45 +03:00
|
|
|
assert im.mode == mode
|
|
|
|
assert im.size == size
|
2020-02-12 19:29:19 +03:00
|
|
|
|
2022-12-23 11:07:45 +03:00
|
|
|
assert_image_equal_tofile(im, test_file.replace(".dds", ".png"))
|
2019-02-22 22:44:06 +03:00
|
|
|
|
|
|
|
|
2021-04-03 13:51:23 +03:00
|
|
|
def test__accept_true():
|
2020-02-12 19:29:19 +03:00
|
|
|
"""Check valid prefix"""
|
|
|
|
# Arrange
|
|
|
|
prefix = b"DDS etc"
|
2019-02-22 22:44:06 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
# Act
|
2021-04-03 13:51:23 +03:00
|
|
|
output = DdsImagePlugin._accept(prefix)
|
2016-01-06 11:59:37 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
# Assert
|
|
|
|
assert output
|
2016-01-06 11:59:37 +03:00
|
|
|
|
|
|
|
|
2021-04-03 13:51:23 +03:00
|
|
|
def test__accept_false():
|
2020-02-12 19:29:19 +03:00
|
|
|
"""Check invalid prefix"""
|
|
|
|
# Arrange
|
|
|
|
prefix = b"something invalid"
|
2016-01-06 11:59:37 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
# Act
|
2021-04-03 13:51:23 +03:00
|
|
|
output = DdsImagePlugin._accept(prefix)
|
2016-01-06 11:59:37 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
# Assert
|
|
|
|
assert not output
|
2016-01-06 11:59:37 +03:00
|
|
|
|
2016-01-16 00:34:36 +03:00
|
|
|
|
2022-02-27 06:47:07 +03:00
|
|
|
def test_invalid_file():
|
|
|
|
invalid_file = "Tests/images/flower.jpg"
|
|
|
|
|
|
|
|
with pytest.raises(SyntaxError):
|
|
|
|
DdsImagePlugin.DdsImageFile(invalid_file)
|
|
|
|
|
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
def test_short_header():
|
2021-08-12 14:50:09 +03:00
|
|
|
"""Check a short header"""
|
2020-02-12 19:29:19 +03:00
|
|
|
with open(TEST_FILE_DXT5, "rb") as f:
|
|
|
|
img_file = f.read()
|
2016-01-16 00:34:36 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
def short_header():
|
2021-02-11 13:43:54 +03:00
|
|
|
with Image.open(BytesIO(img_file[:119])):
|
2021-02-13 23:58:16 +03:00
|
|
|
pass # pragma: no cover
|
2016-01-16 00:34:36 +03:00
|
|
|
|
2020-04-07 09:58:21 +03:00
|
|
|
with pytest.raises(OSError):
|
2020-02-12 19:29:19 +03:00
|
|
|
short_header()
|
2016-02-05 01:57:13 +03:00
|
|
|
|
2016-01-16 00:34:36 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
def test_short_file():
|
2021-08-12 14:50:09 +03:00
|
|
|
"""Check that the appropriate error is thrown for a short file"""
|
2020-02-12 19:29:19 +03:00
|
|
|
|
|
|
|
with open(TEST_FILE_DXT5, "rb") as f:
|
|
|
|
img_file = f.read()
|
|
|
|
|
|
|
|
def short_file():
|
|
|
|
with Image.open(BytesIO(img_file[:-100])) as im:
|
|
|
|
im.load()
|
|
|
|
|
2020-04-07 09:58:21 +03:00
|
|
|
with pytest.raises(OSError):
|
2020-02-12 19:29:19 +03:00
|
|
|
short_file()
|
2016-01-16 00:34:36 +03:00
|
|
|
|
2019-02-23 00:53:45 +03:00
|
|
|
|
2020-01-09 06:06:48 +03:00
|
|
|
def test_dxt5_colorblock_alpha_issue_4142():
|
2021-08-12 14:50:09 +03:00
|
|
|
"""Check that colorblocks are decoded correctly in DXT5"""
|
2020-01-09 06:06:48 +03:00
|
|
|
|
|
|
|
with Image.open("Tests/images/dxt5-colorblock-alpha-issue-4142.dds") as im:
|
|
|
|
px = im.getpixel((0, 0))
|
|
|
|
assert px[0] != 0
|
|
|
|
assert px[1] != 0
|
|
|
|
assert px[2] != 0
|
|
|
|
|
|
|
|
px = im.getpixel((1, 0))
|
|
|
|
assert px[0] != 0
|
|
|
|
assert px[1] != 0
|
|
|
|
assert px[2] != 0
|
|
|
|
|
|
|
|
|
2023-09-27 12:41:45 +03:00
|
|
|
def test_palette():
|
|
|
|
with Image.open("Tests/images/palette.dds") as im:
|
|
|
|
assert_image_equal_tofile(im, "Tests/images/transparent.gif")
|
|
|
|
|
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
def test_unimplemented_pixel_format():
|
|
|
|
with pytest.raises(NotImplementedError):
|
2021-02-11 13:43:54 +03:00
|
|
|
with Image.open("Tests/images/unimplemented_pixel_format.dds"):
|
|
|
|
pass
|
2021-05-17 13:48:05 +03:00
|
|
|
|
|
|
|
|
|
|
|
def test_save_unsupported_mode(tmp_path):
|
|
|
|
out = str(tmp_path / "temp.dds")
|
|
|
|
im = hopper("HSV")
|
|
|
|
with pytest.raises(OSError):
|
|
|
|
im.save(out)
|
|
|
|
|
|
|
|
|
2021-05-17 13:48:31 +03:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
("mode", "test_file"),
|
|
|
|
[
|
2022-12-23 09:43:18 +03:00
|
|
|
("L", "Tests/images/linear_gradient.png"),
|
2022-12-23 11:07:45 +03:00
|
|
|
("LA", "Tests/images/uncompressed_la.png"),
|
2021-05-17 13:48:31 +03:00
|
|
|
("RGB", "Tests/images/hopper.png"),
|
|
|
|
("RGBA", "Tests/images/pil123rgba.png"),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_save(mode, test_file, tmp_path):
|
2021-05-17 13:48:05 +03:00
|
|
|
out = str(tmp_path / "temp.dds")
|
2021-05-17 13:48:31 +03:00
|
|
|
with Image.open(test_file) as im:
|
|
|
|
assert im.mode == mode
|
|
|
|
im.save(out)
|
2021-05-17 13:48:05 +03:00
|
|
|
|
2021-05-17 13:48:31 +03:00
|
|
|
with Image.open(out) as reloaded:
|
|
|
|
assert_image_equal(im, reloaded)
|