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.
This commit is contained in:
Paul Allen
2024-09-13 15:51:00 +01:00
parent 297a27c5be
commit 82bf4851ab
12 changed files with 955 additions and 0 deletions

View File

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

View 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>

View File

@@ -0,0 +1,5 @@
package com.twelvemonkeys.imageio.plugins.dds;
interface DDS {
byte[] MAGIC = new byte[] {'D', 'D', 'S', ' '};
}

View File

@@ -0,0 +1,61 @@
package com.twelvemonkeys.imageio.plugins.dds;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.nio.ByteOrder;
final class DDSHeader {
// https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-pguide
private int width;
private int height;
public static DDSHeader read(final ImageInputStream imageInput) throws IOException {
DDSHeader header = new DDSHeader();
imageInput.mark();
imageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
byte[] magic = new byte[4];
imageInput.readFully(magic);
// DDS_HEADER structure
int dwSize = imageInput.readInt();
int dwFlags = imageInput.readInt();
header.height = imageInput.readInt();
header.width = imageInput.readInt();
int dwPitchOrLinearSize = imageInput.readInt();
int dwDepth = imageInput.readInt();
int dwMipMapCount = imageInput.readInt();
byte[] dwReserved1 = new byte[11];
imageInput.readFully(dwReserved1);
// DDS_PIXELFORMAT structure
int px_dwSize = imageInput.readInt();
int px_dwFlags = imageInput.readInt();
int px_dwFourCC = imageInput.readInt();
int px_dwRGBBitCount = imageInput.readInt();
int px_dwRBitMask = imageInput.readInt();
int px_dwGBitMask = imageInput.readInt();
int px_dwBBitMask = imageInput.readInt();
int px_dwABitMask = imageInput.readInt();
int dwCaps = imageInput.readInt();
int dwCaps2 = imageInput.readInt();
int dwCaps3 = imageInput.readInt();
int dwCaps4 = imageInput.readInt();
int dwReserved2 = imageInput.readInt();
imageInput.reset();
return header;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
}

View File

@@ -0,0 +1,97 @@
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.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Iterator;
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();
}
@Override
public int getHeight(int imageIndex) throws IOException {
checkBounds(imageIndex);
readHeader();
return header.getHeight();
}
@Override
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
checkBounds(imageIndex);
readHeader();
// TODO changes based on format
ColorSpace sRGB = ColorSpace.getInstance(ColorSpace.CS_sRGB);
return Collections.singletonList(ImageTypeSpecifiers.createInterleaved(sRGB, new int[]{0, 1, 2}, DataBuffer.TYPE_FLOAT, false, false)).iterator();
}
@Override
public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
checkBounds(imageIndex);
readHeader();
int width = getWidth(imageIndex);
int height = getHeight(imageIndex);
BufferedImage destination = getDestination(param, getImageTypes(imageIndex), width, height);
// TODO
byte[] buffer = new byte[width * height * 4];
imageInput.read(buffer);
int[] pixels = DDSReader.read(buffer, DDSReader.ARGB, 0);
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
image.setRGB(0, 0, width, height, pixels, 0, width);
processImageComplete();
return image;
}
private void readHeader() throws IOException {
if (header == null) {
header = DDSHeader.read(imageInput);
imageInput.flushBefore(imageInput.getStreamPosition());
}
imageInput.seek(imageInput.getFlushedPosition());
}
public static void main(final String[] args) throws IOException {
File file = new File("imageio/imageio-dds/src/test/resources/dds/dxt5.dds");
BufferedImage image = ImageIO.read(file);
showIt(image, file.getName());
}
}

View File

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

View File

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

View File

