mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-04 20:15:28 -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 71a4e73ca61265e9ad59f7196d39470b069aab5b. * 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:
parent
4fc9c3513b
commit
3c01071452
@ -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>
|
||||
|
52
imageio/imageio-dds/pom.xml
Normal file
52
imageio/imageio-dds/pom.xml
Normal file
@ -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.
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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";
|
||||
}
|
||||
}
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
com.twelvemonkeys.imageio.plugins.dds.DDSImageReaderSpi
|
141
imageio/imageio-dds/src/test/java/DDSImageTeaderTest.java
Normal file
141
imageio/imageio-dds/src/test/java/DDSImageTeaderTest.java
Normal file
@ -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();
|
||||
}
|
||||
}
|
BIN
imageio/imageio-dds/src/test/resources/dds/dds_A1R5G5B5.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dds_A1R5G5B5.dds
Normal file
Binary file not shown.
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dds_A4R4G4B4.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dds_A4R4G4B4.dds
Normal file
Binary file not shown.
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dds_A8B8G8R8.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dds_A8B8G8R8.dds
Normal file
Binary file not shown.
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dds_A8R8G8B8.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dds_A8R8G8B8.dds
Normal file
Binary file not shown.
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dds_DXT1.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dds_DXT1.dds
Normal file
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dds_DXT1_mipmap.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dds_DXT1_mipmap.dds
Normal file
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dds_DXT2.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dds_DXT2.dds
Normal file
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dds_DXT2_mipmap.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dds_DXT2_mipmap.dds
Normal file
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dds_DXT3.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dds_DXT3.dds
Normal file
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dds_DXT3_mipmap.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dds_DXT3_mipmap.dds
Normal file
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dds_DXT4.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dds_DXT4.dds
Normal file
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dds_DXT4_mipmap.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dds_DXT4_mipmap.dds
Normal file
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dds_DXT5.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dds_DXT5.dds
Normal file
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dds_DXT5_mipmap.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dds_DXT5_mipmap.dds
Normal file
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dds_R5G6B5.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dds_R5G6B5.dds
Normal file
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dds_R5G6B5_mipmap.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dds_R5G6B5_mipmap.dds
Normal file
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dds_R8G8B8.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dds_R8G8B8.dds
Normal file
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dds_R8G8B8_mipmap.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dds_R8G8B8_mipmap.dds
Normal file
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dds_X1R5G5B5.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dds_X1R5G5B5.dds
Normal file
Binary file not shown.
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dds_X4R4G4B4.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dds_X4R4G4B4.dds
Normal file
Binary file not shown.
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dds_X8B8G8R8.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dds_X8B8G8R8.dds
Normal file
Binary file not shown.
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dds_X8R8G8B8.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dds_X8R8G8B8.dds
Normal file
Binary file not shown.
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dxt1-noalpha.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dxt1-noalpha.dds
Normal file
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dxt1.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dxt1.dds
Normal file
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dxt3.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dxt3.dds
Normal file
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/dxt5.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/dxt5.dds
Normal file
Binary file not shown.
BIN
imageio/imageio-dds/src/test/resources/dds/stones.dxt5.dds
Normal file
BIN
imageio/imageio-dds/src/test/resources/dds/stones.dxt5.dds
Normal file
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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user