Compare commits

..

8 Commits

Author SHA1 Message Date
Harald Kuhr 75d7d5cdef Using new sequence support in DDSImageWriter
+ some minor bonus clean-up
2026-03-13 11:40:06 +01:00
Harald Kuhr 177eec06cc Using new sequence support in exising writers. 2026-03-13 11:38:28 +01:00
Harald Kuhr dc3d77ad95 New class for simpler sequence write support. 2026-03-13 11:37:47 +01:00
Harald Kuhr 263fb75d1d DDS cleanup (#1262)
* Refactorings and code clean-up
* Major rework/standardization:
 * DDSEncoderType, DX10DXGIFormat merged with DDSType for a single way to describe a DDS format
 * Added constants for DXGI formats
 * DDSImageWriteParam is now mutable and supports standard way of setting compression type
 * DDSImageMetadata now supports more of the format
 Performance:
 * DDSReader now use seek() to jump to correct mipmap instead of reading all bytes
 * DDSImageWriter now uses getTile(0, 0) instead of getData() for better performance
* Fix JavaDoc 🎉
* Sonar issues + roll back accidental check-in
* More clean-up: Removed optional flags from param, header size validation, metadata now reports compresion as lossy
* More clean-up: Now keeps stream byte order consistent (LE), support for Raster, more tests
* Mipmap support using ImageIO sequence API
* Added raster write test
+ fixed a small issue for PAM
* Sonar issues
2026-03-11 21:09:26 +01:00
dependabot[bot] e61ec45737 Bump org.apache.maven.plugins:maven-shade-plugin from 3.6.1 to 3.6.2
Bumps [org.apache.maven.plugins:maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.6.1 to 3.6.2.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.6.1...maven-shade-plugin-3.6.2)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-shade-plugin
  dependency-version: 3.6.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-10 16:03:03 +01:00
dependabot[bot] 6e063f263c Bump actions/upload-artifact from 6.0.0 to 7.0.0 in /.github/workflows
---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-10 16:02:33 +01:00
dependabot[bot] e2cf529000 Bump org.apache.maven.plugins:maven-resources-plugin from 3.4.0 to 3.5.0
Bumps [org.apache.maven.plugins:maven-resources-plugin](https://github.com/apache/maven-resources-plugin) from 3.4.0 to 3.5.0.
- [Release notes](https://github.com/apache/maven-resources-plugin/releases)
- [Commits](https://github.com/apache/maven-resources-plugin/compare/v3.4.0...maven-resources-plugin-3.5.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-resources-plugin
  dependency-version: 3.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-10 14:21:22 +01:00
dependabot[bot] 9d1c418d8d Bump mikepenz/action-junit-report in /.github/workflows
Bumps [mikepenz/action-junit-report](https://github.com/mikepenz/action-junit-report) from 6.2.0 to 6.3.1.
- [Release notes](https://github.com/mikepenz/action-junit-report/releases)
- [Commits](https://github.com/mikepenz/action-junit-report/compare/74626db7353a25a20a72816467ebf035f674c5f8...49b2ca06f62aa7ef83ae6769a2179271e160d8e4)

---
updated-dependencies:
- dependency-name: mikepenz/action-junit-report
  dependency-version: 6.3.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-10 14:20:41 +01:00
13 changed files with 281 additions and 124 deletions
+3 -3
View File
@@ -35,7 +35,7 @@ jobs:
- name: Run Tests
run: mvn --batch-mode --no-transfer-progress test
- name: Publish Test Report
uses: mikepenz/action-junit-report@74626db7353a25a20a72816467ebf035f674c5f8 # v5
uses: mikepenz/action-junit-report@49b2ca06f62aa7ef83ae6769a2179271e160d8e4 # v5
if: ${{ !cancelled() }}
with:
report_paths: "**/target/surefire-reports/TEST*.xml"
@@ -57,7 +57,7 @@ jobs:
- name: Run Tests
run: mvn --batch-mode --no-transfer-progress test
- name: Publish Test Report
uses: mikepenz/action-junit-report@74626db7353a25a20a72816467ebf035f674c5f8 # v5
uses: mikepenz/action-junit-report@49b2ca06f62aa7ef83ae6769a2179271e160d8e4 # v5
if: ${{ !cancelled() }}
with:
report_paths: "**/target/surefire-reports/TEST*.xml"
@@ -91,7 +91,7 @@ jobs:
- name: Run Tests
run: mvn --batch-mode --no-transfer-progress test
- name: Publish Test Report
uses: mikepenz/action-junit-report@74626db7353a25a20a72816467ebf035f674c5f8 # v5
uses: mikepenz/action-junit-report@49b2ca06f62aa7ef83ae6769a2179271e160d8e4 # v5
if: ${{ !cancelled() }}
with:
report_paths: "**/target/surefire-reports/TEST*.xml"
+1 -1
View File
@@ -49,7 +49,7 @@ jobs:
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: SARIF file
path: results.sarif
@@ -32,6 +32,7 @@ package com.twelvemonkeys.imageio.plugins.bmp;
import com.twelvemonkeys.imageio.stream.SubImageOutputStream;
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
import com.twelvemonkeys.imageio.util.SequenceSupport;
import javax.imageio.IIOException;
import javax.imageio.IIOImage;
@@ -64,7 +65,7 @@ public final class ICOImageWriter extends DIBImageWriter {
private static final int ICO_MAX_DIMENSION = 256;
private static final int INITIAL_ENTRY_COUNT = 8;
private int sequenceIndex = -1;
private final SequenceSupport sequence = new SequenceSupport();
private ImageWriter pngDelegate;
@@ -74,7 +75,7 @@ public final class ICOImageWriter extends DIBImageWriter {
@Override
protected void resetMembers() {
sequenceIndex = -1;
sequence.reset();
if (pngDelegate != null) {
pngDelegate.dispose();
@@ -107,16 +108,12 @@ public final class ICOImageWriter extends DIBImageWriter {
@Override
public void prepareWriteSequence(final IIOMetadata streamMetadata) throws IOException {
assertOutput();
if (sequenceIndex >= 0) {
throw new IllegalStateException("writeSequence already started");
}
sequence.start();
writeICOHeader();
// Count: Needs to be updated for each new image
imageOutput.writeShort(0);
sequenceIndex = 0;
// TODO: Allow passing the initial size of the directory in the stream metadata?
// - as this is much more efficient than growing...
@@ -130,27 +127,19 @@ public final class ICOImageWriter extends DIBImageWriter {
@Override
public void endWriteSequence() {
assertOutput();
if (sequenceIndex < 0) {
throw new IllegalStateException("prepareWriteSequence not called");
}
sequenceIndex = -1;
sequence.end();
}
@Override
public void writeToSequence(final IIOImage image, final ImageWriteParam param) throws IOException {
assertOutput();
if (sequenceIndex < 0) {
throw new IllegalStateException("prepareWriteSequence not called");
}
int imageIndex = sequence.advance();
if (image.hasRaster()) {
throw new UnsupportedOperationException("Raster not supported");
}
if (sequenceIndex >= INITIAL_ENTRY_COUNT) {
if (imageIndex >= INITIAL_ENTRY_COUNT) {
growIfNecessary();
}
@@ -172,7 +161,7 @@ public final class ICOImageWriter extends DIBImageWriter {
// Uncompressed, RLE4/RLE8 or PNG compressed
boolean pngCompression = param != null && "BI_PNG".equals(param.getCompressionType());
processImageStarted(sequenceIndex);
processImageStarted(imageIndex);
if (pngCompression) {
// NOTE: Embedding a PNG in a ICO is slightly different than a BMP with BI_PNG compression,
@@ -198,17 +187,15 @@ public final class ICOImageWriter extends DIBImageWriter {
// Update count
imageOutput.seek(4);
imageOutput.writeShort(sequenceIndex + 1);
imageOutput.writeShort(imageIndex + 1);
// Write entry
int entryPosition = 6 + sequenceIndex * ENTRY_SIZE;
int entryPosition = 6 + imageIndex * ENTRY_SIZE;
imageOutput.seek(entryPosition);
long size = nextPosition - imageOffset;
writeEntry(width, height, colorModel, (int) size, (int) imageOffset);
sequenceIndex++;
imageOutput.seek(nextPosition);
}
@@ -265,7 +252,7 @@ public final class ICOImageWriter extends DIBImageWriter {
pngDelegate.addIIOWriteWarningListener(new IIOWriteWarningListener() {
@Override
public void warningOccurred(ImageWriter source, int imageIndex, String warning) {
processWarningOccurred(sequenceIndex, warning);
processWarningOccurred(sequence.current(), warning);
}
});
}
@@ -0,0 +1,115 @@
/*
* Copyright (c) 2026, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.util;
import javax.imageio.IIOImage;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
/**
* A tiny utility class that keeps state for sequences.
* For use by {@code ImageWriter} implementations that supports sequence (multiple images in same stream).
*
* @see ImageWriter#canWriteSequence()
*/
public final class SequenceSupport {
// Initial state, no sequence running
private int index = -1;
/**
* Resets the sequence to initial state, regardless of the current sequence state.
*/
public void reset() {
index = -1;
}
/**
* Starts a new sequence.
*
* @throws IllegalStateException if a sequence is already running.
* @see ImageWriter#prepareWriteSequence(IIOMetadata)
*/
public void start() {
if (index >= 0) {
throw new IllegalStateException("prepareWriteSequence already invoked");
}
index = 0;
}
/**
* Advances the current sequence.
*
* @return the current sequence index.
* @throws IllegalStateException if a sequence is not running.
* @see ImageWriter#writeToSequence(IIOImage, ImageWriteParam)
*/
public int advance() {
if (index < 0) {
throw new IllegalStateException("prepareWriteSequence not invoked");
}
return index++;
}
/**
* Gets the current sequence index.
*
* @return the current sequence index, or {@code -1} if a sequence is not running.
*/
public int current() {
// This method does not throw IllegalStateException, to allow
// ImageWriters to use the index in cases that may or may not
// happen "inside" a sequence.
// I'm not entirely sure if this is a good idea...
return index;
}
/**
* Ends the current sequence.
* The sequence is reset to initial state, and a new sequence may be started.
*
* @return the current (last) sequence index
* @throws IllegalStateException if a sequence is not running.
* @see ImageWriter#endWriteSequence()
*/
public int end() {
if (index < 0) {
throw new IllegalStateException("prepareWriteSequence not invoked");
}
int last = index;
index = -1;
return last;
}
}
@@ -0,0 +1,89 @@
package com.twelvemonkeys.imageio.util;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class SequenceSupportTest {
@Test
void happyCase() {
SequenceSupport sequence = new SequenceSupport();
sequence.start();
assertEquals(0, sequence.current());
for (int i = 0; i < Byte.MAX_VALUE; i++) {
assertEquals(i, sequence.advance());
assertEquals(i + 1, sequence.current());
}
assertEquals(127, sequence.end());
assertEquals(-1, sequence.current());
assertThrows(IllegalStateException.class, sequence::advance);
}
@Test
void reset() {
SequenceSupport sequence = new SequenceSupport();
sequence.reset();
assertEquals(-1, sequence.current());
assertThrows(IllegalStateException.class, sequence::end);
sequence.start();
sequence.reset();
assertEquals(-1, sequence.current());
assertThrows(IllegalStateException.class, sequence::end);
sequence.start();
sequence.advance();
sequence.reset();
assertEquals(-1, sequence.current());
assertThrows(IllegalStateException.class, sequence::end);
sequence.start();
sequence.end();
sequence.reset();
assertEquals(-1, sequence.current());
assertThrows(IllegalStateException.class, sequence::end);
}
@Test
void startEnd() {
SequenceSupport sequence = new SequenceSupport();
sequence.start();
sequence.end();
assertEquals(-1, sequence.current());
assertThrows(IllegalStateException.class, sequence::end);
}
@Test
void startAlreadyStarted() {
SequenceSupport sequence = new SequenceSupport();
sequence.start();
assertThrows(IllegalStateException.class, sequence::start);
}
@Test
void advanceNotStarted() {
SequenceSupport sequence = new SequenceSupport();
assertThrows(IllegalStateException.class, sequence::advance);
}
@Test
void currentNotStarted() {
SequenceSupport sequence = new SequenceSupport();
assertEquals(-1, sequence.current());
}
@Test
void endNotStarted() {
SequenceSupport sequence = new SequenceSupport();
assertThrows(IllegalStateException.class, sequence::end);
}
}
@@ -47,9 +47,12 @@ import java.awt.Dimension;
import java.io.IOException;
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 {
// https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-pguide
private int flags;
private int mipMapCount;
@@ -69,26 +72,16 @@ final class DDSHeader {
static DDSHeader read(final ImageInputStream imageInput) throws IOException {
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]
if (dwSize != DDS.HEADER_SIZE) {
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]
if (!header.getFlag(DDS.FLAG_CAPS
| DDS.FLAG_HEIGHT
| DDS.FLAG_WIDTH
| DDS.FLAG_PIXELFORMAT)) {
throw new IIOException("Required DDS Flag missing in header: " + Integer.toBinaryString(header.flags));
if (!header.hasFlag(DDS.FLAG_CAPS | DDS.FLAG_HEIGHT | DDS.FLAG_WIDTH | DDS.FLAG_PIXELFORMAT)) {
// NOTE: The Microsoft DDS documentation mention that readers should not rely on these flags...
throw new IIOException("Required DDS flag missing in header: " + Integer.toBinaryString(header.flags));
}
// Read Height & Width
@@ -109,7 +102,7 @@ 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));
throw new IIOException(String.format("Invalid DDS pixel format structure size (expected %d): %d", DDS.PIXELFORMAT_SIZE, dwSize));
}
header.pixelFormatFlags = imageInput.readInt(); // [80,83]
@@ -128,6 +121,7 @@ final class DDSHeader {
int dwReserved2 = imageInput.readInt(); // [124,127]
if (header.fourCC == DDSType.DXT10.fourCC()) {
// If DXT10, the DXT10 header will follow immediately
header.dxt10Header = DXT10Header.read(imageInput);
}
@@ -146,8 +140,8 @@ final class DDSHeader {
}
}
private boolean getFlag(int mask) {
return (flags & mask) != 0;
private boolean hasFlag(int mask) {
return (flags & mask) == mask;
}
int getWidth(int imageIndex) {
@@ -77,5 +77,4 @@ final class DDSImageMetadata extends StandardImageMetadataSupport {
return count;
}
}
@@ -33,6 +33,7 @@ package com.twelvemonkeys.imageio.plugins.dds;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageTypeSpecifier;
@@ -167,6 +168,12 @@ public final class DDSImageReader extends ImageReaderBase {
private void readHeader() throws IOException {
if (header == null) {
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);
imageInput.flushBefore(imageInput.getStreamPosition());
}
@@ -2,6 +2,7 @@ package com.twelvemonkeys.imageio.plugins.dds;
import com.twelvemonkeys.imageio.ImageWriterBase;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.imageio.util.SequenceSupport;
import javax.imageio.IIOException;
import javax.imageio.IIOImage;
@@ -29,9 +30,9 @@ import java.nio.file.Paths;
*/
class DDSImageWriter extends ImageWriterBase {
private long startPos;
// TODO: Create a SequenceSupport class that handles sequence prepare/write/end
private int mipmapIndex = -1;
private final SequenceSupport mipmapSequence = new SequenceSupport();
private long headerStartPos;
private DDSType mipmapType;
private Dimension mipmapDimension;
@@ -46,7 +47,8 @@ class DDSImageWriter extends ImageWriterBase {
@Override
protected void resetMembers() {
mipmapIndex = -1;
headerStartPos = 0;
mipmapSequence.reset();
mipmapType = null;
mipmapDimension = null;
}
@@ -64,30 +66,22 @@ class DDSImageWriter extends ImageWriterBase {
@Override
public void prepareWriteSequence(IIOMetadata streamMetadata) throws IOException {
assertOutput();
mipmapSequence.start();
if (mipmapIndex >= 0) {
throw new IllegalStateException("writeSequence already started");
}
mipmapIndex = 0;
startPos = imageOutput.getStreamPosition();
imageOutput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
imageOutput.writeInt(DDS.MAGIC);
imageOutput.flush();
headerStartPos = imageOutput.getStreamPosition();
}
@Override
public void endWriteSequence() throws IOException {
assertOutput();
int mipmapCount = mipmapSequence.end();
if (mipmapIndex < 0) {
throw new IllegalStateException("prepareWriteSequence not called");
}
// Go back and update header
updateHeader(mipmapCount);
// Go back and update hader
updateHeader(mipmapIndex);
mipmapIndex = -1;
mipmapType = null;
mipmapDimension = null;
@@ -103,13 +97,12 @@ class DDSImageWriter extends ImageWriterBase {
@Override
public void writeToSequence(IIOImage image, ImageWriteParam param) throws IOException {
if (mipmapIndex < 0) {
throw new IllegalStateException("prepareWriteSequence not called");
}
int mipmapIndex = mipmapSequence.advance();
Raster raster = getRaster(image);
ensureImageChannels(raster);
ensureTextureDimension(raster);
mipmapDimension = new Dimension(raster.getWidth(), raster.getHeight());
DDSImageWriteParam ddsParam = param instanceof DDSImageWriteParam
? ((DDSImageWriteParam) param)
@@ -120,7 +113,7 @@ class DDSImageWriter extends ImageWriterBase {
mipmapType = type;
}
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) {
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);
processImageComplete();
mipmapDimension = new Dimension(raster.getWidth(), raster.getHeight());
mipmapIndex++;
}
private static Raster getRaster(IIOImage image) throws IIOException {
@@ -210,7 +200,7 @@ class DDSImageWriter extends ImageWriterBase {
//dwDepth
imageOutput.writeInt(0);
//dwMipmapCount
imageOutput.writeInt(1);
imageOutput.writeInt(1); // Should probably write 0 here for non-mipmap?
//reserved
imageOutput.write(new byte[44]);
//pixFmt
@@ -230,7 +220,7 @@ class DDSImageWriter extends ImageWriterBase {
}
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();
imageOutput.seek(imageOutput.getStreamPosition() - 4);
@@ -268,15 +258,14 @@ class DDSImageWriter extends ImageWriterBase {
//dwRGBBitCount
imageOutput.writeInt(type.blockSize() * 8); // TODO: Is bitcount always a multiple of 8?
int[] mask = type.rgbaMasks;
//dwRBitMask
imageOutput.writeInt(mask[0]);
imageOutput.writeInt(type.rgbaMasks[0]);
//dwGBitMask
imageOutput.writeInt(mask[1]);
imageOutput.writeInt(type.rgbaMasks[1]);
//dwBBitMask
imageOutput.writeInt(mask[2]);
imageOutput.writeInt(type.rgbaMasks[2]);
//dwABitMask
imageOutput.writeInt(mask[3]);
imageOutput.writeInt(type.rgbaMasks[3]);
}
else {
//write 5 zero integers as fourCC is used
@@ -302,7 +291,8 @@ class DDSImageWriter extends ImageWriterBase {
imageOutput.writeInt(DDS.PIXEL_FORMAT_FLAG_FOURCC);
}
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) {
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"))));
}
}
@@ -33,6 +33,7 @@ package com.twelvemonkeys.imageio.plugins.icns;
import com.twelvemonkeys.imageio.ImageWriterBase;
import com.twelvemonkeys.imageio.stream.SubImageOutputStream;
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
import com.twelvemonkeys.imageio.util.SequenceSupport;
import javax.imageio.IIOException;
import javax.imageio.IIOImage;
@@ -55,7 +56,7 @@ import java.util.Iterator;
*/
public final class ICNSImageWriter extends ImageWriterBase {
private int sequenceIndex = -1;
private final SequenceSupport sequence = new SequenceSupport();
private ImageWriter pngDelegate;
ICNSImageWriter(ImageWriterSpi provider) {
@@ -64,7 +65,7 @@ public final class ICNSImageWriter extends ImageWriterBase {
@Override
protected void resetMembers() {
sequenceIndex = -1;
sequence.reset();
if (pngDelegate != null) {
pngDelegate.dispose();
@@ -97,41 +98,29 @@ public final class ICNSImageWriter extends ImageWriterBase {
@Override
public void prepareWriteSequence(final IIOMetadata streamMetadata) throws IOException {
assertOutput();
sequence.start();
// TODO: Allow TOC resource to be passed as stream metadata?
// - We only need number of icons to be written later
// - The contents of the TOC could be updated while adding to the sequence
if (sequenceIndex >= 0) {
throw new IllegalStateException("writeSequence already started");
}
writeICNSHeader();
sequenceIndex = 0;
}
@SuppressWarnings("RedundantThrows")
@Override
public void endWriteSequence() throws IOException {
assertOutput();
if (sequenceIndex < 0) {
throw new IllegalStateException("prepareWriteSequence not called");
}
sequence.end();
// TODO: Now that we know the number of icon resources, we could move all data backwards
// and write a TOC... But I don't think the benefit will outweigh the cost.
sequenceIndex = -1;
}
@Override
public void writeToSequence(final IIOImage image, final ImageWriteParam param) throws IOException {
assertOutput();
if (sequenceIndex < 0) {
throw new IllegalStateException("prepareWriteSequence not called");
}
int imageIndex = sequence.advance();
if (image.hasRaster()) {
throw new UnsupportedOperationException("image has a Raster");
@@ -148,7 +137,7 @@ public final class ICNSImageWriter extends ImageWriterBase {
imageOutput.writeInt(IconResource.typeFromImage(image.getRenderedImage(), "PNG"));
imageOutput.writeInt(0); // Size, update later
processImageStarted(sequenceIndex);
processImageStarted(imageIndex);
// Write icon in PNG format
ImageWriter writer = getPNGDelegate();
@@ -208,7 +197,7 @@ public final class ICNSImageWriter extends ImageWriterBase {
pngDelegate.addIIOWriteWarningListener(new IIOWriteWarningListener() {
@Override
public void warningOccurred(ImageWriter source, int imageIndex, String warning) {
processWarningOccurred(sequenceIndex, warning);
processWarningOccurred(sequence.current(), warning);
}
});
}
@@ -42,6 +42,7 @@ import com.twelvemonkeys.imageio.stream.SubImageOutputStream;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
import com.twelvemonkeys.imageio.util.SequenceSupport;
import com.twelvemonkeys.io.enc.EncoderStream;
import com.twelvemonkeys.io.enc.PackBitsEncoder;
import com.twelvemonkeys.lang.Validate;
@@ -110,12 +111,7 @@ public final class TIFFImageWriter extends ImageWriterBase {
// Support storing multiple images in one stream (multi-page TIFF)
// Support more of the ImageIO metadata (ie. compression from metadata, etc)
/**
* Flag for active sequence writing
*/
private boolean writingSequence = false;
private int sequenceIndex = 0;
private final SequenceSupport sequence = new SequenceSupport();
/**
* Metadata writer for sequence writing
@@ -751,7 +747,7 @@ public final class TIFFImageWriter extends ImageWriterBase {
ifd = ((TIFFImageMetadata) inData).getIFD();
}
else {
TIFFImageMetadata outData = new TIFFImageMetadata(Collections.<Entry>emptySet());
TIFFImageMetadata outData = new TIFFImageMetadata(Collections.emptySet());
try {
if (Arrays.asList(inData.getMetadataFormatNames()).contains(SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME)) {
@@ -766,7 +762,7 @@ public final class TIFFImageWriter extends ImageWriterBase {
}
}
catch (IIOInvalidTreeException e) {
processWarningOccurred(sequenceIndex, "Could not convert image meta data: " + e.getMessage());
processWarningOccurred(sequence.current(), "Could not convert image meta data: " + e.getMessage());
}
ifd = outData.getIFD();
@@ -966,14 +962,11 @@ public final class TIFFImageWriter extends ImageWriterBase {
@Override
public void prepareWriteSequence(final IIOMetadata streamMetadata) throws IOException {
if (writingSequence) {
throw new IllegalStateException("sequence writing has already been started!");
}
sequence.start();
assertOutput();
configureStreamByteOrder(streamMetadata, imageOutput);
writingSequence = true;
sequenceTIFFWriter = new TIFFWriter(isBigTIFF() ? 8 : 4);
sequenceTIFFWriter.writeTIFFHeader(imageOutput);
sequenceLastIFDPos = imageOutput.getStreamPosition();
@@ -985,26 +978,20 @@ public final class TIFFImageWriter extends ImageWriterBase {
@Override
public void writeToSequence(final IIOImage image, final ImageWriteParam param) throws IOException {
if (!writingSequence) {
throw new IllegalStateException("prepareWriteSequence() must be called before writeToSequence()!");
}
int sequenceIndex = sequence.advance();
if (sequenceIndex > 0) {
imageOutput.flushBefore(sequenceLastIFDPos);
imageOutput.seek(imageOutput.length());
}
sequenceLastIFDPos = writePage(sequenceIndex++, image, param, sequenceTIFFWriter, sequenceLastIFDPos);
sequenceLastIFDPos = writePage(sequenceIndex, image, param, sequenceTIFFWriter, sequenceLastIFDPos);
}
@Override
public void endWriteSequence() throws IOException {
if (!writingSequence) {
throw new IllegalStateException("prepareWriteSequence() must be called before endWriteSequence()!");
}
sequence.end();
writingSequence = false;
sequenceIndex = 0;
sequenceTIFFWriter = null;
sequenceLastIFDPos = -1;
imageOutput.flush();
@@ -1014,8 +1001,7 @@ public final class TIFFImageWriter extends ImageWriterBase {
protected void resetMembers() {
super.resetMembers();
writingSequence = false;
sequenceIndex = 0;
sequence.reset();
sequenceTIFFWriter = null;
sequenceLastIFDPos = -1;
}
+1 -1
View File
@@ -191,7 +191,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.4.0</version>
<version>3.5.0</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
+1 -1
View File
@@ -82,7 +82,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.1</version>
<version>3.6.2</version>
<executions>
<execution>
<id>jakarta</id>