DDS DXT10 support, with some certain supported DXGI Formats only. (#1230)

* dds dxt10 support, with some certain supported DXGI Formats only.
* expand the supporting range for some DX10 DXGI Format in the DXGI_FORMAT enumeration
* readability and maintainability fixes, adding DXT10 test cases.
* java.awt.* -> java.awt.Dimension
This commit is contained in:
KhanhTypo
2025-12-17 23:38:47 +07:00
committed by GitHub
parent a09629be32
commit 46a399ff02
14 changed files with 113 additions and 21 deletions

View File

@@ -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;
}

View File

@@ -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]

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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),

View File

@@ -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 <a href="https://learn.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format">DXGI Format List</a>
*/
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;
}
}

View File

@@ -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();
}
}

View File

@@ -86,7 +86,13 @@ public class DDSImageReaderTest extends ImageReaderAbstractTest<DDSImageReader>
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)
);
}