@@ -0,0 +1,629 @@
/**
* DDSReader.java
*
* Copyright (c) 2015 Kenji Sasaki
* Released under the MIT license.
* https://github.com/npedotnet/DDSReader/blob/master/LICENSE
*
* English document
* https://github.com/npedotnet/DDSReader/blob/master/README.md
*
* Japanese document
* http://3dtech.jp/wiki/index.php?DDSReader
*
*/
package com.twelvemonkeys.imageio.plugins.dds;
public final class DDSReader {
public static final Order ARGB = new Order(16, 8, 0, 24);
public static final Order ABGR = new Order(0, 8, 16, 24);
public static int getHeight(byte[] buffer) {
return (buffer[12] & 0xFF) | (buffer[13] & 0xFF) << 8 | (buffer[14] & 0xFF) << 16 | (buffer[15] & 0xFF) << 24;
}
public static int getWidth(byte[] buffer) {
return (buffer[16] & 0xFF) | (buffer[17] & 0xFF) << 8 | (buffer[18] & 0xFF) << 16 | (buffer[19] & 0xFF) << 24;
}
public static int getMipmap(byte[] buffer) {
return (buffer[28] & 0xFF) | (buffer[29] & 0xFF) << 8 | (buffer[30] & 0xFF) << 16 | (buffer[31] & 0xFF) << 24;
}
public static int getPixelFormatFlags(byte[] buffer) {
return (buffer[80] & 0xFF) | (buffer[81] & 0xFF) << 8 | (buffer[82] & 0xFF) << 16 | (buffer[83] & 0xFF) << 24;
}
public static int getFourCC(byte[] buffer) {
return (buffer[84] & 0xFF) << 24 | (buffer[85] & 0xFF) << 16 | (buffer[86] & 0xFF) << 8 | (buffer[87] & 0xFF);
}
public static int getBitCount(byte[] buffer) {
return (buffer[88] & 0xFF) | (buffer[89] & 0xFF) << 8 | (buffer[90] & 0xFF) << 16 | (buffer[91] & 0xFF) << 24;
}
public static int getRedMask(byte[] buffer) {
return (buffer[92] & 0xFF) | (buffer[93] & 0xFF) << 8 | (buffer[94] & 0xFF) << 16 | (buffer[95] & 0xFF) << 24;
}
public static int getGreenMask(byte[] buffer) {
return (buffer[96] & 0xFF) | (buffer[97] & 0xFF) << 8 | (buffer[98] & 0xFF) << 16 | (buffer[99] & 0xFF) << 24;
}
public static int getBlueMask(byte[] buffer) {
return (buffer[100] & 0xFF) | (buffer[101] & 0xFF) << 8 | (buffer[102] & 0xFF) << 16 | (buffer[103] & 0xFF) << 24;
}
public static int getAlphaMask(byte[] buffer) {
return (buffer[104] & 0xFF) | (buffer[105] & 0xFF) << 8 | (buffer[106] & 0xFF) << 16 | (buffer[107] & 0xFF) << 24;
}
public static int[] read(byte[] buffer, Order order, int mipmapLevel) {
// header
int width = getWidth(buffer);
int height = getHeight(buffer);
int mipmap = getMipmap(buffer);
// type
int type = getType(buffer);
if (type == 0) return null;
// offset
int offset = 128; // header size
if (mipmapLevel > 0 && mipmapLevel < mipmap) {
for (int i = 0; i < mipmapLevel; i++) {
switch (type) {
case DXT1:
offset += 8 * ((width + 3) / 4) * ((height + 3) / 4);
break;
case DXT2:
case DXT3:
case DXT4:
case DXT5:
offset += 16 * ((width + 3) / 4) * ((height + 3) / 4);
break;
case A1R5G5B5:
case X1R5G5B5:
case A4R4G4B4:
case X4R4G4B4:
case R5G6B5:
case R8G8B8:
case A8B8G8R8:
case X8B8G8R8:
case A8R8G8B8:
case X8R8G8B8:
offset += (type & 0xFF) * width * height;
break;
}
width /= 2;
height /= 2;
}
if (width <= 0) width = 1;
if (height <= 0) height = 1;
}
int[] pixels = null;
switch (type) {
case DXT1:
pixels = decodeDXT1(width, height, offset, buffer, order);
break;
case DXT2:
pixels = decodeDXT2(width, height, offset, buffer, order);
break;
case DXT3:
pixels = decodeDXT3(width, height, offset, buffer, order);
break;
case DXT4:
pixels = decodeDXT4(width, height, offset, buffer, order);
break;
case DXT5:
pixels = decodeDXT5(width, height, offset, buffer, order);
break;
case A1R5G5B5:
pixels = readA1R5G5B5(width, height, offset, buffer, order);
break;
case X1R5G5B5:
pixels = readX1R5G5B5(width, height, offset, buffer, order);
break;
case A4R4G4B4:
pixels = readA4R4G4B4(width, height, offset, buffer, order);
break;
case X4R4G4B4:
pixels = readX4R4G4B4(width, height, offset, buffer, order);
break;
case R5G6B5:
pixels = readR5G6B5(width, height, offset, buffer, order);
break;
case R8G8B8:
pixels = readR8G8B8(width, height, offset, buffer, order);
break;
case A8B8G8R8:
pixels = readA8B8G8R8(width, height, offset, buffer, order);
break;
case X8B8G8R8:
pixels = readX8B8G8R8(width, height, offset, buffer, order);
break;
case A8R8G8B8:
pixels = readA8R8G8B8(width, height, offset, buffer, order);
break;
case X8R8G8B8:
pixels = readX8R8G8B8(width, height, offset, buffer, order);
break;
}
return pixels;
}
private static int getType(byte[] buffer) {
int type = 0;
int flags = getPixelFormatFlags(buffer);
if ((flags & 0x04) != 0) {
// DXT
type = getFourCC(buffer);
} else if ((flags & 0x40) != 0) {
// RGB
int bitCount = getBitCount(buffer);
int redMask = getRedMask(buffer);
int greenMask = getGreenMask(buffer);
int blueMask = getBlueMask(buffer);
int alphaMask = ((flags & 0x01) != 0) ? getAlphaMask(buffer) : 0; // 0x01 alpha
if (bitCount == 16) {
if (redMask == A1R5G5B5_MASKS[0] && greenMask == A1R5G5B5_MASKS[1] && blueMask == A1R5G5B5_MASKS[2] && alphaMask == A1R5G5B5_MASKS[3]) {
// A1R5G5B5
type = A1R5G5B5;
} else if (redMask == X1R5G5B5_MASKS[0] && greenMask == X1R5G5B5_MASKS[1] && blueMask == X1R5G5B5_MASKS[2] && alphaMask == X1R5G5B5_MASKS[3]) {
// X1R5G5B5
type = X1R5G5B5;
} else if (redMask == A4R4G4B4_MASKS[0] && greenMask == A4R4G4B4_MASKS[1] && blueMask == A4R4G4B4_MASKS[2] && alphaMask == A4R4G4B4_MASKS[3]) {
// A4R4G4B4
type = A4R4G4B4;
} else if (redMask == X4R4G4B4_MASKS[0] && greenMask == X4R4G4B4_MASKS[1] && blueMask == X4R4G4B4_MASKS[2] && alphaMask == X4R4G4B4_MASKS[3]) {
// X4R4G4B4
type = X4R4G4B4;
} else if (redMask == R5G6B5_MASKS[0] && greenMask == R5G6B5_MASKS[1] && blueMask == R5G6B5_MASKS[2] && alphaMask == R5G6B5_MASKS[3]) {
// R5G6B5
type = R5G6B5;
} else {
// 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
type = R8G8B8;
} else {
// 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
type = A8B8G8R8;
} else if (redMask == X8B8G8R8_MASKS[0] && greenMask == X8B8G8R8_MASKS[1] && blueMask == X8B8G8R8_MASKS[2] && alphaMask == X8B8G8R8_MASKS[3]) {
// X8B8G8R8
type = X8B8G8R8;
} else if (redMask == A8R8G8B8_MASKS[0] && greenMask == A8R8G8B8_MASKS[1] && blueMask == A8R8G8B8_MASKS[2] && alphaMask == A8R8G8B8_MASKS[3]) {
// A8R8G8B8
type = A8R8G8B8;
} else if (redMask == X8R8G8B8_MASKS[0] && greenMask == X8R8G8B8_MASKS[1] && blueMask == X8R8G8B8_MASKS[2] && alphaMask == X8R8G8B8_MASKS[3]) {
// X8R8G8B8
type = X8R8G8B8;
} else {
// Unsupported 32bit RGB image
}
}
} else {
// YUV or LUMINANCE image
}
return type;
}
private static int[] decodeDXT1(int width, int height, int offset, byte[] buffer, Order order) {
int[] pixels = new int[width * height];
int index = offset;
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, order);
if (4 * j + 1 >= width) continue;
pixels[4 * width * i + 4 * j + width * k + 1] = getDXTColor(c0, c1, 0xFF, t1, order);
if (4 * j + 2 >= width) continue;
pixels[4 * width * i + 4 * j + width * k + 2] = getDXTColor(c0, c1, 0xFF, t2, order);
if (4 * j + 3 >= width) continue;
pixels[4 * width * i + 4 * j + width * k + 3] = getDXTColor(c0, c1, 0xFF, t3, order);
}
}
}
return pixels;
}
private static int[] decodeDXT2(int width, int height, int offset, byte[] buffer, Order order) {
return decodeDXT3(width, height, offset, buffer, order);
}
private static int[] decodeDXT3(int width, int height, int offset, byte[] buffer, Order order) {
int index = offset;
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, order);
if (4 * j + 1 >= width) continue;
pixels[4 * width * i + 4 * j + width * k + 1] = getDXTColor(c0, c1, alphaTable[4 * k + 1], t1, order);
if (4 * j + 2 >= width) continue;
pixels[4 * width * i + 4 * j + width * k + 2] = getDXTColor(c0, c1, alphaTable[4 * k + 2], t2, order);
if (4 * j + 3 >= width) continue;
pixels[4 * width * i + 4 * j + width * k + 3] = getDXTColor(c0, c1, alphaTable[4 * k + 3], t3, order);
}
}
}
return pixels;
}
private static int[] decodeDXT4(int width, int height, int offset, byte[] buffer, Order order) {
return decodeDXT5(width, height, offset, buffer, order);
}
private static int[] decodeDXT5(int width, int height, int offset, byte[] buffer, Order order) {
int index = offset;
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, order);
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, order);
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, order);
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, order);
}
}
}
return pixels;
}
private static int[] readA1R5G5B5(int width, int height, int offset, byte[] buffer, Order order) {
int index = offset;
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, int offset, byte[] buffer, Order order) {
int index = offset;
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, int offset, byte[] buffer, Order order) {
int index = offset;
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, int offset, byte[] buffer, Order order) {
int index = offset;
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, int offset, byte[] buffer, Order order) {
int index = offset;
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, int offset, byte[] buffer, Order order) {
int index = offset;
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, int offset, byte[] buffer, Order order) {
int index = offset;
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, int offset, byte[] buffer, Order order) {
int index = offset;
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, int offset, byte[] buffer, Order order) {
int index = offset;
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, int offset, byte[] buffer, Order order) {
int index = offset;
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, Order order) {
switch (t) {
case 0:
return getDXTColor1(c0, a, order);
case 1:
return getDXTColor1(c1, a, order);
case 2:
return (c0 > c1) ? getDXTColor2_1(c0, c1, a, order) : getDXTColor1_1(c0, c1, a, order);
case 3:
return (c0 > c1) ? getDXTColor2_1(c1, c0, a, order) : 0;
}
return 0;
}
private static int getDXTColor2_1(int c0, int c1, int a, Order order) {
// 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, Order order) {
// (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, Order order) {
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;
}
// Image Type
private static final int DXT1 = (0x44585431);
private static final int DXT2 = (0x44585432);
private static final int DXT3 = (0x44585433);
private static final int DXT4 = (0x44585434);
private static final int DXT5 = (0x44585435);
private static final int A1R5G5B5 = ((1 << 16) | 2);
private static final int X1R5G5B5 = ((2 << 16) | 2);
private static final int A4R4G4B4 = ((3 << 16) | 2);
private static final int X4R4G4B4 = ((4 << 16) | 2);
private static final int R5G6B5 = ((5 << 16) | 2);
private static final int R8G8B8 = ((1 << 16) | 3);
private static final int A8B8G8R8 = ((1 << 16) | 4);
private static final int X8B8G8R8 = ((2 << 16) | 4);
private static final int A8R8G8B8 = ((3 << 16) | 4);
private static final int X8R8G8B8 = ((4 << 16) | 4);
// 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 DDSReader() {
}
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;
}
}

View File

@@ -0,0 +1 @@
com.twelvemonkeys.imageio.plugins.dds.DDSImageReaderSpi

View File

@@ -0,0 +1,38 @@
import com.twelvemonkeys.imageio.plugins.dds.DDSImageReader;
import com.twelvemonkeys.imageio.plugins.dds.DDSImageReaderSpi;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.Dimension;
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() {
return Collections.singletonList(
new TestData(getClassLoaderResource("/dds/dxt5.dds"), new Dimension(512, 512))
);
}
@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");
}
}

Binary file not shown.

View File

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