diff --git a/PIL/DdsImagePlugin.py b/PIL/DdsImagePlugin.py index 2ebfdf037..b7725872f 100644 --- a/PIL/DdsImagePlugin.py +++ b/PIL/DdsImagePlugin.py @@ -174,6 +174,38 @@ def _dxtc_alpha(a0, a1, ac0, ac1, ai): return alpha +def _dxt3(data, width, height): + # TODO implement this function as pixel format in decode.c + ret = bytearray(4 * width * height) + + for y in range(0, height, 4): + for x in range(0, width, 4): + ai, c0, c1, code = struct.unpack("> 4 * (4 * j + i)) & 15) * 17 # map alpha to range 0-255 + + cc = (code >> 2 * (4 * j + i)) & 3 + if cc == 0: + r, g, b = r0, g0, b0 + elif cc == 1: + r, g, b = r1, g1, b1 + elif cc == 2: + r, g, b = _c2a(r0, r1), _c2a(g0, g1), _c2a(b0, b1) + elif cc == 3: + r, g, b = _c3(r0, r1), _c3(g0, g1), _c3(b0, b1) + + idx = 4 * ((y + j) * width + (x + i)) + ret[idx:idx+4] = struct.pack('4B', r, g, b, alpha) + + return bytes(ret) + + def _dxt5(data, width, height): # TODO implement this function as pixel format in decode.c ret = bytearray(4 * width * height) @@ -240,6 +272,9 @@ class DdsImageFile(ImageFile.ImageFile): if fourcc == b"DXT1": self.pixel_format = "DXT1" codec = _dxt1 + elif fourcc == b"DXT3": + self.pixel_format = "DXT3" + codec = _dxt3 elif fourcc == b"DXT5": self.pixel_format = "DXT5" codec = _dxt5 diff --git a/Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.png b/Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.png new file mode 100644 index 000000000..25d227b6b Binary files /dev/null and b/Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.png differ diff --git a/Tests/test_file_dds.py b/Tests/test_file_dds.py index e4fbca82c..9a2511877 100644 --- a/Tests/test_file_dds.py +++ b/Tests/test_file_dds.py @@ -46,9 +46,24 @@ class TestFileDds(PillowTestCase): self.assert_image_similar(target, im, 5) def test_sanity_dxt3(self): - """Check DXT3 images are not supported""" - self.assertRaises(NotImplementedError, - lambda: Image.open(TEST_FILE_DXT3)) + """Check DXT3 images can be opened""" + + target = Image.open(TEST_FILE_DXT3.replace('.dds', '.png')) + + im = Image.open(TEST_FILE_DXT3) + im.load() + + self.assertEqual(im.format, "DDS") + self.assertEqual(im.mode, "RGBA") + self.assertEqual(im.size, (256, 256)) + + # Imagemagick, which generated this target image from the .dds + # has a slightly different decoder than is standard. It looks + # a little brighter. The 0,0 pixel is (00,6c,f8,ff) by our code, + # and by the target image for the DXT1, and the imagemagick .png + # is giving (00, 6d, ff, ff). So, assert similar, pretty tight + # I'm currently seeing about a 3 for the epsilon. + self.assert_image_similar(target, im, 5) def test__validate_true(self): """Check valid prefix"""