mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-04-30 00:00:01 -04:00
Using new sequence support in DDSImageWriter
+ some minor bonus clean-up
This commit is contained in:
+12
-18
@@ -47,9 +47,12 @@ import java.awt.Dimension;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see <a href="https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dds-header">DDS_HEADER structure</a>
|
||||||
|
* @see <a href="https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-pguide">Programming Guide for DDS</a>
|
||||||
|
*/
|
||||||
final class DDSHeader {
|
final class DDSHeader {
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-pguide
|
|
||||||
private int flags;
|
private int flags;
|
||||||
|
|
||||||
private int mipMapCount;
|
private int mipMapCount;
|
||||||
@@ -69,26 +72,16 @@ final class DDSHeader {
|
|||||||
static DDSHeader read(final ImageInputStream imageInput) throws IOException {
|
static DDSHeader read(final ImageInputStream imageInput) throws IOException {
|
||||||
DDSHeader header = new DDSHeader();
|
DDSHeader header = new DDSHeader();
|
||||||
|
|
||||||
// Read MAGIC bytes [0,3]
|
|
||||||
int magic = imageInput.readInt();
|
|
||||||
if (magic != DDS.MAGIC) {
|
|
||||||
throw new IIOException(String.format("Not a DDS file. Expected DDS magic 0x%8x', read 0x%8x", DDS.MAGIC, magic));
|
|
||||||
}
|
|
||||||
|
|
||||||
// DDS_HEADER structure
|
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dds-header
|
|
||||||
int dwSize = imageInput.readInt(); // [4,7]
|
int dwSize = imageInput.readInt(); // [4,7]
|
||||||
if (dwSize != DDS.HEADER_SIZE) {
|
if (dwSize != DDS.HEADER_SIZE) {
|
||||||
throw new IIOException(String.format("Invalid DDS header size (expected %d): %d", DDS.HEADER_SIZE, dwSize));
|
throw new IIOException(String.format("Invalid DDS header size (expected %d): %d", DDS.HEADER_SIZE, dwSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify setFlags
|
// Verify flags
|
||||||
header.flags = imageInput.readInt(); // [8,11]
|
header.flags = imageInput.readInt(); // [8,11]
|
||||||
if (!header.getFlag(DDS.FLAG_CAPS
|
if (!header.hasFlag(DDS.FLAG_CAPS | DDS.FLAG_HEIGHT | DDS.FLAG_WIDTH | DDS.FLAG_PIXELFORMAT)) {
|
||||||
| DDS.FLAG_HEIGHT
|
// NOTE: The Microsoft DDS documentation mention that readers should not rely on these flags...
|
||||||
| DDS.FLAG_WIDTH
|
throw new IIOException("Required DDS flag missing in header: " + Integer.toBinaryString(header.flags));
|
||||||
| DDS.FLAG_PIXELFORMAT)) {
|
|
||||||
throw new IIOException("Required DDS Flag missing in header: " + Integer.toBinaryString(header.flags));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read Height & Width
|
// Read Height & Width
|
||||||
@@ -109,7 +102,7 @@ final class DDSHeader {
|
|||||||
// DDS_PIXELFORMAT structure
|
// DDS_PIXELFORMAT structure
|
||||||
int px_dwSize = imageInput.readInt(); // [76,79]
|
int px_dwSize = imageInput.readInt(); // [76,79]
|
||||||
if (px_dwSize != DDS.PIXELFORMAT_SIZE) {
|
if (px_dwSize != DDS.PIXELFORMAT_SIZE) {
|
||||||
throw new IIOException(String.format("Invalid DDS PIXELFORMAT size (expected %d): %d", DDS.PIXELFORMAT_SIZE, dwSize));
|
throw new IIOException(String.format("Invalid DDS pixel format structure size (expected %d): %d", DDS.PIXELFORMAT_SIZE, dwSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
header.pixelFormatFlags = imageInput.readInt(); // [80,83]
|
header.pixelFormatFlags = imageInput.readInt(); // [80,83]
|
||||||
@@ -128,6 +121,7 @@ final class DDSHeader {
|
|||||||
int dwReserved2 = imageInput.readInt(); // [124,127]
|
int dwReserved2 = imageInput.readInt(); // [124,127]
|
||||||
|
|
||||||
if (header.fourCC == DDSType.DXT10.fourCC()) {
|
if (header.fourCC == DDSType.DXT10.fourCC()) {
|
||||||
|
// If DXT10, the DXT10 header will follow immediately
|
||||||
header.dxt10Header = DXT10Header.read(imageInput);
|
header.dxt10Header = DXT10Header.read(imageInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,8 +140,8 @@ final class DDSHeader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean getFlag(int mask) {
|
private boolean hasFlag(int mask) {
|
||||||
return (flags & mask) != 0;
|
return (flags & mask) == mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getWidth(int imageIndex) {
|
int getWidth(int imageIndex) {
|
||||||
|
|||||||
-1
@@ -77,5 +77,4 @@ final class DDSImageMetadata extends StandardImageMetadataSupport {
|
|||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -33,6 +33,7 @@ package com.twelvemonkeys.imageio.plugins.dds;
|
|||||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||||
|
|
||||||
|
import javax.imageio.IIOException;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.imageio.ImageReadParam;
|
import javax.imageio.ImageReadParam;
|
||||||
import javax.imageio.ImageTypeSpecifier;
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
@@ -167,6 +168,12 @@ public final class DDSImageReader extends ImageReaderBase {
|
|||||||
private void readHeader() throws IOException {
|
private void readHeader() throws IOException {
|
||||||
if (header == null) {
|
if (header == null) {
|
||||||
imageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
imageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
|
||||||
|
int magic = imageInput.readInt();
|
||||||
|
if (magic != DDS.MAGIC) {
|
||||||
|
throw new IIOException(String.format("Not a DDS file. Expected DDS magic 0x%8x', read 0x%8x", DDS.MAGIC, magic));
|
||||||
|
}
|
||||||
|
|
||||||
header = DDSHeader.read(imageInput);
|
header = DDSHeader.read(imageInput);
|
||||||
imageInput.flushBefore(imageInput.getStreamPosition());
|
imageInput.flushBefore(imageInput.getStreamPosition());
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-33
@@ -2,6 +2,7 @@ package com.twelvemonkeys.imageio.plugins.dds;
|
|||||||
|
|
||||||
import com.twelvemonkeys.imageio.ImageWriterBase;
|
import com.twelvemonkeys.imageio.ImageWriterBase;
|
||||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||||
|
import com.twelvemonkeys.imageio.util.SequenceSupport;
|
||||||
|
|
||||||
import javax.imageio.IIOException;
|
import javax.imageio.IIOException;
|
||||||
import javax.imageio.IIOImage;
|
import javax.imageio.IIOImage;
|
||||||
@@ -29,9 +30,9 @@ import java.nio.file.Paths;
|
|||||||
*/
|
*/
|
||||||
class DDSImageWriter extends ImageWriterBase {
|
class DDSImageWriter extends ImageWriterBase {
|
||||||
|
|
||||||
private long startPos;
|
private final SequenceSupport mipmapSequence = new SequenceSupport();
|
||||||
// TODO: Create a SequenceSupport class that handles sequence prepare/write/end
|
|
||||||
private int mipmapIndex = -1;
|
private long headerStartPos;
|
||||||
private DDSType mipmapType;
|
private DDSType mipmapType;
|
||||||
private Dimension mipmapDimension;
|
private Dimension mipmapDimension;
|
||||||
|
|
||||||
@@ -46,7 +47,8 @@ class DDSImageWriter extends ImageWriterBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void resetMembers() {
|
protected void resetMembers() {
|
||||||
mipmapIndex = -1;
|
headerStartPos = 0;
|
||||||
|
mipmapSequence.reset();
|
||||||
mipmapType = null;
|
mipmapType = null;
|
||||||
mipmapDimension = null;
|
mipmapDimension = null;
|
||||||
}
|
}
|
||||||
@@ -64,30 +66,22 @@ class DDSImageWriter extends ImageWriterBase {
|
|||||||
@Override
|
@Override
|
||||||
public void prepareWriteSequence(IIOMetadata streamMetadata) throws IOException {
|
public void prepareWriteSequence(IIOMetadata streamMetadata) throws IOException {
|
||||||
assertOutput();
|
assertOutput();
|
||||||
|
mipmapSequence.start();
|
||||||
|
|
||||||
if (mipmapIndex >= 0) {
|
|
||||||
throw new IllegalStateException("writeSequence already started");
|
|
||||||
}
|
|
||||||
mipmapIndex = 0;
|
|
||||||
|
|
||||||
startPos = imageOutput.getStreamPosition();
|
|
||||||
imageOutput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
imageOutput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||||
imageOutput.writeInt(DDS.MAGIC);
|
imageOutput.writeInt(DDS.MAGIC);
|
||||||
imageOutput.flush();
|
imageOutput.flush();
|
||||||
|
|
||||||
|
headerStartPos = imageOutput.getStreamPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void endWriteSequence() throws IOException {
|
public void endWriteSequence() throws IOException {
|
||||||
assertOutput();
|
int mipmapCount = mipmapSequence.end();
|
||||||
|
|
||||||
if (mipmapIndex < 0) {
|
// Go back and update header
|
||||||
throw new IllegalStateException("prepareWriteSequence not called");
|
updateHeader(mipmapCount);
|
||||||
}
|
|
||||||
|
|
||||||
// Go back and update hader
|
|
||||||
updateHeader(mipmapIndex);
|
|
||||||
|
|
||||||
mipmapIndex = -1;
|
|
||||||
mipmapType = null;
|
mipmapType = null;
|
||||||
mipmapDimension = null;
|
mipmapDimension = null;
|
||||||
|
|
||||||
@@ -103,13 +97,12 @@ class DDSImageWriter extends ImageWriterBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeToSequence(IIOImage image, ImageWriteParam param) throws IOException {
|
public void writeToSequence(IIOImage image, ImageWriteParam param) throws IOException {
|
||||||
if (mipmapIndex < 0) {
|
int mipmapIndex = mipmapSequence.advance();
|
||||||
throw new IllegalStateException("prepareWriteSequence not called");
|
|
||||||
}
|
|
||||||
|
|
||||||
Raster raster = getRaster(image);
|
Raster raster = getRaster(image);
|
||||||
ensureImageChannels(raster);
|
ensureImageChannels(raster);
|
||||||
ensureTextureDimension(raster);
|
ensureTextureDimension(raster);
|
||||||
|
mipmapDimension = new Dimension(raster.getWidth(), raster.getHeight());
|
||||||
|
|
||||||
DDSImageWriteParam ddsParam = param instanceof DDSImageWriteParam
|
DDSImageWriteParam ddsParam = param instanceof DDSImageWriteParam
|
||||||
? ((DDSImageWriteParam) param)
|
? ((DDSImageWriteParam) param)
|
||||||
@@ -120,7 +113,7 @@ class DDSImageWriter extends ImageWriterBase {
|
|||||||
mipmapType = type;
|
mipmapType = type;
|
||||||
}
|
}
|
||||||
else if (type != mipmapType) {
|
else if (type != mipmapType) {
|
||||||
processWarningOccurred(mipmapIndex, "All images in DDS MipMap must use same pixel format and compression");
|
processWarningOccurred(mipmapIndex, "All images in DDS mipmap must use same pixel format and compression");
|
||||||
}
|
}
|
||||||
if (mipmapType == null) {
|
if (mipmapType == null) {
|
||||||
throw new IIOException("Only compressed DDS using DXT1-5 or DXT10 with block compression is currently supported");
|
throw new IIOException("Only compressed DDS using DXT1-5 or DXT10 with block compression is currently supported");
|
||||||
@@ -140,9 +133,6 @@ class DDSImageWriter extends ImageWriterBase {
|
|||||||
|
|
||||||
processImageProgress(100f);
|
processImageProgress(100f);
|
||||||
processImageComplete();
|
processImageComplete();
|
||||||
|
|
||||||
mipmapDimension = new Dimension(raster.getWidth(), raster.getHeight());
|
|
||||||
mipmapIndex++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Raster getRaster(IIOImage image) throws IIOException {
|
private static Raster getRaster(IIOImage image) throws IIOException {
|
||||||
@@ -210,7 +200,7 @@ class DDSImageWriter extends ImageWriterBase {
|
|||||||
//dwDepth
|
//dwDepth
|
||||||
imageOutput.writeInt(0);
|
imageOutput.writeInt(0);
|
||||||
//dwMipmapCount
|
//dwMipmapCount
|
||||||
imageOutput.writeInt(1);
|
imageOutput.writeInt(1); // Should probably write 0 here for non-mipmap?
|
||||||
//reserved
|
//reserved
|
||||||
imageOutput.write(new byte[44]);
|
imageOutput.write(new byte[44]);
|
||||||
//pixFmt
|
//pixFmt
|
||||||
@@ -230,7 +220,7 @@ class DDSImageWriter extends ImageWriterBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
long streamPosition = imageOutput.getStreamPosition();
|
long streamPosition = imageOutput.getStreamPosition();
|
||||||
imageOutput.seek(startPos + 8); // Seek back to start + 4 magic + 4 header size
|
imageOutput.seek(headerStartPos + 4); // Seek back to header start, skip 4 byte header size
|
||||||
|
|
||||||
int flags = imageOutput.readInt();
|
int flags = imageOutput.readInt();
|
||||||
imageOutput.seek(imageOutput.getStreamPosition() - 4);
|
imageOutput.seek(imageOutput.getStreamPosition() - 4);
|
||||||
@@ -268,15 +258,14 @@ class DDSImageWriter extends ImageWriterBase {
|
|||||||
//dwRGBBitCount
|
//dwRGBBitCount
|
||||||
imageOutput.writeInt(type.blockSize() * 8); // TODO: Is bitcount always a multiple of 8?
|
imageOutput.writeInt(type.blockSize() * 8); // TODO: Is bitcount always a multiple of 8?
|
||||||
|
|
||||||
int[] mask = type.rgbaMasks;
|
|
||||||
//dwRBitMask
|
//dwRBitMask
|
||||||
imageOutput.writeInt(mask[0]);
|
imageOutput.writeInt(type.rgbaMasks[0]);
|
||||||
//dwGBitMask
|
//dwGBitMask
|
||||||
imageOutput.writeInt(mask[1]);
|
imageOutput.writeInt(type.rgbaMasks[1]);
|
||||||
//dwBBitMask
|
//dwBBitMask
|
||||||
imageOutput.writeInt(mask[2]);
|
imageOutput.writeInt(type.rgbaMasks[2]);
|
||||||
//dwABitMask
|
//dwABitMask
|
||||||
imageOutput.writeInt(mask[3]);
|
imageOutput.writeInt(type.rgbaMasks[3]);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//write 5 zero integers as fourCC is used
|
//write 5 zero integers as fourCC is used
|
||||||
@@ -302,7 +291,8 @@ class DDSImageWriter extends ImageWriterBase {
|
|||||||
imageOutput.writeInt(DDS.PIXEL_FORMAT_FLAG_FOURCC);
|
imageOutput.writeInt(DDS.PIXEL_FORMAT_FLAG_FOURCC);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
imageOutput.writeInt(DDS.PIXEL_FORMAT_FLAG_RGB | (type.rgbaMasks != null ? DDS.PIXEL_FORMAT_FLAG_ALPHAPIXELS : 0));
|
imageOutput.writeInt(DDS.PIXEL_FORMAT_FLAG_RGB
|
||||||
|
| (type.rgbaMasks != null && type.rgbaMasks[3] != 0 ? DDS.PIXEL_FORMAT_FLAG_ALPHAPIXELS : 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,6 +324,7 @@ class DDSImageWriter extends ImageWriterBase {
|
|||||||
if (args.length != 1) {
|
if (args.length != 1) {
|
||||||
throw new IllegalArgumentException("Use 1 input file at a time.");
|
throw new IllegalArgumentException("Use 1 input file at a time.");
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageIO.write(ImageIO.read(new File(args[0])), "dds", new MemoryCacheImageOutputStream(Files.newOutputStream(Paths.get("output.dds"))));
|
ImageIO.write(ImageIO.read(new File(args[0])), "dds", new MemoryCacheImageOutputStream(Files.newOutputStream(Paths.get("output.dds"))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user