diff --git a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDS.java b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDS.java index 02282708..5ed74509 100644 --- a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDS.java +++ b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDS.java @@ -32,7 +32,7 @@ package com.twelvemonkeys.imageio.plugins.dds; @SuppressWarnings("unused") interface DDS { - byte[] MAGIC = new byte[]{'D', 'D', 'S', ' '}; + int MAGIC = ('D' << 24) + ('D' << 16) + ('S' << 8) + ' '; //Big-Endian int HEADER_SIZE = 124; // Header Flags @@ -48,4 +48,7 @@ interface DDS { // Pixel Format Flags int PIXEL_FORMAT_FLAG_FOURCC = 0x04; int PIXEL_FORMAT_FLAG_RGB = 0x40; + + //DX10 Resource Dimensions + int D3D10_RESOURCE_DIMENSION_TEXTURE2D = 3; } diff --git a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSHeader.java b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSHeader.java index bc784fda..23d7b55a 100644 --- a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSHeader.java +++ b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSHeader.java @@ -34,8 +34,7 @@ import javax.imageio.IIOException; import javax.imageio.stream.ImageInputStream; import java.awt.Dimension; import java.io.IOException; -import java.math.BigInteger; -import java.util.Arrays; +import java.nio.ByteOrder; final class DDSHeader { @@ -58,11 +57,12 @@ final class DDSHeader { DDSHeader header = new DDSHeader(); // Read MAGIC bytes [0,3] - byte[] magic = new byte[DDS.MAGIC.length]; - imageInput.readFully(magic); - if (!Arrays.equals(DDS.MAGIC, magic)) { - throw new IIOException(String.format("Not a DDS file. Expected DDS magic 0x%08x', read 0x%08x", new BigInteger(DDS.MAGIC), new BigInteger(magic))); + imageInput.setByteOrder(ByteOrder.BIG_ENDIAN); + int magic = imageInput.readInt(); + if (magic != DDS.MAGIC) { + throw new IIOException(String.format("Not a DDS file. Expected DDS magic 0x%8x', read 0x%8x", DDS.MAGIC, magic)); } + imageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN); // DDS_HEADER structure // https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dds-header @@ -93,8 +93,7 @@ final class DDSHeader { // build dimensions list header.addDimensions(dwWidth, dwHeight); - byte[] dwReserved1 = new byte[11 * 4]; // [32,75] - imageInput.readFully(dwReserved1); + imageInput.skipBytes(44); // DDS_PIXELFORMAT structure int px_dwSize = imageInput.readInt(); // [76,79] diff --git a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageReaderSpi.java b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageReaderSpi.java index 33113c38..28af3768 100644 --- a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageReaderSpi.java +++ b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageReaderSpi.java @@ -35,7 +35,6 @@ import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase; import javax.imageio.ImageReader; import javax.imageio.stream.ImageInputStream; import java.io.IOException; -import java.util.Arrays; import java.util.Locale; public final class DDSImageReaderSpi extends ImageReaderSpiBase { @@ -55,10 +54,7 @@ public final class DDSImageReaderSpi extends ImageReaderSpiBase { stream.mark(); try { - byte[] magic = new byte[DDS.MAGIC.length]; - stream.readFully(magic); - - return Arrays.equals(DDS.MAGIC, magic); + return stream.readInt() == DDS.MAGIC; } finally { stream.reset(); } diff --git a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSReader.java b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSReader.java index dc8a8dae..8b40f963 100644 --- a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSReader.java +++ b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSReader.java @@ -74,15 +74,19 @@ final class DDSReader { static final Order ARGB_ORDER = new Order(16, 8, 0, 24); private final DDSHeader header; + private DX10Header dxt10Header; DDSReader(DDSHeader header) { this.header = header; } int[] read(ImageInputStream imageInput, int imageIndex) throws IOException { - // type DDSType type = getType(); + if (type == DDSType.DXT10) { + dxt10Header = DX10Header.read(imageInput); + type = dxt10Header.getDDSType(); + } // offset buffer to index mipmap image byte[] buffer = null; @@ -138,7 +142,6 @@ final class DDSReader { // DXT int type = header.getFourCC(); return DDSType.valueOf(type); - } else if ((flags & DDS.PIXEL_FORMAT_FLAG_RGB) != 0) { // RGB int bitCount = header.getBitCount(); @@ -224,6 +227,7 @@ final class DDSReader { } } + private static int[] decodeDXT1(int width, int height, byte[] buffer) { int[] pixels = new int[width * height]; int index = 0; @@ -241,7 +245,7 @@ final class DDSReader { int t1 = (buffer[index] & 0x0C) >> 2; int t2 = (buffer[index] & 0x30) >> 4; int t3 = (buffer[index++] & 0xC0) >> 6; - pixels[4 * width * i + 4 * j + width * k ] = getDXTColor(c0, c1, 0xFF, t0); + pixels[4 * width * i + 4 * j + width * k] = getDXTColor(c0, c1, 0xFF, t0); if (4 * j + 1 >= width) continue; pixels[4 * width * i + 4 * j + width * k + 1] = getDXTColor(c0, c1, 0xFF, t1); if (4 * j + 2 >= width) continue; @@ -286,7 +290,7 @@ final class DDSReader { int t1 = (buffer[index] & 0x0C) >> 2; int t2 = (buffer[index] & 0x30) >> 4; int t3 = (buffer[index++] & 0xC0) >> 6; - pixels[4 * width * i + 4 * j + width * k ] = getDXTColor(c0, c1, alphaTable[4 * k ], t0); + pixels[4 * width * i + 4 * j + width * k] = getDXTColor(c0, c1, alphaTable[4 * k], t0); if (4 * j + 1 >= width) continue; pixels[4 * width * i + 4 * j + width * k + 1] = getDXTColor(c0, c1, alphaTable[4 * k + 1], t1); if (4 * j + 2 >= width) continue; @@ -344,7 +348,7 @@ final class DDSReader { int t1 = (buffer[index] & 0x0C) >> 2; int t2 = (buffer[index] & 0x30) >> 4; int t3 = (buffer[index++] & 0xC0) >> 6; - pixels[4 * width * i + 4 * j + width * k ] = getDXTColor(c0, c1, getDXT5Alpha(a0, a1, alphaTable[4 * k ]), t0); + pixels[4 * width * i + 4 * j + width * k] = getDXTColor(c0, c1, getDXT5Alpha(a0, a1, alphaTable[4 * k]), t0); if (4 * j + 1 >= width) continue; pixels[4 * width * i + 4 * j + width * k + 1] = getDXTColor(c0, c1, getDXT5Alpha(a0, a1, alphaTable[4 * k + 1]), t1); if (4 * j + 2 >= width) continue; diff --git a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSType.java b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSType.java index 370e37e4..da552c85 100644 --- a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSType.java +++ b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSType.java @@ -31,12 +31,12 @@ package com.twelvemonkeys.imageio.plugins.dds; enum DDSType { - DXT1(0x31545844), DXT2(0x32545844), DXT3(0x33545844), DXT4(0x34545844), DXT5(0x35545844), + DXT10(0x30315844), A1R5G5B5((1 << 16) | 2), X1R5G5B5((2 << 16) | 2), A4R4G4B4((3 << 16) | 2), diff --git a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DX10DXGIFormat.java b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DX10DXGIFormat.java new file mode 100644 index 00000000..a08efdb6 --- /dev/null +++ b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DX10DXGIFormat.java @@ -0,0 +1,51 @@ +package com.twelvemonkeys.imageio.plugins.dds; + +import java.util.Arrays; +import java.util.function.IntPredicate; +import java.util.stream.IntStream; + +/** + * Enum that lists a certain types of DXGI Format this reader supports to read. + * + * @link DXGI Format List + */ +public enum DX10DXGIFormat { + BC1(DDSType.DXT1, rangeInclusive(70, 72)), + BC2(DDSType.DXT2, rangeInclusive(73, 75)), + BC3(DDSType.DXT5, rangeInclusive(76, 78)), + //BC7(99), + B8G8R8A8(DDSType.A8B8G8R8, exactly(87, 90, 91)), + B8G8R8X8(DDSType.X8B8G8R8, exactly(88, 92, 93)), + R8G8B8A8(DDSType.A8R8G8B8, rangeInclusive(27, 32)); + private final DDSType ddsType; + private final IntPredicate dxgiFormat; + + DX10DXGIFormat(DDSType ddsType, IntPredicate dxgiFormat) { + this.ddsType = ddsType; + this.dxgiFormat = dxgiFormat; + } + + DDSType getCorrespondingType() { + return ddsType; + } + + static DX10DXGIFormat getFormat(int value) { + for (DX10DXGIFormat format : values()) { + if (format.dxgiFormat.test(value)) return format; + } + + throw new IllegalArgumentException("Unsupported DXGI_FORMAT : " + value); + } + + + /** + * @param acceptedValues values in DXGI Formats List, passed values are expected to be in ascending order + */ + private static IntPredicate exactly(int ... acceptedValues) { + return test -> Arrays.binarySearch(acceptedValues, test) >= 0; + } + + private static IntPredicate rangeInclusive(int from, int to) { + return test -> from <= test && test <= to; + } +} diff --git a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DX10Header.java b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DX10Header.java new file mode 100644 index 00000000..d599d7f8 --- /dev/null +++ b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DX10Header.java @@ -0,0 +1,33 @@ +package com.twelvemonkeys.imageio.plugins.dds; + +import javax.imageio.stream.ImageInputStream; +import java.io.IOException; + +//https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dds-header-dxt10 +public final class DX10Header { + final DX10DXGIFormat dxgiFormat; + final int resourceDimension, miscFlag, arraySize, miscFlags2; + + private DX10Header(int dxgiFormat, int resourceDimension, int miscFlag, int arraySize, int miscFlags2) { + this.dxgiFormat = DX10DXGIFormat.getFormat(dxgiFormat); + this.resourceDimension = resourceDimension; + if (this.resourceDimension != DDS.D3D10_RESOURCE_DIMENSION_TEXTURE2D) + throw new IllegalArgumentException("Resource dimension " + resourceDimension + " is not supported, expected 3."); + this.miscFlag = miscFlag; + this.arraySize = arraySize; + this.miscFlags2 = miscFlags2; + } + + static DX10Header read(ImageInputStream inputStream) throws IOException { + int dxgiFormat = inputStream.readInt(); + int resourceDimension = inputStream.readInt(); + int miscFlag = inputStream.readInt(); + int arraySize = inputStream.readInt(); + int miscFlags2 = inputStream.readInt(); + return new DX10Header(dxgiFormat, resourceDimension, miscFlag, arraySize, miscFlags2); + } + + DDSType getDDSType() { + return dxgiFormat.getCorrespondingType(); + } +} diff --git a/imageio/imageio-dds/src/test/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageReaderTest.java b/imageio/imageio-dds/src/test/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageReaderTest.java index a9c7a52e..5b4c347b 100644 --- a/imageio/imageio-dds/src/test/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageReaderTest.java +++ b/imageio/imageio-dds/src/test/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageReaderTest.java @@ -86,7 +86,13 @@ public class DDSImageReaderTest extends ImageReaderAbstractTest new TestData(getClassLoaderResource("/dds/dds_X8B8G8R8.dds"), dim256), new TestData(getClassLoaderResource("/dds/dds_X8B8G8R8_mipmap.dds"), dim256, dim128, dim64), new TestData(getClassLoaderResource("/dds/dds_X8R8G8B8.dds"), dim256), - new TestData(getClassLoaderResource("/dds/dds_X8R8G8B8_mipmap.dds"), dim256, dim128, dim64) + new TestData(getClassLoaderResource("/dds/dds_X8R8G8B8_mipmap.dds"), dim256, dim128, dim64), + new TestData(getClassLoaderResource("/dds/dxt10_BC1_sRGB.dds"), dim256), + new TestData(getClassLoaderResource("/dds/dxt10_BC2_sRGB.dds"), dim256), + new TestData(getClassLoaderResource("/dds/dxt10_BC3_sRGB.dds"), dim256), + new TestData(getClassLoaderResource("/dds/dxt10_B8G8R8A8.dds"), dim256), + new TestData(getClassLoaderResource("/dds/dxt10_B8G8R8X8.dds"), dim256), + new TestData(getClassLoaderResource("/dds/dxt10_R8G8B8A8.dds"), dim256) ); } diff --git a/imageio/imageio-dds/src/test/resources/dds/dxt10_B8G8R8A8.dds b/imageio/imageio-dds/src/test/resources/dds/dxt10_B8G8R8A8.dds new file mode 100644 index 00000000..0b9ffcdd Binary files /dev/null and b/imageio/imageio-dds/src/test/resources/dds/dxt10_B8G8R8A8.dds differ diff --git a/imageio/imageio-dds/src/test/resources/dds/dxt10_B8G8R8X8.dds b/imageio/imageio-dds/src/test/resources/dds/dxt10_B8G8R8X8.dds new file mode 100644 index 00000000..fb5f27e4 Binary files /dev/null and b/imageio/imageio-dds/src/test/resources/dds/dxt10_B8G8R8X8.dds differ diff --git a/imageio/imageio-dds/src/test/resources/dds/dxt10_BC1_sRGB.dds b/imageio/imageio-dds/src/test/resources/dds/dxt10_BC1_sRGB.dds new file mode 100644 index 00000000..339213a4 Binary files /dev/null and b/imageio/imageio-dds/src/test/resources/dds/dxt10_BC1_sRGB.dds differ diff --git a/imageio/imageio-dds/src/test/resources/dds/dxt10_BC2_sRGB.dds b/imageio/imageio-dds/src/test/resources/dds/dxt10_BC2_sRGB.dds new file mode 100644 index 00000000..e0fd8b66 Binary files /dev/null and b/imageio/imageio-dds/src/test/resources/dds/dxt10_BC2_sRGB.dds differ diff --git a/imageio/imageio-dds/src/test/resources/dds/dxt10_BC3_sRGB.dds b/imageio/imageio-dds/src/test/resources/dds/dxt10_BC3_sRGB.dds new file mode 100644 index 00000000..6460ed3c Binary files /dev/null and b/imageio/imageio-dds/src/test/resources/dds/dxt10_BC3_sRGB.dds differ diff --git a/imageio/imageio-dds/src/test/resources/dds/dxt10_R8G8B8A8.dds b/imageio/imageio-dds/src/test/resources/dds/dxt10_R8G8B8A8.dds new file mode 100644 index 00000000..70e076f4 Binary files /dev/null and b/imageio/imageio-dds/src/test/resources/dds/dxt10_R8G8B8A8.dds differ