mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-28 00:00:03 -04:00
More clean-up: Removed optional flags from param, header size validation, metadata now reports compresion as lossy
This commit is contained in:
@@ -72,7 +72,7 @@ public final class IIOUtil {
|
|||||||
*/
|
*/
|
||||||
public static InputStream createStreamAdapter(final ImageInputStream pStream) {
|
public static InputStream createStreamAdapter(final ImageInputStream pStream) {
|
||||||
// TODO: Include stream start pos?
|
// 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));
|
return new BufferedInputStream(new IIOInputStreamAdapter(pStream));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ public final class IIOUtil {
|
|||||||
*/
|
*/
|
||||||
public static InputStream createStreamAdapter(final ImageInputStream pStream, final long pLength) {
|
public static InputStream createStreamAdapter(final ImageInputStream pStream, final long pLength) {
|
||||||
// TODO: Include stream start pos?
|
// 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));
|
return new BufferedInputStream(new IIOInputStreamAdapter(pStream, pLength));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ package com.twelvemonkeys.imageio.plugins.dds;
|
|||||||
interface DDS {
|
interface DDS {
|
||||||
int MAGIC = ('D' << 24) + ('D' << 16) + ('S' << 8) + ' '; // Big-Endian
|
int MAGIC = ('D' << 24) + ('D' << 16) + ('S' << 8) + ' '; // Big-Endian
|
||||||
int HEADER_SIZE = 124;
|
int HEADER_SIZE = 124;
|
||||||
|
int PIXELFORMAT_SIZE = 32;
|
||||||
|
|
||||||
// Header Flags
|
// Header Flags
|
||||||
int FLAG_CAPS = 1; // Required in every .dds file.
|
int FLAG_CAPS = 1; // Required in every .dds file.
|
||||||
|
|||||||
+12
-3
@@ -111,6 +111,9 @@ 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) {
|
||||||
|
throw new IIOException(String.format("Invalid DDS PIXELFORMAT size (expected %d): %d", DDS.PIXELFORMAT_SIZE, dwSize));
|
||||||
|
}
|
||||||
|
|
||||||
header.pixelFormatFlags = imageInput.readInt(); // [80,83]
|
header.pixelFormatFlags = imageInput.readInt(); // [80,83]
|
||||||
header.fourCC = imageInput.readInt(); // [84,87]
|
header.fourCC = imageInput.readInt(); // [84,87]
|
||||||
@@ -243,10 +246,12 @@ final class DDSHeader {
|
|||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "DDSHeader{" +
|
return "DDSHeader{" +
|
||||||
"flags=" + flags +
|
"flags=" + Integer.toBinaryString(flags) +
|
||||||
", mipMapCount=" + mipMapCount +
|
", mipMapCount=" + mipMapCount +
|
||||||
", dimensions=" + Arrays.toString(dimensions) +
|
", dimensions=" + Arrays.toString(Arrays.stream(dimensions)
|
||||||
", pixelFormatFlags=" + pixelFormatFlags +
|
.map(DDSHeader::dimensionToString)
|
||||||
|
.toArray(String[]::new)) +
|
||||||
|
", pixelFormatFlags=" + Integer.toBinaryString(pixelFormatFlags) +
|
||||||
", fourCC=" + fourCC +
|
", fourCC=" + fourCC +
|
||||||
", bitCount=" + bitCount +
|
", bitCount=" + bitCount +
|
||||||
", redMask=" + redMask +
|
", redMask=" + redMask +
|
||||||
@@ -255,4 +260,8 @@ final class DDSHeader {
|
|||||||
", alphaMask=" + alphaMask +
|
", alphaMask=" + alphaMask +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String dimensionToString(Dimension dimension) {
|
||||||
|
return String.format("%dx%d", dimension.width, dimension.height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-10
@@ -13,15 +13,11 @@ import static com.twelvemonkeys.imageio.plugins.dds.DDSReader.RGB_16_ORDER;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A designated class to encode image data to binary.
|
* A designated class to encode image data to binary.
|
||||||
* <p>
|
*
|
||||||
* References:
|
* @see <a href="https://www.ludicon.com/castano/blog/2009/03/gpu-dxt-decompression/">GPU DXT Decompression</a>.
|
||||||
* <p>
|
* @see <a href="https://sv-journal.org/2014-1/06/en/index.php">TEXTURE COMPRESSION TECHNIQUES</a>.
|
||||||
* [1] <a href="https://www.ludicon.com/castano/blog/2009/03/gpu-dxt-decompression/">GPU DXT Decompression</a>.
|
* @see <a href="https://mrelusive.com/publications/papers/Real-Time-Dxt-Compression.pdf">Real-Time DXT Compression by J.M.P. van Waveren</a>
|
||||||
* [2] <a href="https://sv-journal.org/2014-1/06/en/index.php">TEXTURE COMPRESSION TECHNIQUES</a>.
|
* @see <a href="https://registry.khronos.org/DataFormat/specs/1.4/dataformat.1.4.pdf">Khronos Data Format Specification v1.4 by Andrew Garrard</a>
|
||||||
* [3] <a href="https://mrelusive.com/publications/papers/Real-Time-Dxt-Compression.pdf">Real-Time DXT Compression by J.M.P. van Waveren</a>
|
|
||||||
* [4] <a href="https://registry.khronos.org/DataFormat/specs/1.4/dataformat.1.4.pdf">Khronos Data Format Specification v1.4 by Andrew Garrard</a>
|
|
||||||
* </p>
|
|
||||||
* </p>
|
|
||||||
*/
|
*/
|
||||||
class DDSImageDataEncoder {
|
class DDSImageDataEncoder {
|
||||||
private 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
|
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 {
|
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) {
|
switch (compression) {
|
||||||
case BC1:
|
case BC1:
|
||||||
|
|||||||
+1
@@ -39,6 +39,7 @@ final class DDSImageMetadata extends StandardImageMetadataSupport {
|
|||||||
DDSImageMetadata(ImageTypeSpecifier specifier, DDSType type) {
|
DDSImageMetadata(ImageTypeSpecifier specifier, DDSType type) {
|
||||||
super(builder(specifier)
|
super(builder(specifier)
|
||||||
.withCompressionTypeName(compressionName(type))
|
.withCompressionTypeName(compressionName(type))
|
||||||
|
.withCompressionLossless(!type.isBlockCompression())
|
||||||
.withBitsPerSample(bitsPerSample(type))
|
.withBitsPerSample(bitsPerSample(type))
|
||||||
.withFormatVersion("1.0")
|
.withFormatVersion("1.0")
|
||||||
);
|
);
|
||||||
|
|||||||
+8
-1
@@ -101,6 +101,11 @@ public final class DDSImageReader extends ImageReaderBase {
|
|||||||
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
|
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);
|
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,9 +166,11 @@ 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); // TODO: Move to setInput?
|
||||||
header = DDSHeader.read(imageInput);
|
header = DDSHeader.read(imageInput);
|
||||||
imageInput.flushBefore(imageInput.getStreamPosition());
|
imageInput.flushBefore(imageInput.getStreamPosition());
|
||||||
|
|
||||||
|
System.out.println("header = " + header);
|
||||||
}
|
}
|
||||||
|
|
||||||
imageInput.seek(imageInput.getFlushedPosition());
|
imageInput.seek(imageInput.getFlushedPosition());
|
||||||
|
|||||||
-26
@@ -22,38 +22,12 @@ public final class DDSImageWriteParam extends ImageWriteParam {
|
|||||||
return compressionTypes.toArray(new String[0]);
|
return compressionTypes.toArray(new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int optionalBitFlags;
|
|
||||||
private boolean writeDXT10;
|
private boolean writeDXT10;
|
||||||
|
|
||||||
DDSImageWriteParam() {
|
DDSImageWriteParam() {
|
||||||
canWriteCompressed = true;
|
canWriteCompressed = true;
|
||||||
compressionTypes = COMPRESSION_TYPES;
|
compressionTypes = COMPRESSION_TYPES;
|
||||||
compressionType = DEFAULT_TYPE.name();
|
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() {
|
public void setWriteDX10() {
|
||||||
|
|||||||
+12
-5
@@ -35,6 +35,10 @@ class DDSImageWriter extends ImageWriterBase {
|
|||||||
return new DDSImageWriteParam();
|
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
|
@Override
|
||||||
public void write(IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param) throws IOException {
|
public void write(IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param) throws IOException {
|
||||||
assertOutput();
|
assertOutput();
|
||||||
@@ -52,7 +56,7 @@ class DDSImageWriter extends ImageWriterBase {
|
|||||||
imageOutput.writeInt(DDS.MAGIC);
|
imageOutput.writeInt(DDS.MAGIC);
|
||||||
imageOutput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
imageOutput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
|
||||||
writeHeader(image, ddsParam.type(), ddsParam.isWriteDXT10(), ddsParam.optionalBitFlags());
|
writeHeader(image, ddsParam.type(), ddsParam.isWriteDXT10());
|
||||||
if (ddsParam.isWriteDXT10()) {
|
if (ddsParam.isWriteDXT10()) {
|
||||||
writeDXT10Header(ddsParam.getDxgiFormat());
|
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.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();
|
RenderedImage renderedImage = image.getRenderedImage();
|
||||||
int height = renderedImage.getHeight();
|
int height = renderedImage.getHeight();
|
||||||
@@ -182,7 +187,8 @@ class DDSImageWriter extends ImageWriterBase {
|
|||||||
private void writePixelFormatFlags(DDSType type, boolean writeDXT10) throws IOException {
|
private void writePixelFormatFlags(DDSType type, boolean writeDXT10) throws IOException {
|
||||||
if (writeDXT10 || type.isFourCC()) {
|
if (writeDXT10 || type.isFourCC()) {
|
||||||
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 ? 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 {
|
private void writePitchOrLinearSize(int height, int width, DDSType type) throws IOException {
|
||||||
if (type.isBlockCompression()) {
|
if (type.isBlockCompression()) {
|
||||||
imageOutput.writeInt(((width + 3) / 4) * ((height + 3) / 4) * type.blockSize());
|
imageOutput.writeInt(((width + 3) / 4) * ((height + 3) / 4) * type.blockSize());
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
imageOutput.writeInt(width * type.blockSize());
|
imageOutput.writeInt(width * type.blockSize());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-5
@@ -17,13 +17,16 @@ class DDSImageMetadataTest {
|
|||||||
DDSImageMetadata metadata = createDDSImageMetadata(BufferedImage.TYPE_INT_ARGB, DDSType.DXT1);
|
DDSImageMetadata metadata = createDDSImageMetadata(BufferedImage.TYPE_INT_ARGB, DDSType.DXT1);
|
||||||
IIOMetadataNode tree = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
|
IIOMetadataNode tree = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
|
||||||
|
|
||||||
NodeList compressions = tree.getElementsByTagName("CompressionTypeName");
|
NodeList compressionTypeNames = tree.getElementsByTagName("CompressionTypeName");
|
||||||
assertEquals(1, compressions.getLength());
|
assertEquals(1, compressionTypeNames.getLength());
|
||||||
|
IIOMetadataNode compressionTypeName = (IIOMetadataNode) compressionTypeNames.item(0);
|
||||||
|
assertEquals("DXT1", compressionTypeName.getAttribute("value"));
|
||||||
|
|
||||||
IIOMetadataNode compression = (IIOMetadataNode) compressions.item(0);
|
NodeList losslesses = tree.getElementsByTagName("Lossless");
|
||||||
assertEquals("DXT1", compression.getAttribute("value"));
|
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");
|
NodeList alphas = tree.getElementsByTagName("Alpha");
|
||||||
assertEquals(1, alphas.getLength());
|
assertEquals(1, alphas.getLength());
|
||||||
IIOMetadataNode alpha = (IIOMetadataNode) alphas.item(0);
|
IIOMetadataNode alpha = (IIOMetadataNode) alphas.item(0);
|
||||||
|
|||||||
Reference in New Issue
Block a user