From afc3e2d78813801f4c817058e6da40ba4e61bb51 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 13 Apr 2021 08:46:46 +1000 Subject: [PATCH] Added RGB saving --- Tests/test_file_dds.py | 18 +++++++++++++++++- src/PIL/DdsImagePlugin.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_dds.py b/Tests/test_file_dds.py index 236ad41d67a..0fd0ec8f7ae 100644 --- a/Tests/test_file_dds.py +++ b/Tests/test_file_dds.py @@ -5,7 +5,7 @@ from PIL import DdsImagePlugin, Image -from .helper import assert_image_equal, assert_image_equal_tofile +from .helper import assert_image_equal, assert_image_equal_tofile, hopper TEST_FILE_DXT1 = "Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.dds" TEST_FILE_DXT3 = "Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.dds" @@ -199,3 +199,19 @@ def test_unimplemented_pixel_format(): with pytest.raises(NotImplementedError): with Image.open("Tests/images/unimplemented_pixel_format.dds"): pass + + +def test_save_unsupported_mode(tmp_path): + out = str(tmp_path / "temp.dds") + im = hopper("HSV") + with pytest.raises(OSError): + im.save(out) + + +def test_save(tmp_path): + out = str(tmp_path / "temp.dds") + im = hopper() + im.save(out) + + with Image.open(out) as reloaded: + assert_image_equal(im, reloaded) diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index c1681a90041..f0cb48973bf 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -14,6 +14,7 @@ from io import BytesIO from . import Image, ImageFile +from ._binary import o32le as o32 # Magic ("DDS ") DDS_MAGIC = 0x20534444 @@ -184,9 +185,43 @@ def load_seek(self, pos): pass +def _save(im, fp, filename): + if im.mode != "RGB": + raise OSError(f"cannot write mode {im.mode} as DDS") + + fp.write( + o32(DDS_MAGIC) + + o32(124) # header size + + o32( + DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PITCH | DDSD_PIXELFORMAT + ) # flags + + o32(im.height) + + o32(im.width) + + o32((im.width * 24 + 7) // 8) # pitch + + o32(0) # depth + + o32(0) # mipmaps + + o32(0) * 11 # reserved + + o32(32) # pfsize + + o32(DDPF_RGB) # pfflags + + o32(0) # fourcc + + o32(24) # bitcount + + o32(0xFF0000) # rbitmask + + o32(0xFF00) # gbitmask + + o32(0xFF) # bbitmask + + o32(0) # abitmask + + o32(0) # dwCaps + + o32(0) # dwCaps2 + + o32(0) # dwCaps3 + + o32(0) # dwCaps4 + + o32(0) # dwReserved2 + ) + ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (im.mode[::-1], 0, 1))]) + + def _accept(prefix): return prefix[:4] == b"DDS " Image.register_open(DdsImageFile.format, DdsImageFile, _accept) +Image.register_save(DdsImageFile.format, _save) Image.register_extension(DdsImageFile.format, ".dds")