diff --git a/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/util/IIOUtil.java b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/util/IIOUtil.java index 912a8910..85ad435e 100644 --- a/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/util/IIOUtil.java +++ b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/util/IIOUtil.java @@ -72,7 +72,7 @@ public final class IIOUtil { */ public static InputStream createStreamAdapter(final ImageInputStream pStream) { // TODO: Include stream start pos? - // TODO: Skip buffering for known in-memory implementations? + // TODO: Skip buffering for known in-memory implementations? pStream.isCachedMemory return new BufferedInputStream(new IIOInputStreamAdapter(pStream)); } @@ -86,7 +86,7 @@ public final class IIOUtil { */ public static InputStream createStreamAdapter(final ImageInputStream pStream, final long pLength) { // TODO: Include stream start pos? - // TODO: Skip buffering for known in-memory implementations? + // TODO: Skip buffering for known in-memory implementations? pStream.isCachedMemory return new BufferedInputStream(new IIOInputStreamAdapter(pStream, pLength)); } diff --git a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDS.java b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDS.java index 713fbb71..126956cf 100644 --- a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDS.java +++ b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDS.java @@ -35,6 +35,7 @@ package com.twelvemonkeys.imageio.plugins.dds; interface DDS { int MAGIC = ('D' << 24) + ('D' << 16) + ('S' << 8) + ' '; // Big-Endian int HEADER_SIZE = 124; + int PIXELFORMAT_SIZE = 32; // Header Flags int FLAG_CAPS = 1; // Required in every .dds file. diff --git a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSHeader.java b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSHeader.java index dee8e985..4a315939 100644 --- a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSHeader.java +++ b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSHeader.java @@ -111,6 +111,9 @@ final class DDSHeader { // DDS_PIXELFORMAT structure int px_dwSize = imageInput.readInt(); // [76,79] + if (px_dwSize != DDS.PIXELFORMAT_SIZE) { + throw new IIOException(String.format("Invalid DDS PIXELFORMAT size (expected %d): %d", DDS.PIXELFORMAT_SIZE, dwSize)); + } header.pixelFormatFlags = imageInput.readInt(); // [80,83] header.fourCC = imageInput.readInt(); // [84,87] @@ -243,10 +246,12 @@ final class DDSHeader { @Override public String toString() { return "DDSHeader{" + - "flags=" + flags + + "flags=" + Integer.toBinaryString(flags) + ", mipMapCount=" + mipMapCount + - ", dimensions=" + Arrays.toString(dimensions) + - ", pixelFormatFlags=" + pixelFormatFlags + + ", dimensions=" + Arrays.toString(Arrays.stream(dimensions) + .map(DDSHeader::dimensionToString) + .toArray(String[]::new)) + + ", pixelFormatFlags=" + Integer.toBinaryString(pixelFormatFlags) + ", fourCC=" + fourCC + ", bitCount=" + bitCount + ", redMask=" + redMask + @@ -255,4 +260,8 @@ final class DDSHeader { ", alphaMask=" + alphaMask + '}'; } + + private static String dimensionToString(Dimension dimension) { + return String.format("%dx%d", dimension.width, dimension.height); + } } diff --git a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageDataEncoder.java b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageDataEncoder.java index dadd26d5..acf242f2 100644 --- a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageDataEncoder.java +++ b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageDataEncoder.java @@ -13,15 +13,11 @@ import static com.twelvemonkeys.imageio.plugins.dds.DDSReader.RGB_16_ORDER; /** * A designated class to encode image data to binary. - *

- * References: - *

- * [1] GPU DXT Decompression. - * [2] TEXTURE COMPRESSION TECHNIQUES. - * [3] Real-Time DXT Compression by J.M.P. van Waveren - * [4] Khronos Data Format Specification v1.4 by Andrew Garrard - *

- *

+ * + * @see GPU DXT Decompression. + * @see TEXTURE COMPRESSION TECHNIQUES. + * @see Real-Time DXT Compression by J.M.P. van Waveren + * @see Khronos Data Format Specification v1.4 by Andrew Garrard */ class DDSImageDataEncoder { private DDSImageDataEncoder() {} @@ -32,7 +28,7 @@ class DDSImageDataEncoder { private static final int BC4_CHANNEL_GREEN = 1; //same re-usage as BC3 but for green channel BC5 uses static void writeImageData(ImageOutputStream imageOutput, RenderedImage renderedImage, BlockCompression compression) throws IOException { - // TODO: compression == null for custom RGB data? + // TODO: Support compression == null for uncompressed RGB(A/X) data? switch (compression) { case BC1: diff --git a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageMetadata.java b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageMetadata.java index 575795b5..ff77fef9 100755 --- a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageMetadata.java +++ b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageMetadata.java @@ -39,6 +39,7 @@ final class DDSImageMetadata extends StandardImageMetadataSupport { DDSImageMetadata(ImageTypeSpecifier specifier, DDSType type) { super(builder(specifier) .withCompressionTypeName(compressionName(type)) + .withCompressionLossless(!type.isBlockCompression()) .withBitsPerSample(bitsPerSample(type)) .withFormatVersion("1.0") ); diff --git a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageReader.java b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageReader.java index b74bfc65..474b7ff5 100644 --- a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageReader.java +++ b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageReader.java @@ -101,6 +101,11 @@ public final class DDSImageReader extends ImageReaderBase { return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB); } + // TODO: DXT1 can have 1 bit alpha, usually don't... + // DXT3/5 have alpha + // DXT2/4 ...? + + return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB); } @@ -161,9 +166,11 @@ public final class DDSImageReader extends ImageReaderBase { private void readHeader() throws IOException { if (header == null) { - imageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN); + imageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN); // TODO: Move to setInput? header = DDSHeader.read(imageInput); imageInput.flushBefore(imageInput.getStreamPosition()); + + System.out.println("header = " + header); } imageInput.seek(imageInput.getFlushedPosition()); diff --git a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageWriteParam.java b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageWriteParam.java index 33eea204..18ec5a53 100644 --- a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageWriteParam.java +++ b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageWriteParam.java @@ -22,38 +22,12 @@ public final class DDSImageWriteParam extends ImageWriteParam { return compressionTypes.toArray(new String[0]); } - private int optionalBitFlags; private boolean writeDXT10; DDSImageWriteParam() { canWriteCompressed = true; compressionTypes = COMPRESSION_TYPES; compressionType = DEFAULT_TYPE.name(); - setLinearSize(); - } - - // TODO: Set this always for compressed images? - public void setLinearSize() { - optionalBitFlags |= DDS.FLAG_LINEARSIZE; - } - - public void clearLinearSize() { - optionalBitFlags &= ~DDS.FLAG_LINEARSIZE; - } - - // TODO: Set this always for uncompressed images? - public void setPitch() { - optionalBitFlags |= DDS.FLAG_PITCH; - } - - public void clearPitch() { - optionalBitFlags &= ~DDS.FLAG_PITCH; - } - - // TODO: Other flags? - - public int optionalBitFlags() { - return optionalBitFlags; } public void setWriteDX10() { diff --git a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageWriter.java b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageWriter.java index 87beed3b..22f50b46 100644 --- a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageWriter.java +++ b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageWriter.java @@ -35,6 +35,10 @@ class DDSImageWriter extends ImageWriterBase { return new DDSImageWriteParam(); } + // TODO: Suppport MipMaps using sequence methods + // This involves seeking backwards, updating the mipmap flag and mipmapcount in the header... :-/ + // + ensuring that each level is half the size of the previous, but still a multiple of 4... + @Override public void write(IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param) throws IOException { assertOutput(); @@ -52,7 +56,7 @@ class DDSImageWriter extends ImageWriterBase { imageOutput.writeInt(DDS.MAGIC); imageOutput.setByteOrder(ByteOrder.LITTLE_ENDIAN); - writeHeader(image, ddsParam.type(), ddsParam.isWriteDXT10(), ddsParam.optionalBitFlags()); + writeHeader(image, ddsParam.type(), ddsParam.isWriteDXT10()); if (ddsParam.isWriteDXT10()) { writeDXT10Header(ddsParam.getDxgiFormat()); } @@ -96,9 +100,10 @@ class DDSImageWriter extends ImageWriterBase { } } - private void writeHeader(IIOImage image, DDSType type, boolean writeDXT10, int optionalBitFlags) throws IOException { + private void writeHeader(IIOImage image, DDSType type, boolean writeDXT10) throws IOException { imageOutput.writeInt(DDS.HEADER_SIZE); - imageOutput.writeInt(DDS.FLAG_CAPS | DDS.FLAG_HEIGHT | DDS.FLAG_WIDTH | DDS.FLAG_PIXELFORMAT | optionalBitFlags); + int linearSizeOrPitch = type.isBlockCompression() ? DDS.FLAG_LINEARSIZE : DDS.FLAG_PITCH; + imageOutput.writeInt(DDS.FLAG_CAPS | DDS.FLAG_HEIGHT | DDS.FLAG_WIDTH | DDS.FLAG_PIXELFORMAT | linearSizeOrPitch); RenderedImage renderedImage = image.getRenderedImage(); int height = renderedImage.getHeight(); @@ -182,7 +187,8 @@ class DDSImageWriter extends ImageWriterBase { private void writePixelFormatFlags(DDSType type, boolean writeDXT10) throws IOException { if (writeDXT10 || type.isFourCC()) { 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)); } } @@ -190,7 +196,8 @@ class DDSImageWriter extends ImageWriterBase { private void writePitchOrLinearSize(int height, int width, DDSType type) throws IOException { if (type.isBlockCompression()) { imageOutput.writeInt(((width + 3) / 4) * ((height + 3) / 4) * type.blockSize()); - } else { + } + else { imageOutput.writeInt(width * type.blockSize()); } } diff --git a/imageio/imageio-dds/src/test/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageMetadataTest.java b/imageio/imageio-dds/src/test/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageMetadataTest.java index 4e2a6993..12ccff12 100644 --- a/imageio/imageio-dds/src/test/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageMetadataTest.java +++ b/imageio/imageio-dds/src/test/java/com/twelvemonkeys/imageio/plugins/dds/DDSImageMetadataTest.java @@ -17,13 +17,16 @@ class DDSImageMetadataTest { DDSImageMetadata metadata = createDDSImageMetadata(BufferedImage.TYPE_INT_ARGB, DDSType.DXT1); IIOMetadataNode tree = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName); - NodeList compressions = tree.getElementsByTagName("CompressionTypeName"); - assertEquals(1, compressions.getLength()); + NodeList compressionTypeNames = tree.getElementsByTagName("CompressionTypeName"); + assertEquals(1, compressionTypeNames.getLength()); + IIOMetadataNode compressionTypeName = (IIOMetadataNode) compressionTypeNames.item(0); + assertEquals("DXT1", compressionTypeName.getAttribute("value")); - IIOMetadataNode compression = (IIOMetadataNode) compressions.item(0); - assertEquals("DXT1", compression.getAttribute("value")); + NodeList losslesses = tree.getElementsByTagName("Lossless"); + assertEquals(1, losslesses.getLength()); + IIOMetadataNode lossless = (IIOMetadataNode) losslesses.item(0); + assertEquals("FALSE", lossless.getAttribute("value")); - // TODO: This should probably not have alpha... NodeList alphas = tree.getElementsByTagName("Alpha"); assertEquals(1, alphas.getLength()); IIOMetadataNode alpha = (IIOMetadataNode) alphas.item(0);