mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-04-03 00:00:04 -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) {
|
||||
// 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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,15 +13,11 @@ import static com.twelvemonkeys.imageio.plugins.dds.DDSReader.RGB_16_ORDER;
|
||||
|
||||
/**
|
||||
* A designated class to encode image data to binary.
|
||||
* <p>
|
||||
* References:
|
||||
* <p>
|
||||
* [1] <a href="https://www.ludicon.com/castano/blog/2009/03/gpu-dxt-decompression/">GPU DXT Decompression</a>.
|
||||
* [2] <a href="https://sv-journal.org/2014-1/06/en/index.php">TEXTURE COMPRESSION TECHNIQUES</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>
|
||||
*
|
||||
* @see <a href="https://www.ludicon.com/castano/blog/2009/03/gpu-dxt-decompression/">GPU DXT Decompression</a>.
|
||||
* @see <a href="https://sv-journal.org/2014-1/06/en/index.php">TEXTURE COMPRESSION TECHNIQUES</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>
|
||||
* @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>
|
||||
*/
|
||||
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:
|
||||
|
||||
@@ -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")
|
||||
);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user