From fc80ac2ee993382c1c7635a9955daff71cf7b62c Mon Sep 17 00:00:00 2001 From: Oliver Schmidtmer Date: Sun, 17 Jan 2016 17:12:35 +0100 Subject: [PATCH 1/3] Enable sequence writing on TIFFImageWriter --- .../imageio/plugins/tiff/TIFFImageWriter.java | 117 +++++++++++++++--- .../plugins/tiff/TIFFImageWriterTest.java | 46 ++++++- 2 files changed, 140 insertions(+), 23 deletions(-) diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java index db5f9d5b..814e0a25 100644 --- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java +++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java @@ -72,7 +72,6 @@ public final class TIFFImageWriter extends ImageWriterBase { // TODO: Support more of the ImageIO metadata (ie. compression from metadata, etc) // Long term - // TODO: Support writing multipage TIFFs using canWriteSequence/prepareWriteSequence/writeToSequence/endWriteSequence // TODO: Support tiling // TODO: Support thumbnails // TODO: Support CCITT Modified Huffman compression (2) @@ -102,6 +101,21 @@ public final class TIFFImageWriter extends ImageWriterBase { public static final Rational STANDARD_DPI = new Rational(72); + /** + * Flag for active sequence writing + */ + private boolean isWritingSequence = false; + + /** + * Metadata writer for sequence writing + */ + private EXIFWriter sequenceExifWriter = null; + + /** + * Position of last IFD Pointer on active sequence writing + */ + private long sequenceLastIFDPos = -1; + TIFFImageWriter(final ImageWriterSpi provider) { super(provider); } @@ -184,6 +198,7 @@ public final class TIFFImageWriter extends ImageWriterBase { // TODO: Validate input assertOutput(); + // TODO: streamMetadata? // TODO: Consider writing TIFF header, offset to IFD0 (leave blank), write image data with correct // tiling/compression/etc, then write IFD0, go back and update IFD0 offset? @@ -191,11 +206,21 @@ public final class TIFFImageWriter extends ImageWriterBase { // Write minimal TIFF header (required "Baseline" fields) // Use EXIFWriter to write leading metadata (TODO: consider rename to TTIFFWriter, again...) // TODO: Make TIFFEntry and possibly TIFFDirectory? public + EXIFWriter exifWriter = new EXIFWriter(); + exifWriter.writeTIFFHeader(imageOutput); + long IFD0Pos = imageOutput.getStreamPosition(); + imageOutput.writeInt(0); // IFD0 pointer, will be updated later + + writePage(image, param, exifWriter, IFD0Pos); + imageOutput.flush(); + } + + private long writePage(IIOImage image, ImageWriteParam param, EXIFWriter exifWriter, long lastIFDPointer) + throws IOException { RenderedImage renderedImage = image.getRenderedImage(); ColorModel colorModel = renderedImage.getColorModel(); int numComponents = colorModel.getNumComponents(); - //TODO: streamMetadata? TIFFImageMetadata metadata; if (image.getMetadata() != null) { metadata = convertImageMetadata(image.getMetadata(), ImageTypeSpecifier.createFromRenderedImage(renderedImage), param); @@ -335,9 +360,7 @@ public final class TIFFImageWriter extends ImageWriterBase { // TODO: If tiled, write tile indexes etc // Depending on param.getTilingMode - - EXIFWriter exifWriter = new EXIFWriter(); - + long nextIFDPointer = -1; if (compression == TIFFBaseline.COMPRESSION_NONE) { // This implementation, allows semi-streaming-compatible uncompressed TIFFs long streamOffset = exifWriter.computeIFDSize(entries.values()) + 12; // 12 == 4 byte magic, 4 byte IDD 0 pointer, 4 byte EOF @@ -348,13 +371,16 @@ public final class TIFFImageWriter extends ImageWriterBase { entries.remove(dummyStripOffsets); entries.put(TIFF.TAG_STRIP_OFFSETS, new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, streamOffset)); - exifWriter.write(entries.values(), imageOutput); // NOTE: Writer takes case of ordering tags - imageOutput.flush(); - } - else { - // Unless compression == 1 / COMPRESSION_NONE (and all offsets known), write only TIFF header/magic + leave room for IFD0 offset - exifWriter.writeTIFFHeader(imageOutput); - imageOutput.writeInt(-1); // IFD0 pointer, will be updated later + long idfOffset = exifWriter.writeIFD(entries.values(), imageOutput); // NOTE: Writer takes case of ordering tags + nextIFDPointer = imageOutput.getStreamPosition(); + imageOutput.writeInt(0); // Next IFD (none) + long streamPosition = imageOutput.getStreamPosition(); + + // Update IFD0 pointer + imageOutput.seek(lastIFDPointer); + imageOutput.writeInt((int) idfOffset); + imageOutput.seek(streamPosition); + imageOutput.flushBefore(nextIFDPointer); } // TODO: Create compressor stream per Tile/Strip @@ -383,23 +409,26 @@ public final class TIFFImageWriter extends ImageWriterBase { // Update IFD0-pointer, and write IFD if (compression != TIFFBaseline.COMPRESSION_NONE) { - long streamPosition = imageOutput.getStreamPosition(); + long thisIFD = imageOutput.getStreamPosition(); entries.remove(dummyStripOffsets); entries.put(TIFF.TAG_STRIP_OFFSETS, new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 8)); entries.remove(dummyStripByteCounts); - entries.put(TIFF.TAG_STRIP_BYTE_COUNTS, new TIFFEntry(TIFF.TAG_STRIP_BYTE_COUNTS, streamPosition - 8)); + entries.put(TIFF.TAG_STRIP_BYTE_COUNTS, new TIFFEntry(TIFF.TAG_STRIP_BYTE_COUNTS, thisIFD - 8)); - long ifdOffset = exifWriter.writeIFD(entries.values(), imageOutput); + long idfOffset = exifWriter.writeIFD(entries.values(), imageOutput); // NOTE: Writer takes case of ordering tags + nextIFDPointer = imageOutput.getStreamPosition(); imageOutput.writeInt(0); // Next IFD (none) - streamPosition = imageOutput.getStreamPosition(); + long streamPosition = imageOutput.getStreamPosition(); // Update IFD0 pointer - imageOutput.seek(4); - imageOutput.writeInt((int) ifdOffset); + imageOutput.seek(lastIFDPointer); + imageOutput.writeInt((int) idfOffset); imageOutput.seek(streamPosition); - imageOutput.flush(); + imageOutput.flushBefore(nextIFDPointer); } + + return nextIFDPointer; } private DataOutput createCompressorStream(RenderedImage image, ImageWriteParam param, Map entries) { @@ -838,6 +867,56 @@ public final class TIFFImageWriter extends ImageWriterBase { return new TIFFImageWriteParam(); } + @Override + public boolean canWriteSequence() { + return true; + } + + @Override + public void prepareWriteSequence(IIOMetadata streamMetadata) throws IOException { + if (isWritingSequence) { + throw new IllegalStateException("sequence writing has already been started!"); + } + + // Ignore streamMetadata. ByteOrder is determined from OutputStream + assertOutput(); + isWritingSequence = true; + sequenceExifWriter = new EXIFWriter(); + sequenceExifWriter.writeTIFFHeader(imageOutput); + sequenceLastIFDPos = imageOutput.getStreamPosition(); + imageOutput.writeInt(0); // IFD0 + } + + @Override + public void writeToSequence(IIOImage image, ImageWriteParam param) throws IOException { + if (!isWritingSequence) { + throw new IllegalStateException("prepareWriteSequence() must be called before writeToSequence()!"); + } + + sequenceLastIFDPos = writePage(image, param, sequenceExifWriter, sequenceLastIFDPos); + } + + @Override + public void endWriteSequence() throws IOException { + if (!isWritingSequence) { + throw new IllegalStateException("prepareWriteSequence() must be called before endWriteSequence()!"); + } + + isWritingSequence = false; + sequenceExifWriter = null; + sequenceLastIFDPos = -1; + imageOutput.flush(); + } + + @Override + protected void resetMembers() { + super.resetMembers(); + + isWritingSequence = false; + sequenceExifWriter = null; + sequenceLastIFDPos = -1; + } + // Test public static void main(String[] args) throws IOException { diff --git a/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriterTest.java b/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriterTest.java index c775f3d4..07e93d28 100644 --- a/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriterTest.java +++ b/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriterTest.java @@ -37,17 +37,17 @@ import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream; import com.twelvemonkeys.imageio.util.ImageWriterAbstractTestCase; import org.junit.Test; -import javax.imageio.IIOImage; -import javax.imageio.ImageIO; -import javax.imageio.ImageTypeSpecifier; -import javax.imageio.ImageWriter; +import javax.imageio.*; import javax.imageio.metadata.IIOMetadata; import javax.imageio.metadata.IIOMetadataFormatImpl; import javax.imageio.metadata.IIOMetadataNode; +import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageOutputStream; import java.awt.image.BufferedImage; import java.awt.image.RenderedImage; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.util.Arrays; import java.util.List; @@ -277,4 +277,42 @@ public class TIFFImageWriterTest extends ImageWriterAbstractTestCase { assertNotNull(software); assertEquals(softwareString, software.getValueAsString()); } + + @Test + public void testSequenceWriter() throws IOException { + ImageWriter writer = createImageWriter(); + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + ImageOutputStream stream = ImageIO.createImageOutputStream(buffer); + writer.setOutput(stream); + + RenderedImage image = getTestData(0); + + ImageWriteParam params = writer.getDefaultWriteParam(); + params.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); + + assertTrue("", writer.canWriteSequence()); + + try { + writer.prepareWriteSequence(null); + + params.setCompressionType("None"); + writer.writeToSequence(new IIOImage(image, null, null), params); + writer.writeToSequence(new IIOImage(image, null, null), params); + params.setCompressionType("JPEG"); + writer.writeToSequence(new IIOImage(image, null, null), params); + + writer.endWriteSequence(); + } + catch (IOException e) { + fail(e.getMessage()); + } + finally { + stream.close(); // Force data to be written + } + + ImageInputStream input = ImageIO.createImageInputStream(new ByteArrayInputStream(buffer.toByteArray())); + ImageReader reader = ImageIO.getImageReaders(input).next(); + reader.setInput(input); + assertEquals("wrong image count", 3, reader.getNumImages(true)); + } } From e145de01f38fbf428207f157728505e526b06f17 Mon Sep 17 00:00:00 2001 From: Oliver Schmidtmer Date: Wed, 20 Jan 2016 00:22:46 +0100 Subject: [PATCH 2/3] corrected IFD & strip width calculations for sequence writer --- .../imageio/plugins/tiff/TIFFImageWriter.java | 29 ++++++-------- .../plugins/tiff/TIFFImageWriterTest.java | 40 ++++++++++++++----- 2 files changed, 43 insertions(+), 26 deletions(-) diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java index 814e0a25..ead830c5 100644 --- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java +++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java @@ -361,9 +361,11 @@ public final class TIFFImageWriter extends ImageWriterBase { // TODO: If tiled, write tile indexes etc // Depending on param.getTilingMode long nextIFDPointer = -1; + long stripOffset = -1; + long stripByteCount = 0; if (compression == TIFFBaseline.COMPRESSION_NONE) { // This implementation, allows semi-streaming-compatible uncompressed TIFFs - long streamOffset = exifWriter.computeIFDSize(entries.values()) + 12; // 12 == 4 byte magic, 4 byte IDD 0 pointer, 4 byte EOF + long streamOffset = imageOutput.getStreamPosition() + exifWriter.computeIFDSize(entries.values()) + 4; // IFD + 4 byte EOF, (header, ifd0 + n images already written) entries.remove(dummyStripByteCounts); entries.put(TIFF.TAG_STRIP_BYTE_COUNTS, new TIFFEntry(TIFF.TAG_STRIP_BYTE_COUNTS, @@ -373,16 +375,15 @@ public final class TIFFImageWriter extends ImageWriterBase { long idfOffset = exifWriter.writeIFD(entries.values(), imageOutput); // NOTE: Writer takes case of ordering tags nextIFDPointer = imageOutput.getStreamPosition(); - imageOutput.writeInt(0); // Next IFD (none) - long streamPosition = imageOutput.getStreamPosition(); - // Update IFD0 pointer imageOutput.seek(lastIFDPointer); imageOutput.writeInt((int) idfOffset); - imageOutput.seek(streamPosition); - imageOutput.flushBefore(nextIFDPointer); + imageOutput.seek(nextIFDPointer); + imageOutput.flush(); + imageOutput.writeInt(0); // EOF } + stripOffset = imageOutput.getStreamPosition(); // TODO: Create compressor stream per Tile/Strip if (compression == TIFFExtension.COMPRESSION_JPEG) { Iterator writers = ImageIO.getImageWritersByFormatName("JPEG"); @@ -406,26 +407,23 @@ public final class TIFFImageWriter extends ImageWriterBase { writeImageData(createCompressorStream(renderedImage, param, entries), renderedImage, numComponents, bandOffsets, bitOffsets); } + stripByteCount = imageOutput.getStreamPosition() - stripOffset; // Update IFD0-pointer, and write IFD if (compression != TIFFBaseline.COMPRESSION_NONE) { - long thisIFD = imageOutput.getStreamPosition(); - entries.remove(dummyStripOffsets); - entries.put(TIFF.TAG_STRIP_OFFSETS, new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 8)); + entries.put(TIFF.TAG_STRIP_OFFSETS, new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, stripOffset)); entries.remove(dummyStripByteCounts); - entries.put(TIFF.TAG_STRIP_BYTE_COUNTS, new TIFFEntry(TIFF.TAG_STRIP_BYTE_COUNTS, thisIFD - 8)); + entries.put(TIFF.TAG_STRIP_BYTE_COUNTS, new TIFFEntry(TIFF.TAG_STRIP_BYTE_COUNTS, stripByteCount)); long idfOffset = exifWriter.writeIFD(entries.values(), imageOutput); // NOTE: Writer takes case of ordering tags nextIFDPointer = imageOutput.getStreamPosition(); - imageOutput.writeInt(0); // Next IFD (none) - long streamPosition = imageOutput.getStreamPosition(); - // Update IFD0 pointer imageOutput.seek(lastIFDPointer); imageOutput.writeInt((int) idfOffset); - imageOutput.seek(streamPosition); - imageOutput.flushBefore(nextIFDPointer); + imageOutput.seek(nextIFDPointer); + imageOutput.flush(); + imageOutput.writeInt(0); // EOF } return nextIFDPointer; @@ -892,7 +890,6 @@ public final class TIFFImageWriter extends ImageWriterBase { if (!isWritingSequence) { throw new IllegalStateException("prepareWriteSequence() must be called before writeToSequence()!"); } - sequenceLastIFDPos = writePage(image, param, sequenceExifWriter, sequenceLastIFDPos); } diff --git a/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriterTest.java b/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriterTest.java index 07e93d28..86d6e135 100644 --- a/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriterTest.java +++ b/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriterTest.java @@ -43,12 +43,10 @@ import javax.imageio.metadata.IIOMetadataFormatImpl; import javax.imageio.metadata.IIOMetadataNode; import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageOutputStream; +import java.awt.*; import java.awt.image.BufferedImage; import java.awt.image.RenderedImage; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.FileOutputStream; -import java.io.IOException; +import java.io.*; import java.util.Arrays; import java.util.List; @@ -285,7 +283,25 @@ public class TIFFImageWriterTest extends ImageWriterAbstractTestCase { ImageOutputStream stream = ImageIO.createImageOutputStream(buffer); writer.setOutput(stream); - RenderedImage image = getTestData(0); + Graphics2D g2d = null; + BufferedImage image[] = new BufferedImage[] { + new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB), + new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB), + new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB) + }; + g2d = image[0].createGraphics(); + g2d.setColor(Color.red); + g2d.fillRect(0,0,100,100); + g2d.dispose(); + g2d = image[1].createGraphics(); + g2d.setColor(Color.green); + g2d.fillRect(0,0,100,100); + g2d.dispose(); + g2d = image[2].createGraphics(); + g2d.setColor(Color.blue); + g2d.fillRect(0,0,100,100); + g2d.dispose(); + ImageWriteParam params = writer.getDefaultWriteParam(); params.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); @@ -295,12 +311,13 @@ public class TIFFImageWriterTest extends ImageWriterAbstractTestCase { try { writer.prepareWriteSequence(null); - params.setCompressionType("None"); - writer.writeToSequence(new IIOImage(image, null, null), params); - writer.writeToSequence(new IIOImage(image, null, null), params); params.setCompressionType("JPEG"); - writer.writeToSequence(new IIOImage(image, null, null), params); - + writer.writeToSequence(new IIOImage(image[0], null, null), params); + params.setCompressionType("None"); + writer.writeToSequence(new IIOImage(image[1], null, null), params); + params.setCompressionType("JPEG"); + writer.writeToSequence(new IIOImage(image[2], null, null), params); + g2d.dispose(); writer.endWriteSequence(); } catch (IOException e) { @@ -314,5 +331,8 @@ public class TIFFImageWriterTest extends ImageWriterAbstractTestCase { ImageReader reader = ImageIO.getImageReaders(input).next(); reader.setInput(input); assertEquals("wrong image count", 3, reader.getNumImages(true)); + for(int i = 0; i < reader.getNumImages(true); i++){ + reader.read(i); + } } } From e4f193400db7c53e6347c6228ed5bb12d341d64a Mon Sep 17 00:00:00 2001 From: Oliver Schmidtmer Date: Thu, 28 Jan 2016 00:31:10 +0100 Subject: [PATCH 3/3] Remove seeks on uncompressed tiffs to reenable streaming --- .../imageio/metadata/exif/EXIFWriter.java | 4 ++ .../imageio/plugins/tiff/TIFFImageWriter.java | 43 +++++++++---------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/EXIFWriter.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/EXIFWriter.java index 9f0881f5..af0ac041 100644 --- a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/EXIFWriter.java +++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/EXIFWriter.java @@ -158,6 +158,10 @@ public final class EXIFWriter extends MetadataWriter { return WORD_LENGTH + computeDataSize(new IFD(directory)) + directory.size() * ENTRY_LENGTH; } + public long computeIFDOffsetSize(final Collection directory) { + return computeDataSize(new IFD(directory)) + LONGWORD_LENGTH; + } + private long computeDataSize(final Directory directory) { long dataSize = 0; diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java index ead830c5..f26adcb3 100644 --- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java +++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java @@ -209,9 +209,8 @@ public final class TIFFImageWriter extends ImageWriterBase { EXIFWriter exifWriter = new EXIFWriter(); exifWriter.writeTIFFHeader(imageOutput); long IFD0Pos = imageOutput.getStreamPosition(); - imageOutput.writeInt(0); // IFD0 pointer, will be updated later - writePage(image, param, exifWriter, IFD0Pos); + imageOutput.writeInt(0); // EOF imageOutput.flush(); } @@ -363,24 +362,15 @@ public final class TIFFImageWriter extends ImageWriterBase { long nextIFDPointer = -1; long stripOffset = -1; long stripByteCount = 0; + if (compression == TIFFBaseline.COMPRESSION_NONE) { - // This implementation, allows semi-streaming-compatible uncompressed TIFFs - long streamOffset = imageOutput.getStreamPosition() + exifWriter.computeIFDSize(entries.values()) + 4; // IFD + 4 byte EOF, (header, ifd0 + n images already written) - - entries.remove(dummyStripByteCounts); - entries.put(TIFF.TAG_STRIP_BYTE_COUNTS, new TIFFEntry(TIFF.TAG_STRIP_BYTE_COUNTS, - renderedImage.getWidth() * renderedImage.getHeight() * numComponents)); - entries.remove(dummyStripOffsets); - entries.put(TIFF.TAG_STRIP_OFFSETS, new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, streamOffset)); - - long idfOffset = exifWriter.writeIFD(entries.values(), imageOutput); // NOTE: Writer takes case of ordering tags - nextIFDPointer = imageOutput.getStreamPosition(); - // Update IFD0 pointer - imageOutput.seek(lastIFDPointer); - imageOutput.writeInt((int) idfOffset); - imageOutput.seek(nextIFDPointer); - imageOutput.flush(); - imageOutput.writeInt(0); // EOF + long ifdOffset = exifWriter.computeIFDOffsetSize(entries.values()); + long dataLength = renderedImage.getWidth() * renderedImage.getHeight() * numComponents; + long pointerPos = imageOutput.getStreamPosition() + dataLength + 4 + ifdOffset; + imageOutput.writeInt((int) pointerPos); + } + else { + imageOutput.writeInt(0); // Update IFD Pointer later } stripOffset = imageOutput.getStreamPosition(); @@ -418,12 +408,20 @@ public final class TIFFImageWriter extends ImageWriterBase { long idfOffset = exifWriter.writeIFD(entries.values(), imageOutput); // NOTE: Writer takes case of ordering tags nextIFDPointer = imageOutput.getStreamPosition(); - // Update IFD0 pointer imageOutput.seek(lastIFDPointer); imageOutput.writeInt((int) idfOffset); imageOutput.seek(nextIFDPointer); imageOutput.flush(); - imageOutput.writeInt(0); // EOF + } + else { + entries.remove(dummyStripOffsets); + entries.put(TIFF.TAG_STRIP_OFFSETS, new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, stripOffset)); + entries.remove(dummyStripByteCounts); + entries.put(TIFF.TAG_STRIP_BYTE_COUNTS, new TIFFEntry(TIFF.TAG_STRIP_BYTE_COUNTS, stripByteCount)); + + exifWriter.writeIFD(entries.values(), imageOutput); // NOTE: Writer takes case of ordering tags + nextIFDPointer = imageOutput.getStreamPosition(); + imageOutput.flush(); } return nextIFDPointer; @@ -882,7 +880,6 @@ public final class TIFFImageWriter extends ImageWriterBase { sequenceExifWriter = new EXIFWriter(); sequenceExifWriter.writeTIFFHeader(imageOutput); sequenceLastIFDPos = imageOutput.getStreamPosition(); - imageOutput.writeInt(0); // IFD0 } @Override @@ -898,7 +895,7 @@ public final class TIFFImageWriter extends ImageWriterBase { if (!isWritingSequence) { throw new IllegalStateException("prepareWriteSequence() must be called before endWriteSequence()!"); } - + imageOutput.writeInt(0); // EOF isWritingSequence = false; sequenceExifWriter = null; sequenceLastIFDPos = -1;