TMI-26: TIFF writer support with JPEG encoder

This commit is contained in:
Harald Kuhr 2015-03-30 14:21:36 +02:00
parent b0e667e683
commit 47a18c63d7
3 changed files with 107 additions and 16 deletions

View File

@ -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 <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @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();
}
}

View File

@ -39,16 +39,20 @@ import java.util.Locale;
* @version $Id: TIFFImageWriteParam.java,v 1.0 18.09.13 12:47 haraldk Exp$ * @version $Id: TIFFImageWriteParam.java,v 1.0 18.09.13 12:47 haraldk Exp$
*/ */
public final class TIFFImageWriteParam extends ImageWriteParam { public final class TIFFImageWriteParam extends ImageWriteParam {
// TODO: Support no compression (None/1) // TODO: Support CCITT Modified Huffman compression (2) BASELINE!!
// TODO: Support ZLIB (/Deflate) compression (8) // TODO: Support CCITT T.4 (3)
// TODO: Support PackBits compression (32773) // TODO: Support CCITT T.6 (4)
// TODO: Support JPEG compression (7)
// TODO: Support CCITT Modified Huffman compression (2)
// TODO: Support LZW compression (5)?
// TODO: Support JBIG compression via ImageIO plugin/delegate? // TODO: Support JBIG compression via ImageIO plugin/delegate?
// TODO: Support JPEG2000 compression via ImageIO plugin/delegate? // TODO: Support JPEG2000 compression via ImageIO plugin/delegate?
// TODO: Support tiling // 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() { TIFFImageWriteParam() {
this(Locale.getDefault()); 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 // 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 // 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]; compressionType = compressionTypes[0];
canWriteCompressed = true; canWriteCompressed = true;
} }
@ -102,6 +111,9 @@ public final class TIFFImageWriteParam extends ImageWriteParam {
else if (param.getCompressionType().equals("JPEG")) { else if (param.getCompressionType().equals("JPEG")) {
return TIFFExtension.COMPRESSION_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())); throw new IllegalArgumentException(String.format("Unsupported compression type: %s", param.getCompressionType()));
} }

View File

@ -35,6 +35,7 @@ import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.exif.EXIFWriter; import com.twelvemonkeys.imageio.metadata.exif.EXIFWriter;
import com.twelvemonkeys.imageio.metadata.exif.Rational; import com.twelvemonkeys.imageio.metadata.exif.Rational;
import com.twelvemonkeys.imageio.metadata.exif.TIFF; import com.twelvemonkeys.imageio.metadata.exif.TIFF;
import com.twelvemonkeys.imageio.stream.SubImageOutputStream;
import com.twelvemonkeys.imageio.util.IIOUtil; import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.io.enc.EncoderStream; import com.twelvemonkeys.io.enc.EncoderStream;
import com.twelvemonkeys.io.enc.PackBitsEncoder; 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)); entries.add(new TIFFEntry(TIFF.TAG_EXTRA_SAMPLES, TIFFBaseline.EXTRASAMPLE_UNSPECIFIED));
} }
} }
// Write compression field from param or metadata // Write compression field from param or metadata
int compression = TIFFImageWriteParam.getCompressionType(param); int compression = TIFFImageWriteParam.getCompressionType(param);
entries.add(new TIFFEntry(TIFF.TAG_COMPRESSION, compression)); entries.add(new TIFFEntry(TIFF.TAG_COMPRESSION, compression));
// TODO: Let param control // TODO: Let param control predictor
switch (compression) { switch (compression) {
case TIFFExtension.COMPRESSION_ZLIB: case TIFFExtension.COMPRESSION_ZLIB:
case TIFFExtension.COMPRESSION_DEFLATE: case TIFFExtension.COMPRESSION_DEFLATE:
@ -169,7 +171,9 @@ public final class TIFFImageWriter extends ImageWriterBase {
default: 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)); entries.add(new TIFFEntry(TIFF.TAG_PHOTOMETRIC_INTERPRETATION, photometric));
if (photometric == TIFFBaseline.PHOTOMETRIC_PALETTE && colorModel instanceof IndexColorModel) { if (photometric == TIFFBaseline.PHOTOMETRIC_PALETTE && colorModel instanceof IndexColorModel) {
@ -179,9 +183,9 @@ public final class TIFFImageWriter extends ImageWriterBase {
else { else {
entries.add(new TIFFEntry(TIFF.TAG_SAMPLES_PER_PIXEL, numComponents)); 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(); 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())); 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 // TODO: Create compressor stream per Tile/Strip
if (compression == TIFFExtension.COMPRESSION_JPEG) {
Iterator<ImageWriter> 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 // Write image data
writeImageData(createCompressorStream(renderedImage, param), renderedImage, numComponents, bandOffsets, bitOffsets); writeImageData(createCompressorStream(renderedImage, param), renderedImage, numComponents, bandOffsets, bitOffsets);
}
// TODO: Update IFD0-pointer, and write IFD // TODO: Update IFD0-pointer, and write IFD
if (compression != TIFFBaseline.COMPRESSION_NONE) { if (compression != TIFFBaseline.COMPRESSION_NONE) {
@ -304,7 +326,8 @@ public final class TIFFImageWriter extends ImageWriterBase {
output.length: 12600399 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); int compression = TIFFImageWriteParam.getCompressionType(param);
OutputStream stream; OutputStream stream;
@ -321,7 +344,7 @@ public final class TIFFImageWriter extends ImageWriterBase {
case TIFFExtension.COMPRESSION_ZLIB: case TIFFExtension.COMPRESSION_ZLIB:
case TIFFExtension.COMPRESSION_DEFLATE: 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) { if (param.getCompressionMode() == ImageWriteParam.MODE_EXPLICIT) {
// TODO: Determine how to interpret compression quality... // TODO: Determine how to interpret compression quality...
// Docs says: // Docs says:
@ -740,4 +763,5 @@ public final class TIFFImageWriter extends ImageWriterBase {
TIFFImageReader.showIt(read, output.getName()); TIFFImageReader.showIt(read, output.getName());
} }
} }