diff --git a/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/SubImageOutputStream.java b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/SubImageOutputStream.java
new file mode 100644
index 00000000..372e5758
--- /dev/null
+++ b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/SubImageOutputStream.java
@@ -0,0 +1,55 @@
+package com.twelvemonkeys.imageio.stream;
+
+import javax.imageio.stream.ImageOutputStream;
+import javax.imageio.stream.ImageOutputStreamImpl;
+import java.io.IOException;
+
+/**
+ * SubImageOutputStream.
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: harald.kuhr$
+ * @version $Id: SubImageOutputStream.java,v 1.0 30/03/15 harald.kuhr Exp$
+ */
+public class SubImageOutputStream extends ImageOutputStreamImpl {
+ private final ImageOutputStream stream;
+
+ public SubImageOutputStream(final ImageOutputStream stream) {
+ this.stream = stream;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ stream.write(b);
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ stream.write(b, off, len);
+ }
+
+ @Override
+ public int read() throws IOException {
+ return stream.read();
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ return stream.read(b, off, len);
+ }
+
+ @Override
+ public boolean isCached() {
+ return stream.isCached();
+ }
+
+ @Override
+ public boolean isCachedMemory() {
+ return stream.isCachedMemory();
+ }
+
+ @Override
+ public boolean isCachedFile() {
+ return stream.isCachedFile();
+ }
+}
diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriteParam.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriteParam.java
index 69eba0c1..637ea6f3 100644
--- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriteParam.java
+++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriteParam.java
@@ -39,16 +39,20 @@ import java.util.Locale;
* @version $Id: TIFFImageWriteParam.java,v 1.0 18.09.13 12:47 haraldk Exp$
*/
public final class TIFFImageWriteParam extends ImageWriteParam {
- // TODO: Support no compression (None/1)
- // TODO: Support ZLIB (/Deflate) compression (8)
- // TODO: Support PackBits compression (32773)
- // TODO: Support JPEG compression (7)
- // TODO: Support CCITT Modified Huffman compression (2)
- // TODO: Support LZW compression (5)?
+ // TODO: Support CCITT Modified Huffman compression (2) BASELINE!!
+ // TODO: Support CCITT T.4 (3)
+ // TODO: Support CCITT T.6 (4)
// TODO: Support JBIG compression via ImageIO plugin/delegate?
// TODO: Support JPEG2000 compression via ImageIO plugin/delegate?
// TODO: Support tiling
- // TODO: Support predictor. See TIFF 6.0 Specification, Section 14: "Differencing Predictor", page 64.
+ // TODO: Support OPTIONAL predictor. See TIFF 6.0 Specification, Section 14: "Differencing Predictor", page 64.
+
+ // DONE:
+ // Support no compression (None/1)
+ // Support ZLIB (/Deflate) compression (8)
+ // Support PackBits compression (32773)
+ // Support LZW compression (5)?
+ // Support JPEG compression (7)
TIFFImageWriteParam() {
this(Locale.getDefault());
@@ -59,7 +63,12 @@ public final class TIFFImageWriteParam extends ImageWriteParam {
// NOTE: We use the same spelling/casing as the JAI equivalent to be as compatible as possible
// See: http://download.java.net/media/jai-imageio/javadoc/1.1/com/sun/media/imageio/plugins/tiff/TIFFImageWriteParam.html
- compressionTypes = new String[] {"None", /* "CCITT RLE", "CCITT T.4", "CCITT T.6", */ "LZW", "JPEG", "ZLib", "PackBits", "Deflate", /* "EXIF JPEG" */ };
+ compressionTypes = new String[] {
+ "None",
+ null, null, null,/* "CCITT RLE", "CCITT T.4", "CCITT T.6", */
+ "LZW", "JPEG", "ZLib", "PackBits", "Deflate",
+ null/* "EXIF JPEG" */ // A well-defined form of "Old-style JPEG", no tables/process, only 513 (offset) and 514 (length)
+ };
compressionType = compressionTypes[0];
canWriteCompressed = true;
}
@@ -102,6 +111,9 @@ public final class TIFFImageWriteParam extends ImageWriteParam {
else if (param.getCompressionType().equals("JPEG")) {
return TIFFExtension.COMPRESSION_JPEG;
}
+// else if (param.getCompressionType().equals("EXIF JPEG")) {
+// return TIFFExtension.COMPRESSION_OLD_JPEG;
+// }
throw new IllegalArgumentException(String.format("Unsupported compression type: %s", param.getCompressionType()));
}
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 93eac986..9f672cfb 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
@@ -35,6 +35,7 @@ import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.exif.EXIFWriter;
import com.twelvemonkeys.imageio.metadata.exif.Rational;
import com.twelvemonkeys.imageio.metadata.exif.TIFF;
+import com.twelvemonkeys.imageio.stream.SubImageOutputStream;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.io.enc.EncoderStream;
import com.twelvemonkeys.io.enc.PackBitsEncoder;
@@ -157,10 +158,11 @@ public final class TIFFImageWriter extends ImageWriterBase {
entries.add(new TIFFEntry(TIFF.TAG_EXTRA_SAMPLES, TIFFBaseline.EXTRASAMPLE_UNSPECIFIED));
}
}
+
// Write compression field from param or metadata
int compression = TIFFImageWriteParam.getCompressionType(param);
entries.add(new TIFFEntry(TIFF.TAG_COMPRESSION, compression));
- // TODO: Let param control
+ // TODO: Let param control predictor
switch (compression) {
case TIFFExtension.COMPRESSION_ZLIB:
case TIFFExtension.COMPRESSION_DEFLATE:
@@ -169,7 +171,9 @@ public final class TIFFImageWriter extends ImageWriterBase {
default:
}
- int photometric = getPhotometricInterpretation(colorModel);
+ int photometric = compression == TIFFExtension.COMPRESSION_JPEG ?
+ TIFFExtension.PHOTOMETRIC_YCBCR :
+ getPhotometricInterpretation(colorModel);
entries.add(new TIFFEntry(TIFF.TAG_PHOTOMETRIC_INTERPRETATION, photometric));
if (photometric == TIFFBaseline.PHOTOMETRIC_PALETTE && colorModel instanceof IndexColorModel) {
@@ -179,9 +183,9 @@ public final class TIFFImageWriter extends ImageWriterBase {
else {
entries.add(new TIFFEntry(TIFF.TAG_SAMPLES_PER_PIXEL, numComponents));
- // TODO: What is the default TIFF color space?
+ // Note: Assuming sRGB to be the default RGB interpretation
ColorSpace colorSpace = colorModel.getColorSpace();
- if (colorSpace instanceof ICC_ColorSpace) {
+ if (colorSpace instanceof ICC_ColorSpace && !colorSpace.isCS_sRGB()) {
entries.add(new TIFFEntry(TIFF.TAG_ICC_PROFILE, ((ICC_ColorSpace) colorSpace).getProfile().getData()));
}
}
@@ -228,8 +232,26 @@ public final class TIFFImageWriter extends ImageWriterBase {
}
// TODO: Create compressor stream per Tile/Strip
- // Write image data
- writeImageData(createCompressorStream(renderedImage, param), renderedImage, numComponents, bandOffsets, bitOffsets);
+ if (compression == TIFFExtension.COMPRESSION_JPEG) {
+ Iterator writers = ImageIO.getImageWritersByFormatName("JPEG");
+ if (!writers.hasNext()) {
+ // This can only happen if someone deliberately uninstalled it
+ throw new IIOException("No JPEG ImageWriter found!");
+ }
+
+ ImageWriter jpegWriter = writers.next();
+ try {
+ jpegWriter.setOutput(new SubImageOutputStream(imageOutput));
+ jpegWriter.write(renderedImage);
+ }
+ finally {
+ jpegWriter.dispose();
+ }
+ }
+ else {
+ // Write image data
+ writeImageData(createCompressorStream(renderedImage, param), renderedImage, numComponents, bandOffsets, bitOffsets);
+ }
// TODO: Update IFD0-pointer, and write IFD
if (compression != TIFFBaseline.COMPRESSION_NONE) {
@@ -304,7 +326,8 @@ public final class TIFFImageWriter extends ImageWriterBase {
output.length: 12600399
*/
- // TODO: Use predictor only by default for -PackBits,- LZW and ZLib/Deflate, unless explicitly disabled (ImageWriteParam)
+ // Use predictor by default for LZW and ZLib/Deflate
+ // TODO: Unless explicitly disabled in TIFFImageWriteParam
int compression = TIFFImageWriteParam.getCompressionType(param);
OutputStream stream;
@@ -321,7 +344,7 @@ public final class TIFFImageWriter extends ImageWriterBase {
case TIFFExtension.COMPRESSION_ZLIB:
case TIFFExtension.COMPRESSION_DEFLATE:
- int deflateSetting = Deflater.BEST_SPEED; // This is consistent with default compression quality being 1.0 and 0 meaning max compression....
+ int deflateSetting = Deflater.BEST_SPEED; // This is consistent with default compression quality being 1.0 and 0 meaning max compression...
if (param.getCompressionMode() == ImageWriteParam.MODE_EXPLICIT) {
// TODO: Determine how to interpret compression quality...
// Docs says:
@@ -740,4 +763,5 @@ public final class TIFFImageWriter extends ImageWriterBase {
TIFFImageReader.showIt(read, output.getName());
}
+
}