mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-04-05 00:00:01 -04:00
Microsoft Direct DrawSurface (DDS) support. (#1021)
* Basic wrapper around DDSReader.
Nasty hack to get initial plugin working...
Mark and Reset the imageinput stream when reading the header. Then read the whole buffer into DDSReader and generate a fixed BufferedImage ignoring the getDestination() BufferedImage.
* Read header (DDSHeader) and pass into DDSReader
Remove header methods using buffer offset addressing.
Switch endian for DX1-DX5 types
* Fix pixel order to ARGB
* Push ImageInputStream into DDSReader
Unable to determine buffer length (so as a hack I over allocate buffer and read)
```
byte[] buffer = new byte[width * height * 4];
int len = imageInput.read(buffer);
```
Added test files for all supported formats.
* Added processImageStarted and stubbed out processImageProgress and processReadAborted
Added all DDS format test cases to getTestData
* Remove offset and use imageInput.readFully
Reads next image calculating/updating width/height from mipmapLevel.
Probably will never get used as we only want the largest image returned.
* Code cleanup and added exception handling.
* Use Enum DDSType instead of int values.
Pass imageIndex into mipmap.
* Update imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSHeader.java
Improve IIOException "Invalid DDS header size"
Co-authored-by: Harald Kuhr <harald.kuhr@gmail.com>
* Update imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSHeader.java
Improve Magic IIOException
Co-authored-by: Harald Kuhr <harald.kuhr@gmail.com>
* Delete .run directory
* Format tabs -> 4 spaces
* Convert 4byte array into %08x string format.
* Don't need to expose classes outside the package
DDSHeader, DDSReader, DDSType
* (fix) wrong public...
* Move setByteOrder to DDSImageReader.readHeader
* Use imageIndex to calculate height/width and buffer offset.
* Delete .gitignore
* Revert "Delete .gitignore"
This reverts commit 71a4e73ca6.
* Undelete/Restore .gitignore
* Simplify String.format into one.
* Override failing tests and ignore.
* Revert formatting on PNGImageReaderTest.java
---------
Co-authored-by: Harald Kuhr <harald.kuhr@gmail.com>
This commit is contained in:
@@ -63,6 +63,11 @@
|
||||
<artifactId>imageio-hdr</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-dds</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-icns</artifactId>
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.11.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-dds</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: DDS plugin</name>
|
||||
<description>
|
||||
ImageIO plugin for Microsoft Direct DrawSurface (DDS).
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<project.jpms.module.name>com.twelvemonkeys.imageio.dds</project.jpms.module.name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-metadata</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Provide-Capability>
|
||||
osgi.serviceloader;
|
||||
osgi.serviceloader=javax.imageio.spi.ImageReaderSpi
|
||||
</Provide-Capability>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.twelvemonkeys.imageio.plugins.dds;
|
||||
|
||||
interface DDS {
|
||||
byte[] MAGIC = new byte[]{'D', 'D', 'S', ' '};
|
||||
int HEADER_SIZE = 124;
|
||||
|
||||
int FLAG_CAPS = 0x1; // Required in every .dds file.
|
||||
int FLAG_HEIGHT = 0x2; // Required in every .dds file.
|
||||
int FLAG_WIDTH = 0x4; // Required in every .dds file.
|
||||
int FLAG_PITCH = 0x8; // Required when pitch is provided for an uncompressed texture.
|
||||
int FLAG_PIXELFORMAT = 0x1000; // Required in every .dds file.
|
||||
int FLAG_MIPMAPCOUNT = 0x20000; // Required in a mipmapped texture.
|
||||
int FLAG_LINEARSIZE = 0x80000; // Required when pitch is provided for a compressed texture.
|
||||
int FLAG_DEPTH = 0x800000; // Required in a depth texture.
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
package com.twelvemonkeys.imageio.plugins.dds;
|
||||
|
||||
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;
|
||||
|
||||
final class DDSHeader {
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-pguide
|
||||
private int flags;
|
||||
|
||||
private int mipMapCount;
|
||||
private Dimension[] dimensions;
|
||||
|
||||
private int pixelFormatFlags;
|
||||
private int fourCC;
|
||||
private int bitCount;
|
||||
private int redMask;
|
||||
private int greenMask;
|
||||
private int blueMask;
|
||||
private int alphaMask;
|
||||
|
||||
public static DDSHeader read(final ImageInputStream imageInput) throws IOException {
|
||||
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 %08x, read %08x", new BigInteger(1, DDS.MAGIC), new BigInteger(1, magic)));
|
||||
}
|
||||
|
||||
// DDS_HEADER structure
|
||||
// https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dds-header
|
||||
int dwSize = imageInput.readInt(); // [4,7]
|
||||
if (dwSize != DDS.HEADER_SIZE) {
|
||||
throw new IIOException(String.format("Invalid DDS header size (expected %d): %d", DDS.HEADER_SIZE, dwSize));
|
||||
}
|
||||
|
||||
// Verify flags
|
||||
header.flags = imageInput.readInt(); // [8,11]
|
||||
if (header.getFlag(DDS.FLAG_CAPS
|
||||
& DDS.FLAG_HEIGHT
|
||||
& DDS.FLAG_WIDTH
|
||||
& DDS.FLAG_PIXELFORMAT)) {
|
||||
throw new IIOException("Required DDS Flag missing in header: " + Integer.toHexString(header.flags));
|
||||
}
|
||||
|
||||
// Read Height & Width
|
||||
int dwHeight = imageInput.readInt(); // [12,15]
|
||||
int dwWidth = imageInput.readInt(); // [16,19]
|
||||
|
||||
int dwPitchOrLinearSize = imageInput.readInt(); // [20,23]
|
||||
int dwDepth = imageInput.readInt(); // [24,27]
|
||||
header.mipMapCount = imageInput.readInt(); // [28,31]
|
||||
|
||||
// build dimensions list
|
||||
header.addDimensions(dwWidth, dwHeight);
|
||||
|
||||
byte[] dwReserved1 = new byte[11 * 4]; // [32,75]
|
||||
imageInput.readFully(dwReserved1);
|
||||
|
||||
// DDS_PIXELFORMAT structure
|
||||
int px_dwSize = imageInput.readInt(); // [76,79]
|
||||
|
||||
header.pixelFormatFlags = imageInput.readInt(); // [80,83]
|
||||
header.fourCC = imageInput.readInt(); // [84,87]
|
||||
header.bitCount = imageInput.readInt(); // [88,91]
|
||||
header.redMask = imageInput.readInt(); // [92,95]
|
||||
header.greenMask = imageInput.readInt(); // [96,99]
|
||||
header.blueMask = imageInput.readInt(); // [100,103]
|
||||
header.alphaMask = imageInput.readInt(); // [104,107]
|
||||
|
||||
int dwCaps = imageInput.readInt(); // [108,111]
|
||||
int dwCaps2 = imageInput.readInt(); // [112,115]
|
||||
int dwCaps3 = imageInput.readInt(); // [116,119]
|
||||
int dwCaps4 = imageInput.readInt(); // [120,123]
|
||||
|
||||
int dwReserved2 = imageInput.readInt(); // [124,127]
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
private void addDimensions(int width, int height) {
|
||||
dimensions = new Dimension[getMipMapCount()];
|
||||
|
||||
int w = width;
|
||||
int h = height;
|
||||
for (int i = 0; i < getMipMapCount(); i++) {
|
||||
dimensions[i] = new Dimension(w, h);
|
||||
w /= 2;
|
||||
h /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean getFlag(int mask) {
|
||||
return (flags & mask) != 0;
|
||||
}
|
||||
|
||||
public int getWidth(int imageIndex) {
|
||||
int lim = dimensions[imageIndex].width;
|
||||
return (lim <= 0) ? 1 : lim;
|
||||
}
|
||||
|
||||
public int getHeight(int imageIndex) {
|
||||
int lim = dimensions[imageIndex].height;
|
||||
return (lim <= 0) ? 1 : lim;
|
||||
}
|
||||
|
||||
public int getMipMapCount() {
|
||||
// 0 = (unused) or 1 = (1 level), but still only one 'base' image
|
||||
return (mipMapCount == 0) ? 1 : mipMapCount;
|
||||
}
|
||||
|
||||
public int getAlphaMask() {
|
||||
return alphaMask;
|
||||
}
|
||||
|
||||
public int getBitCount() {
|
||||
return bitCount;
|
||||
}
|
||||
|
||||
public int getBlueMask() {
|
||||
return blueMask;
|
||||
}
|
||||
|
||||
public int getFlags() {
|
||||
return flags;
|
||||
}
|
||||
|
||||
public int getFourCC() {
|
||||
return fourCC;
|
||||
}
|
||||
|
||||
public int getGreenMask() {
|
||||
return greenMask;
|
||||
}
|
||||
|
||||
public int getPixelFormatFlags() {
|
||||
return pixelFormatFlags;
|
||||
}
|
||||
|
||||
public int getRedMask() {
|
||||
return redMask;
|
||||
}
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
package com.twelvemonkeys.imageio.plugins.dds;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public final class DDSImageReader extends ImageReaderBase {
|
||||
|
||||
private DDSHeader header;
|
||||
|
||||
public DDSImageReader(final ImageReaderSpi provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetMembers() {
|
||||
header = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth(final int imageIndex) throws IOException {
|
||||
checkBounds(imageIndex);
|
||||
readHeader();
|
||||
|
||||
return header.getWidth(imageIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight(int imageIndex) throws IOException {
|
||||
checkBounds(imageIndex);
|
||||
readHeader();
|
||||
|
||||
return header.getHeight(imageIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumImages(final boolean allowSearch) throws IOException {
|
||||
readHeader();
|
||||
|
||||
return header.getMipMapCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageTypeSpecifier getRawImageType(int imageIndex) throws IOException {
|
||||
checkBounds(imageIndex);
|
||||
readHeader();
|
||||
|
||||
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
|
||||
return Collections.singletonList(getRawImageType(imageIndex)).iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
|
||||
checkBounds(imageIndex);
|
||||
readHeader();
|
||||
|
||||
processImageStarted(imageIndex);
|
||||
|
||||
DDSReader dds = new DDSReader(header);
|
||||
int[] pixels = dds.read(imageInput, imageIndex);
|
||||
|
||||
int width = getWidth(imageIndex);
|
||||
int height = getHeight(imageIndex);
|
||||
|
||||
BufferedImage destination = getDestination(param, getImageTypes(imageIndex), width, height);
|
||||
destination.setRGB(0, 0, width, height, pixels, 0, width);
|
||||
|
||||
// TODO: break read into raster line and add progress and abort checks
|
||||
processImageProgress(100f);
|
||||
if (abortRequested()) {
|
||||
processReadAborted();
|
||||
}
|
||||
|
||||
processImageComplete();
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
private void readHeader() throws IOException {
|
||||
if (header == null) {
|
||||
imageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
header = DDSHeader.read(imageInput);
|
||||
|
||||
imageInput.flushBefore(imageInput.getStreamPosition());
|
||||
}
|
||||
|
||||
imageInput.seek(imageInput.getFlushedPosition());
|
||||
}
|
||||
|
||||
public static void main(final String[] args) throws IOException {
|
||||
|
||||
String parentDir = "imageio/imageio-dds/src/test/resources/dds";
|
||||
|
||||
List<File> testFiles = new ArrayList<>();
|
||||
testFiles.add(new File(parentDir, "dds_A1R5G5B5.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_A1R5G5B5_mipmap.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_A4R4G4B4.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_A4R4G4B4_mipmap.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_A8B8G8R8.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_A8B8G8R8_mipmap.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_A8R8G8B8.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_A8R8G8B8_mipmap.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_DXT1.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_DXT1_mipmap.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_DXT2.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_DXT2_mipmap.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_DXT3.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_DXT3_mipmap.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_DXT4.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_DXT4_mipmap.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_DXT5.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_DXT5_mipmap.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_R5G6B5.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_R5G6B5_mipmap.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_R8G8B8.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_R8G8B8_mipmap.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_X1R5G5B5.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_X1R5G5B5_mipmap.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_X4R4G4B4.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_X4R4G4B4_mipmap.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_X8B8G8R8.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_X8B8G8R8_mipmap.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_X8R8G8B8.dds"));
|
||||
testFiles.add(new File(parentDir, "dds_X8R8G8B8_mipmap.dds"));
|
||||
|
||||
for (File file : testFiles) {
|
||||
BufferedImage image = ImageIO.read(file);
|
||||
showIt(image, file.getName());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.twelvemonkeys.imageio.plugins.dds;
|
||||
|
||||
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 {
|
||||
|
||||
public DDSImageReaderSpi() {
|
||||
super(new DDSProviderInfo());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDecodeInput(final Object source) throws IOException {
|
||||
if (!(source instanceof ImageInputStream)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ImageInputStream stream = (ImageInputStream) source;
|
||||
|
||||
stream.mark();
|
||||
|
||||
try {
|
||||
byte[] magic = new byte[DDS.MAGIC.length];
|
||||
stream.readFully(magic);
|
||||
|
||||
return Arrays.equals(DDS.MAGIC, magic);
|
||||
} finally {
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageReader createReaderInstance(Object extension) throws IOException {
|
||||
return new DDSImageReader(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(Locale locale) {
|
||||
return "Direct DrawSurface (DDS) Image Reader";
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.twelvemonkeys.imageio.plugins.dds;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
|
||||
final class DDSProviderInfo extends ReaderWriterProviderInfo {
|
||||
DDSProviderInfo() {
|
||||
super(
|
||||
DDSProviderInfo.class,
|
||||
new String[]{"DDS", "dds"},
|
||||
new String[]{"dds"},
|
||||
new String[]{"image/vnd-ms.dds"},
|
||||
"com.twelvemonkeys.imageio.plugins.dds.DDSImageReader",
|
||||
new String[]{"com.twelvemonkeys.imageio.plugins.dds.DDSImageReaderSpi"},
|
||||
null,
|
||||
null,
|
||||
false, null, null, null, null,
|
||||
true, null, null, null, null
|
||||
);
|
||||
}
|
||||
}
|
||||
+558
@@ -0,0 +1,558 @@
|
||||
/**
|
||||
* DDSReader.java
|
||||
* <p>
|
||||
* Copyright (c) 2015 Kenji Sasaki
|
||||
* Released under the MIT license.
|
||||
* https://github.com/npedotnet/DDSReader/blob/master/LICENSE
|
||||
* <p>
|
||||
* English document
|
||||
* https://github.com/npedotnet/DDSReader/blob/master/README.md
|
||||
* <p>
|
||||
* Japanese document
|
||||
* http://3dtech.jp/wiki/index.php?DDSReader
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.dds;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
final class DDSReader {
|
||||
|
||||
public static final Order order = new Order(16, 8, 0, 24);
|
||||
|
||||
private final DDSHeader header;
|
||||
|
||||
|
||||
public DDSReader(DDSHeader header) {
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
public int[] read(ImageInputStream imageInput, int imageIndex) throws IOException {
|
||||
|
||||
// type
|
||||
DDSType type = getType();
|
||||
|
||||
// offset buffer to index mipmap image
|
||||
byte[] buffer = null;
|
||||
for (int i = 0; i <= imageIndex; i++) {
|
||||
int len = getLength(type, i);
|
||||
buffer = new byte[len];
|
||||
imageInput.readFully(buffer);
|
||||
}
|
||||
|
||||
int width = header.getWidth(imageIndex);
|
||||
int height = header.getHeight(imageIndex);
|
||||
|
||||
switch (type) {
|
||||
case DXT1:
|
||||
return decodeDXT1(width, height, buffer);
|
||||
case DXT2:
|
||||
return decodeDXT2(width, height, buffer);
|
||||
case DXT3:
|
||||
return decodeDXT3(width, height, buffer);
|
||||
case DXT4:
|
||||
return decodeDXT4(width, height, buffer);
|
||||
case DXT5:
|
||||
return decodeDXT5(width, height, buffer);
|
||||
case A1R5G5B5:
|
||||
return readA1R5G5B5(width, height, buffer);
|
||||
case X1R5G5B5:
|
||||
return readX1R5G5B5(width, height, buffer);
|
||||
case A4R4G4B4:
|
||||
return readA4R4G4B4(width, height, buffer);
|
||||
case X4R4G4B4:
|
||||
return readX4R4G4B4(width, height, buffer);
|
||||
case R5G6B5:
|
||||
return readR5G6B5(width, height, buffer);
|
||||
case R8G8B8:
|
||||
return readR8G8B8(width, height, buffer);
|
||||
case A8B8G8R8:
|
||||
return readA8B8G8R8(width, height, buffer);
|
||||
case X8B8G8R8:
|
||||
return readX8B8G8R8(width, height, buffer);
|
||||
case A8R8G8B8:
|
||||
return readA8R8G8B8(width, height, buffer);
|
||||
case X8R8G8B8:
|
||||
return readX8R8G8B8(width, height, buffer);
|
||||
default:
|
||||
throw new IIOException("Unsupported type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
private DDSType getType() throws IIOException {
|
||||
int flags = header.getPixelFormatFlags();
|
||||
|
||||
if ((flags & 0x04) != 0) {
|
||||
// DXT
|
||||
int type = header.getFourCC();
|
||||
return DDSType.parse(type);
|
||||
} else if ((flags & 0x40) != 0) {
|
||||
// RGB
|
||||
int bitCount = header.getBitCount();
|
||||
int redMask = header.getRedMask();
|
||||
int greenMask = header.getGreenMask();
|
||||
int blueMask = header.getBlueMask();
|
||||
int alphaMask = ((flags & 0x01) != 0) ? header.getAlphaMask() : 0; // 0x01 alpha
|
||||
if (bitCount == 16) {
|
||||
if (redMask == A1R5G5B5_MASKS[0] && greenMask == A1R5G5B5_MASKS[1] && blueMask == A1R5G5B5_MASKS[2] && alphaMask == A1R5G5B5_MASKS[3]) {
|
||||
// A1R5G5B5
|
||||
return DDSType.A1R5G5B5;
|
||||
} else if (redMask == X1R5G5B5_MASKS[0] && greenMask == X1R5G5B5_MASKS[1] && blueMask == X1R5G5B5_MASKS[2] && alphaMask == X1R5G5B5_MASKS[3]) {
|
||||
// X1R5G5B5
|
||||
return DDSType.X1R5G5B5;
|
||||
} else if (redMask == A4R4G4B4_MASKS[0] && greenMask == A4R4G4B4_MASKS[1] && blueMask == A4R4G4B4_MASKS[2] && alphaMask == A4R4G4B4_MASKS[3]) {
|
||||
// A4R4G4B4
|
||||
return DDSType.A4R4G4B4;
|
||||
} else if (redMask == X4R4G4B4_MASKS[0] && greenMask == X4R4G4B4_MASKS[1] && blueMask == X4R4G4B4_MASKS[2] && alphaMask == X4R4G4B4_MASKS[3]) {
|
||||
// X4R4G4B4
|
||||
return DDSType.X4R4G4B4;
|
||||
} else if (redMask == R5G6B5_MASKS[0] && greenMask == R5G6B5_MASKS[1] && blueMask == R5G6B5_MASKS[2] && alphaMask == R5G6B5_MASKS[3]) {
|
||||
// R5G6B5
|
||||
return DDSType.R5G6B5;
|
||||
} else {
|
||||
throw new IIOException("Unsupported 16bit RGB image.");
|
||||
}
|
||||
} else if (bitCount == 24) {
|
||||
if (redMask == R8G8B8_MASKS[0] && greenMask == R8G8B8_MASKS[1] && blueMask == R8G8B8_MASKS[2] && alphaMask == R8G8B8_MASKS[3]) {
|
||||
// R8G8B8
|
||||
return DDSType.R8G8B8;
|
||||
} else {
|
||||
throw new IIOException("Unsupported 24bit RGB image.");
|
||||
}
|
||||
} else if (bitCount == 32) {
|
||||
if (redMask == A8B8G8R8_MASKS[0] && greenMask == A8B8G8R8_MASKS[1] && blueMask == A8B8G8R8_MASKS[2] && alphaMask == A8B8G8R8_MASKS[3]) {
|
||||
// A8B8G8R8
|
||||
return DDSType.A8B8G8R8;
|
||||
} else if (redMask == X8B8G8R8_MASKS[0] && greenMask == X8B8G8R8_MASKS[1] && blueMask == X8B8G8R8_MASKS[2] && alphaMask == X8B8G8R8_MASKS[3]) {
|
||||
// X8B8G8R8
|
||||
return DDSType.X8B8G8R8;
|
||||
} else if (redMask == A8R8G8B8_MASKS[0] && greenMask == A8R8G8B8_MASKS[1] && blueMask == A8R8G8B8_MASKS[2] && alphaMask == A8R8G8B8_MASKS[3]) {
|
||||
// A8R8G8B8
|
||||
return DDSType.A8R8G8B8;
|
||||
} else if (redMask == X8R8G8B8_MASKS[0] && greenMask == X8R8G8B8_MASKS[1] && blueMask == X8R8G8B8_MASKS[2] && alphaMask == X8R8G8B8_MASKS[3]) {
|
||||
// X8R8G8B8
|
||||
return DDSType.X8R8G8B8;
|
||||
} else {
|
||||
throw new IIOException("Unsupported 32bit RGB image.");
|
||||
}
|
||||
} else {
|
||||
throw new IIOException("Unsupported bit count: " + bitCount);
|
||||
}
|
||||
} else {
|
||||
throw new IIOException("Unsupported YUV or LUMINANCE image.");
|
||||
}
|
||||
}
|
||||
|
||||
private int getLength(DDSType type, int imageIndex) throws IIOException {
|
||||
int width = header.getWidth(imageIndex);
|
||||
int height = header.getHeight(imageIndex);
|
||||
|
||||
switch (type) {
|
||||
case DXT1:
|
||||
return 8 * ((width + 3) / 4) * ((height + 3) / 4);
|
||||
case DXT2:
|
||||
case DXT3:
|
||||
case DXT4:
|
||||
case DXT5:
|
||||
return 16 * ((width + 3) / 4) * ((height + 3) / 4);
|
||||
case A1R5G5B5:
|
||||
case X1R5G5B5:
|
||||
case A4R4G4B4:
|
||||
case X4R4G4B4:
|
||||
case R5G6B5:
|
||||
case R8G8B8:
|
||||
case A8B8G8R8:
|
||||
case X8B8G8R8:
|
||||
case A8R8G8B8:
|
||||
case X8R8G8B8:
|
||||
return (type.value() & 0xFF) * width * height;
|
||||
default:
|
||||
throw new IIOException("Unknown type: " + Integer.toHexString(type.value()));
|
||||
}
|
||||
}
|
||||
|
||||
private static int[] decodeDXT1(int width, int height, byte[] buffer) {
|
||||
int[] pixels = new int[width * height];
|
||||
int index = 0;
|
||||
int w = (width + 3) / 4;
|
||||
int h = (height + 3) / 4;
|
||||
for (int i = 0; i < h; i++) {
|
||||
for (int j = 0; j < w; j++) {
|
||||
int c0 = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8;
|
||||
index += 2;
|
||||
int c1 = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8;
|
||||
index += 2;
|
||||
for (int k = 0; k < 4; k++) {
|
||||
if (4 * i + k >= height) break;
|
||||
int t0 = (buffer[index] & 0x03);
|
||||
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 + 0] = 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;
|
||||
pixels[4 * width * i + 4 * j + width * k + 2] = getDXTColor(c0, c1, 0xFF, t2);
|
||||
if (4 * j + 3 >= width) continue;
|
||||
pixels[4 * width * i + 4 * j + width * k + 3] = getDXTColor(c0, c1, 0xFF, t3);
|
||||
}
|
||||
}
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
private static int[] decodeDXT2(int width, int height, byte[] buffer) {
|
||||
return decodeDXT3(width, height, buffer);
|
||||
}
|
||||
|
||||
private static int[] decodeDXT3(int width, int height, byte[] buffer) {
|
||||
int index = 0;
|
||||
int w = (width + 3) / 4;
|
||||
int h = (height + 3) / 4;
|
||||
int[] pixels = new int[width * height];
|
||||
int[] alphaTable = new int[16];
|
||||
for (int i = 0; i < h; i++) {
|
||||
for (int j = 0; j < w; j++) {
|
||||
// create alpha table(4bit to 8bit)
|
||||
for (int k = 0; k < 4; k++) {
|
||||
int a0 = (buffer[index++] & 0xFF);
|
||||
int a1 = (buffer[index++] & 0xFF);
|
||||
// 4bit alpha to 8bit alpha
|
||||
alphaTable[4 * k + 0] = 17 * ((a0 & 0xF0) >> 4);
|
||||
alphaTable[4 * k + 1] = 17 * (a0 & 0x0F);
|
||||
alphaTable[4 * k + 2] = 17 * ((a1 & 0xF0) >> 4);
|
||||
alphaTable[4 * k + 3] = 17 * (a1 & 0x0F);
|
||||
}
|
||||
int c0 = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8;
|
||||
index += 2;
|
||||
int c1 = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8;
|
||||
index += 2;
|
||||
for (int k = 0; k < 4; k++) {
|
||||
if (4 * i + k >= height) break;
|
||||
int t0 = (buffer[index] & 0x03);
|
||||
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 + 0] = getDXTColor(c0, c1, alphaTable[4 * k + 0], 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;
|
||||
pixels[4 * width * i + 4 * j + width * k + 2] = getDXTColor(c0, c1, alphaTable[4 * k + 2], t2);
|
||||
if (4 * j + 3 >= width) continue;
|
||||
pixels[4 * width * i + 4 * j + width * k + 3] = getDXTColor(c0, c1, alphaTable[4 * k + 3], t3);
|
||||
}
|
||||
}
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
private static int[] decodeDXT4(int width, int height, byte[] buffer) {
|
||||
return decodeDXT5(width, height, buffer);
|
||||
}
|
||||
|
||||
private static int[] decodeDXT5(int width, int height, byte[] buffer) {
|
||||
int index = 0;
|
||||
int w = (width + 3) / 4;
|
||||
int h = (height + 3) / 4;
|
||||
int[] pixels = new int[width * height];
|
||||
int[] alphaTable = new int[16];
|
||||
for (int i = 0; i < h; i++) {
|
||||
for (int j = 0; j < w; j++) {
|
||||
// create alpha table
|
||||
int a0 = (buffer[index++] & 0xFF);
|
||||
int a1 = (buffer[index++] & 0xFF);
|
||||
int b0 = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8 | (buffer[index + 2] & 0xFF) << 16;
|
||||
index += 3;
|
||||
int b1 = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8 | (buffer[index + 2] & 0xFF) << 16;
|
||||
index += 3;
|
||||
alphaTable[0] = b0 & 0x07;
|
||||
alphaTable[1] = (b0 >> 3) & 0x07;
|
||||
alphaTable[2] = (b0 >> 6) & 0x07;
|
||||
alphaTable[3] = (b0 >> 9) & 0x07;
|
||||
alphaTable[4] = (b0 >> 12) & 0x07;
|
||||
alphaTable[5] = (b0 >> 15) & 0x07;
|
||||
alphaTable[6] = (b0 >> 18) & 0x07;
|
||||
alphaTable[7] = (b0 >> 21) & 0x07;
|
||||
alphaTable[8] = b1 & 0x07;
|
||||
alphaTable[9] = (b1 >> 3) & 0x07;
|
||||
alphaTable[10] = (b1 >> 6) & 0x07;
|
||||
alphaTable[11] = (b1 >> 9) & 0x07;
|
||||
alphaTable[12] = (b1 >> 12) & 0x07;
|
||||
alphaTable[13] = (b1 >> 15) & 0x07;
|
||||
alphaTable[14] = (b1 >> 18) & 0x07;
|
||||
alphaTable[15] = (b1 >> 21) & 0x07;
|
||||
int c0 = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8;
|
||||
index += 2;
|
||||
int c1 = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8;
|
||||
index += 2;
|
||||
for (int k = 0; k < 4; k++) {
|
||||
if (4 * i + k >= height) break;
|
||||
int t0 = (buffer[index] & 0x03);
|
||||
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 + 0] = getDXTColor(c0, c1, getDXT5Alpha(a0, a1, alphaTable[4 * k + 0]), 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;
|
||||
pixels[4 * width * i + 4 * j + width * k + 2] = getDXTColor(c0, c1, getDXT5Alpha(a0, a1, alphaTable[4 * k + 2]), t2);
|
||||
if (4 * j + 3 >= width) continue;
|
||||
pixels[4 * width * i + 4 * j + width * k + 3] = getDXTColor(c0, c1, getDXT5Alpha(a0, a1, alphaTable[4 * k + 3]), t3);
|
||||
}
|
||||
}
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
private static int[] readA1R5G5B5(int width, int height, byte[] buffer) {
|
||||
int index = 0;
|
||||
int[] pixels = new int[width * height];
|
||||
for (int i = 0; i < height * width; i++) {
|
||||
int rgba = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8;
|
||||
index += 2;
|
||||
int r = BIT5[(rgba & A1R5G5B5_MASKS[0]) >> 10];
|
||||
int g = BIT5[(rgba & A1R5G5B5_MASKS[1]) >> 5];
|
||||
int b = BIT5[(rgba & A1R5G5B5_MASKS[2])];
|
||||
int a = 255 * ((rgba & A1R5G5B5_MASKS[3]) >> 15);
|
||||
pixels[i] = (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
private static int[] readX1R5G5B5(int width, int height, byte[] buffer) {
|
||||
int index = 0;
|
||||
int[] pixels = new int[width * height];
|
||||
for (int i = 0; i < height * width; i++) {
|
||||
int rgba = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8;
|
||||
index += 2;
|
||||
int r = BIT5[(rgba & X1R5G5B5_MASKS[0]) >> 10];
|
||||
int g = BIT5[(rgba & X1R5G5B5_MASKS[1]) >> 5];
|
||||
int b = BIT5[(rgba & X1R5G5B5_MASKS[2])];
|
||||
int a = 255;
|
||||
pixels[i] = (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
private static int[] readA4R4G4B4(int width, int height, byte[] buffer) {
|
||||
int index = 0;
|
||||
int[] pixels = new int[width * height];
|
||||
for (int i = 0; i < height * width; i++) {
|
||||
int rgba = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8;
|
||||
index += 2;
|
||||
int r = 17 * ((rgba & A4R4G4B4_MASKS[0]) >> 8);
|
||||
int g = 17 * ((rgba & A4R4G4B4_MASKS[1]) >> 4);
|
||||
int b = 17 * ((rgba & A4R4G4B4_MASKS[2]));
|
||||
int a = 17 * ((rgba & A4R4G4B4_MASKS[3]) >> 12);
|
||||
pixels[i] = (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
private static int[] readX4R4G4B4(int width, int height, byte[] buffer) {
|
||||
int index = 0;
|
||||
int[] pixels = new int[width * height];
|
||||
for (int i = 0; i < height * width; i++) {
|
||||
int rgba = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8;
|
||||
index += 2;
|
||||
int r = 17 * ((rgba & A4R4G4B4_MASKS[0]) >> 8);
|
||||
int g = 17 * ((rgba & A4R4G4B4_MASKS[1]) >> 4);
|
||||
int b = 17 * ((rgba & A4R4G4B4_MASKS[2]));
|
||||
int a = 255;
|
||||
pixels[i] = (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
private static int[] readR5G6B5(int width, int height, byte[] buffer) {
|
||||
int index = 0;
|
||||
int[] pixels = new int[width * height];
|
||||
for (int i = 0; i < height * width; i++) {
|
||||
int rgba = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8;
|
||||
index += 2;
|
||||
int r = BIT5[((rgba & R5G6B5_MASKS[0]) >> 11)];
|
||||
int g = BIT6[((rgba & R5G6B5_MASKS[1]) >> 5)];
|
||||
int b = BIT5[((rgba & R5G6B5_MASKS[2]))];
|
||||
int a = 255;
|
||||
pixels[i] = (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
private static int[] readR8G8B8(int width, int height, byte[] buffer) {
|
||||
int index = 0;
|
||||
int[] pixels = new int[width * height];
|
||||
for (int i = 0; i < height * width; i++) {
|
||||
int b = buffer[index++] & 0xFF;
|
||||
int g = buffer[index++] & 0xFF;
|
||||
int r = buffer[index++] & 0xFF;
|
||||
int a = 255;
|
||||
pixels[i] = (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
private static int[] readA8B8G8R8(int width, int height, byte[] buffer) {
|
||||
int index = 0;
|
||||
int[] pixels = new int[width * height];
|
||||
for (int i = 0; i < height * width; i++) {
|
||||
int r = buffer[index++] & 0xFF;
|
||||
int g = buffer[index++] & 0xFF;
|
||||
int b = buffer[index++] & 0xFF;
|
||||
int a = buffer[index++] & 0xFF;
|
||||
pixels[i] = (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
private static int[] readX8B8G8R8(int width, int height, byte[] buffer) {
|
||||
int index = 0;
|
||||
int[] pixels = new int[width * height];
|
||||
for (int i = 0; i < height * width; i++) {
|
||||
int r = buffer[index++] & 0xFF;
|
||||
int g = buffer[index++] & 0xFF;
|
||||
int b = buffer[index++] & 0xFF;
|
||||
int a = 255;
|
||||
index++;
|
||||
pixels[i] = (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
private static int[] readA8R8G8B8(int width, int height, byte[] buffer) {
|
||||
int index = 0;
|
||||
int[] pixels = new int[width * height];
|
||||
for (int i = 0; i < height * width; i++) {
|
||||
int b = buffer[index++] & 0xFF;
|
||||
int g = buffer[index++] & 0xFF;
|
||||
int r = buffer[index++] & 0xFF;
|
||||
int a = buffer[index++] & 0xFF;
|
||||
pixels[i] = (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
private static int[] readX8R8G8B8(int width, int height, byte[] buffer) {
|
||||
int index = 0;
|
||||
int[] pixels = new int[width * height];
|
||||
for (int i = 0; i < height * width; i++) {
|
||||
int b = buffer[index++] & 0xFF;
|
||||
int g = buffer[index++] & 0xFF;
|
||||
int r = buffer[index++] & 0xFF;
|
||||
int a = 255;
|
||||
index++;
|
||||
pixels[i] = (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
private static int getDXTColor(int c0, int c1, int a, int t) {
|
||||
switch (t) {
|
||||
case 0:
|
||||
return getDXTColor1(c0, a);
|
||||
case 1:
|
||||
return getDXTColor1(c1, a);
|
||||
case 2:
|
||||
return (c0 > c1) ? getDXTColor2_1(c0, c1, a) : getDXTColor1_1(c0, c1, a);
|
||||
case 3:
|
||||
return (c0 > c1) ? getDXTColor2_1(c1, c0, a) : 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int getDXTColor2_1(int c0, int c1, int a) {
|
||||
// 2*c0/3 + c1/3
|
||||
int r = (2 * BIT5[(c0 & 0xFC00) >> 11] + BIT5[(c1 & 0xFC00) >> 11]) / 3;
|
||||
int g = (2 * BIT6[(c0 & 0x07E0) >> 5] + BIT6[(c1 & 0x07E0) >> 5]) / 3;
|
||||
int b = (2 * BIT5[c0 & 0x001F] + BIT5[c1 & 0x001F]) / 3;
|
||||
return (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
|
||||
}
|
||||
|
||||
private static int getDXTColor1_1(int c0, int c1, int a) {
|
||||
// (c0+c1) / 2
|
||||
int r = (BIT5[(c0 & 0xFC00) >> 11] + BIT5[(c1 & 0xFC00) >> 11]) / 2;
|
||||
int g = (BIT6[(c0 & 0x07E0) >> 5] + BIT6[(c1 & 0x07E0) >> 5]) / 2;
|
||||
int b = (BIT5[c0 & 0x001F] + BIT5[c1 & 0x001F]) / 2;
|
||||
return (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
|
||||
}
|
||||
|
||||
private static int getDXTColor1(int c, int a) {
|
||||
int r = BIT5[(c & 0xFC00) >> 11];
|
||||
int g = BIT6[(c & 0x07E0) >> 5];
|
||||
int b = BIT5[(c & 0x001F)];
|
||||
return (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
|
||||
}
|
||||
|
||||
private static int getDXT5Alpha(int a0, int a1, int t) {
|
||||
if (a0 > a1) switch (t) {
|
||||
case 0:
|
||||
return a0;
|
||||
case 1:
|
||||
return a1;
|
||||
case 2:
|
||||
return (6 * a0 + a1) / 7;
|
||||
case 3:
|
||||
return (5 * a0 + 2 * a1) / 7;
|
||||
case 4:
|
||||
return (4 * a0 + 3 * a1) / 7;
|
||||
case 5:
|
||||
return (3 * a0 + 4 * a1) / 7;
|
||||
case 6:
|
||||
return (2 * a0 + 5 * a1) / 7;
|
||||
case 7:
|
||||
return (a0 + 6 * a1) / 7;
|
||||
}
|
||||
else switch (t) {
|
||||
case 0:
|
||||
return a0;
|
||||
case 1:
|
||||
return a1;
|
||||
case 2:
|
||||
return (4 * a0 + a1) / 5;
|
||||
case 3:
|
||||
return (3 * a0 + 2 * a1) / 5;
|
||||
case 4:
|
||||
return (2 * a0 + 3 * a1) / 5;
|
||||
case 5:
|
||||
return (a0 + 4 * a1) / 5;
|
||||
case 6:
|
||||
return 0;
|
||||
case 7:
|
||||
return 255;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// RGBA Masks
|
||||
private static final int[] A1R5G5B5_MASKS = {0x7C00, 0x03E0, 0x001F, 0x8000};
|
||||
private static final int[] X1R5G5B5_MASKS = {0x7C00, 0x03E0, 0x001F, 0x0000};
|
||||
private static final int[] A4R4G4B4_MASKS = {0x0F00, 0x00F0, 0x000F, 0xF000};
|
||||
private static final int[] X4R4G4B4_MASKS = {0x0F00, 0x00F0, 0x000F, 0x0000};
|
||||
private static final int[] R5G6B5_MASKS = {0xF800, 0x07E0, 0x001F, 0x0000};
|
||||
private static final int[] R8G8B8_MASKS = {0xFF0000, 0x00FF00, 0x0000FF, 0x000000};
|
||||
private static final int[] A8B8G8R8_MASKS = {0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000};
|
||||
private static final int[] X8B8G8R8_MASKS = {0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000};
|
||||
private static final int[] A8R8G8B8_MASKS = {0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000};
|
||||
private static final int[] X8R8G8B8_MASKS = {0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000};
|
||||
|
||||
// BIT4 = 17 * index;
|
||||
private static final int[] BIT5 = {0, 8, 16, 25, 33, 41, 49, 58, 66, 74, 82, 90, 99, 107, 115, 123, 132, 140, 148, 156, 165, 173, 181, 189, 197, 206, 214, 222, 230, 239, 247, 255};
|
||||
private static final int[] BIT6 = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 130, 134, 138, 142, 146, 150, 154, 158, 162, 166, 170, 174, 178, 182, 186, 190, 194, 198, 202, 206, 210, 215, 219, 223, 227, 231, 235, 239, 243, 247, 251, 255};
|
||||
|
||||
private static final class Order {
|
||||
Order(int redShift, int greenShift, int blueShift, int alphaShift) {
|
||||
this.redShift = redShift;
|
||||
this.greenShift = greenShift;
|
||||
this.blueShift = blueShift;
|
||||
this.alphaShift = alphaShift;
|
||||
}
|
||||
|
||||
public int redShift;
|
||||
public int greenShift;
|
||||
public int blueShift;
|
||||
public int alphaShift;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.twelvemonkeys.imageio.plugins.dds;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
|
||||
enum DDSType {
|
||||
|
||||
DXT1(0x31545844),
|
||||
DXT2(0x32545844),
|
||||
DXT3(0x33545844),
|
||||
DXT4(0x34545844),
|
||||
DXT5(0x35545844),
|
||||
A1R5G5B5((1 << 16) | 2),
|
||||
X1R5G5B5((2 << 16) | 2),
|
||||
A4R4G4B4((3 << 16) | 2),
|
||||
X4R4G4B4((4 << 16) | 2),
|
||||
R5G6B5((5 << 16) | 2),
|
||||
R8G8B8((1 << 16) | 3),
|
||||
A8B8G8R8((1 << 16) | 4),
|
||||
X8B8G8R8((2 << 16) | 4),
|
||||
A8R8G8B8((3 << 16) | 4),
|
||||
X8R8G8B8((4 << 16) | 4);
|
||||
|
||||
private final int value;
|
||||
|
||||
DDSType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static DDSType parse(int type) throws IIOException {
|
||||
for (DDSType t : DDSType.values()) {
|
||||
if (type == t.value()) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
throw new IIOException("Unknown type: " + Integer.toHexString(type));
|
||||
}
|
||||
}
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
com.twelvemonkeys.imageio.plugins.dds.DDSImageReaderSpi
|
||||
@@ -0,0 +1,141 @@
|
||||
import com.twelvemonkeys.imageio.plugins.dds.DDSImageReader;
|
||||
import com.twelvemonkeys.imageio.plugins.dds.DDSImageReaderSpi;
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.Dimension;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class DDSImageTeaderTest extends ImageReaderAbstractTest<DDSImageReader> {
|
||||
@Override
|
||||
protected ImageReaderSpi createProvider() {
|
||||
return new DDSImageReaderSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestData> getTestData() {
|
||||
Dimension dim = new Dimension(256, 256);
|
||||
|
||||
List<TestData> testData = new ArrayList<>();
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_A1R5G5B5.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_A1R5G5B5_mipmap.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_A4R4G4B4.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_A4R4G4B4_mipmap.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_A8B8G8R8.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_A8B8G8R8_mipmap.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_A8R8G8B8.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_A8R8G8B8_mipmap.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_DXT1.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_DXT1_mipmap.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_DXT2.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_DXT2_mipmap.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_DXT3.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_DXT3_mipmap.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_DXT4.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_DXT4_mipmap.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_DXT5.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_DXT5_mipmap.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_R5G6B5.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_R5G6B5_mipmap.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_R8G8B8.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_R8G8B8_mipmap.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_X1R5G5B5.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_X1R5G5B5_mipmap.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_X4R4G4B4.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_X4R4G4B4_mipmap.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_X8B8G8R8.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_X8B8G8R8_mipmap.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_X8R8G8B8.dds"), dim));
|
||||
testData.add(new TestData(getClassLoaderResource("/dds/dds_X8R8G8B8_mipmap.dds"), dim));
|
||||
|
||||
return testData;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList("DDS", "dds");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSuffixes() {
|
||||
return Arrays.asList("dds");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getMIMETypes() {
|
||||
return Collections.singletonList("image/vnd-ms.dds");
|
||||
}
|
||||
|
||||
/* ************************************************************************************************************* *
|
||||
* IGNORE Broken Tests...
|
||||
* ************************************************************************************************************* */
|
||||
|
||||
@Ignore("Known issue: currently not supported in DDS")
|
||||
@Test
|
||||
@Override
|
||||
public void testGetNumImagesNoInput() throws IOException {
|
||||
super.testGetNumImagesNoInput();
|
||||
}
|
||||
|
||||
@Ignore("Known issue: currently not supported in DDS")
|
||||
@Test
|
||||
@Override
|
||||
public void testAffineTransformOpCompatibility() throws IOException {
|
||||
super.testAffineTransformOpCompatibility();
|
||||
}
|
||||
|
||||
@Ignore("Known issue: currently not supported in DDS")
|
||||
@Test
|
||||
@Override
|
||||
public void testReadWithSourceRegionParam() throws IOException {
|
||||
super.testReadWithSourceRegionParam();
|
||||
}
|
||||
|
||||
@Ignore("Known issue: currently not supported in DDS")
|
||||
@Test
|
||||
@Override
|
||||
public void testReadWithSourceRegionParamEqualImage() throws IOException {
|
||||
super.testReadWithSourceRegionParamEqualImage();
|
||||
}
|
||||
|
||||
@Ignore("Known issue: currently not supported in DDS")
|
||||
@Test
|
||||
@Override
|
||||
public void testReadWithSubsampleAndSourceRegionParam() throws IOException {
|
||||
super.testReadWithSubsampleAndSourceRegionParam();
|
||||
}
|
||||
|
||||
@Ignore("Known issue: currently not supported in DDS")
|
||||
@Test
|
||||
@Override
|
||||
public void testReadWithSubsampleParamDimensions() throws IOException {
|
||||
super.testReadWithSubsampleParamDimensions();
|
||||
}
|
||||
|
||||
@Ignore("Known issue: currently not supported in DDS")
|
||||
@Test
|
||||
@Override
|
||||
public void testReadWithSubsampleParamPixels() throws IOException {
|
||||
super.testReadWithSubsampleParamPixels();
|
||||
}
|
||||
|
||||
@Ignore("Known issue: currently not supported in DDS")
|
||||
@Test
|
||||
@Override
|
||||
public void testSetDestination() throws IOException {
|
||||
super.testSetDestination();
|
||||
}
|
||||
|
||||
@Ignore("Known issue: currently not supported in DDS")
|
||||
@Test
|
||||
@Override
|
||||
public void testSetDestinationIllegal() throws IOException {
|
||||
super.testSetDestinationIllegal();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -33,6 +33,7 @@
|
||||
<!-- Stand-alone readers/writers -->
|
||||
<module>imageio-bmp</module>
|
||||
<module>imageio-hdr</module>
|
||||
<module>imageio-dds</module>
|
||||
<module>imageio-icns</module>
|
||||
<module>imageio-iff</module>
|
||||
<module>imageio-jpeg</module>
|
||||
|
||||
Reference in New Issue
Block a user