TMI-TIFF: More format support, more lenient TIFF (EXIF) parsing.

This commit is contained in:
Harald Kuhr 2012-06-21 17:05:33 +02:00
parent 9ef8ac9930
commit 92690e1644
8 changed files with 225 additions and 103 deletions

View File

@ -240,7 +240,7 @@ public final class EXIFReader extends MetadataReader {
long offset = pInput.getStreamPosition() - 8l;
System.err.printf("Bad EXIF");
System.err.println("tagId: " + tagId + (tagId <= 0 ? "(INVALID)" : ""));
System.err.println("tagId: " + tagId + (tagId <= 0 ? " (INVALID)" : ""));
System.err.println("type: " + type + " (INVALID)");
System.err.println("count: " + count);

View File

@ -101,6 +101,7 @@ public interface TIFF {
int TAG_ORIENTATION = 274;
int TAG_SAMPLES_PER_PIXELS = 277;
int TAG_PLANAR_CONFIGURATION = 284;
int TAG_SAMPLE_FORMAT = 339;
int TAG_YCBCR_SUB_SAMPLING = 530;
int TAG_YCBCR_POSITIONING = 531;
int TAG_X_RESOLUTION = 282;

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2012, 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 "TwelveMonkeys" 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 OWNER 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.plugins.tiff;
import com.twelvemonkeys.io.enc.Decoder;
import java.io.IOException;
import java.io.InputStream;
/**
* CCITT Group 3 One-Dimensional (G31D) "No EOLs" Decoder.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: G31DDecoder.java,v 1.0 23.05.12 15:55 haraldk Exp$
*/
final class G31DDecoder implements Decoder {
public int decode(final InputStream stream, final byte[] buffer) throws IOException {
throw new UnsupportedOperationException("Method decode not implemented"); // TODO: Implement
}
}

View File

@ -81,7 +81,6 @@ final class LZWDecoder implements Decoder {
this(false);
}
private int maxCodeFor(final int bits) {
return reverseBitOrder ? (1 << bits) - 2 : (1 << bits) - 1;
}
@ -142,7 +141,7 @@ final class LZWDecoder implements Decoder {
return bufferPos;
}
private byte[] concatenate(final byte[] string, final byte firstChar) {
private static byte[] concatenate(final byte[] string, final byte firstChar) {
byte[] result = Arrays.copyOf(string, string.length + 1);
result[string.length] = firstChar;
@ -172,7 +171,7 @@ final class LZWDecoder implements Decoder {
}
}
private int writeString(final byte[] string, final byte[] buffer, final int bufferPos) {
private static int writeString(final byte[] string, final byte[] buffer, final int bufferPos) {
if (string.length == 0) {
return 0;
}

View File

@ -46,14 +46,13 @@ interface TIFFBaseline {
int PHOTOMETRIC_PALETTE = 3;
int PHOTOMETRIC_MASK = 4;
int SAMPLEFORMAT_UINT = 1;
int SAMPLEFORMAT_INT = 2;
int SAMPLEFORMAT_FP = 3;
int SAMPLEFORMAT_UNDEFINED = 4;
int SAMPLEFORMAT_UINT = 1; // Spec says only UINT required for baseline
int PLANARCONFIG_CHUNKY = 1;
int EXTRASAMPLE_UNSPECIFIED = 0;
int EXTRASAMPLE_ASSOCALPHA = 1;
int EXTRASAMPLE_UNASSALPHA = 2;
int EXTRASAMPLE_ASSOCIATED_ALPHA = 1;
int EXTRASAMPLE_UNASSOCIATED_ALPHA = 2;
int PREDICTOR_NONE = 1;
}

View File

@ -36,6 +36,21 @@ package com.twelvemonkeys.imageio.plugins.tiff;
* @version $Id: TIFFCustom.java,v 1.0 10.05.12 17:35 haraldk Exp$
*/
interface TIFFCustom {
int COMPRESSION_NEXT = 32766;
int COMPRESSION_CCITTRLEW = 32771;
int COMPRESSION_THUNDERSCAN = 32809;
int COMPRESSION_IT8CTPAD = 32895;
int COMPRESSION_IT8LW = 32896;
int COMPRESSION_IT8MP = 32897;
int COMPRESSION_IT8BL = 32898;
int COMPRESSION_PIXARFILM = 32908;
int COMPRESSION_PIXARLOG = 32909;
int COMPRESSION_DCS = 32947;
int COMPRESSION_JBIG = 34661;
int COMPRESSION_SGILOG = 34676;
int COMPRESSION_SGILOG24 = 34677;
int COMPRESSION_JP2000 = 34712;
int PHOTOMETRIC_LOGL = 32844;
int PHOTOMETRIC_LOGLUV = 32845;
@ -44,6 +59,6 @@ interface TIFFCustom {
/** DNG: LinearRaw*/
int PHOTOMETRIC_LINEAR_RAW = 34892;
int SAMPLEFORMAT_COMPLEXINT = 5;
int SAMPLEFORMAT_COMPLEXIEEEFP = 6;
int SAMPLEFORMAT_COMPLEX_INT = 5;
int SAMPLEFORMAT_COMPLEX_IEEE_FP = 6;
}

View File

@ -42,7 +42,7 @@ interface TIFFExtension {
int COMPRESSION_CCITT_T6 = 4;
/** LZW Compression. Was baseline, but moved to extension due to license issues in the LZW algorithm. */
int COMPRESSION_LZW = 5;
/** Deprecated. */
/** Deprecated. For backwards compatibility only. */
int COMPRESSION_OLD_JPEG = 6;
/** JPEG Compression (lossy). */
int COMPRESSION_JPEG = 7;
@ -51,34 +51,6 @@ interface TIFFExtension {
/** Adobe-style Deflate. */
int COMPRESSION_ZLIB = 8;
/*
LibTIFF:
COMPRESSION_NONE = 1;
COMPRESSION_CCITTRLE = 2;
COMPRESSION_CCITTFAX3 = COMPRESSION_CCITT_T4 = 3;
COMPRESSION_CCITTFAX4 = COMPRESSION_CCITT_T6 = 4;
COMPRESSION_LZW = 5;
COMPRESSION_OJPEG = 6;
COMPRESSION_JPEG = 7;
COMPRESSION_NEXT = 32766;
COMPRESSION_CCITTRLEW = 32771;
COMPRESSION_PACKBITS = 32773;
COMPRESSION_THUNDERSCAN = 32809;
COMPRESSION_IT8CTPAD = 32895;
COMPRESSION_IT8LW = 32896;
COMPRESSION_IT8MP = 32897;
COMPRESSION_IT8BL = 32898;
COMPRESSION_PIXARFILM = 32908;
COMPRESSION_PIXARLOG = 32909;
COMPRESSION_DEFLATE = 32946;
COMPRESSION_ADOBE_DEFLATE = 8;
COMPRESSION_DCS = 32947;
COMPRESSION_JBIG = 34661;
COMPRESSION_SGILOG = 34676;
COMPRESSION_SGILOG24 = 34677;
COMPRESSION_JP2000 = 34712;
*/
int PHOTOMETRIC_SEPARATED = 5;
int PHOTOMETRIC_YCBCR = 6;
int PHOTOMETRIC_CIELAB = 8;
@ -87,7 +59,10 @@ COMPRESSION_JP2000 = 34712;
int PLANARCONFIG_PLANAR = 2;
int PREDICTOR_NONE = 1;
int PREDICTOR_HORIZONTAL_DIFFERENCING = 2;
int PREDICTOR_HORIZONTAL_FLOATINGPOINT = 3;
int SAMPLEFORMAT_INT = 2;
int SAMPLEFORMAT_FP = 3;
int SAMPLEFORMAT_UNDEFINED = 4;
}

View File

@ -64,10 +64,32 @@ import java.util.Iterator;
import java.util.List;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import java.util.zip.ZipInputStream;
/**
* ImageReader implementation for Aldus/Adobe Tagged Image File Format (TIFF).
* <p/>
* The reader is supposed to be fully "Baseline TIFF" compliant, and supports the following image types:
* <ul>
* <li>Class B (Bi-level), all relevant compression types, 1 bit per sample</li>
* <li>Class G (Gray), all relevant compression types, 2, 4, 8, 16 or 32 bits per sample, unsigned integer</li>
* <li>Class P (Palette/indexed color), all relevant compression types, 1, 2, 4, 8 or 16 bits per sample, unsigned integer</li>
* <li>Class R (RGB), all relevant compression types, 8 or 16 bits per sample, unsigned integer</li>
* </ul>
* In addition, it supports many common TIFF extensions such as:
* <ul>
* <li>Tiling</li>
* <li>LZW Compression (type 5)</li>
* <li>JPEG Compression (type 7)</li>
* <li>ZLib (aka Adobe-style Deflate) Compression (type 8)</li>
* <li>Deflate Compression (type 32946)</li>
* <li>Horizontal differencing Predictor (type 2) for LZW, ZLib, Deflate and PackBits compression</li>
* <li>Alpha channel (ExtraSamples type 1/Associated Alpha)</li>
* <li>CMYK data (PhotometricInterpretation type 5/Separated)</li>
* <li>YCbCr data (PhotometricInterpretation type 6/YCbCr) for JPEG</li>
* <li>Planar data (PlanarConfiguration type 2/Planar)</li>
* <li>ICC profiles (ICCProfile)</li>
* <li>BitsPerSample values up to 16 for most PhotometricInterpretations</li>
* </ul>
*
* @see <a href="http://partners.adobe.com/public/developer/tiff/index.html">Adobe TIFF developer resources</a>
* @see <a href="http://en.wikipedia.org/wiki/Tagged_Image_File_Format">Wikipedia</a>
@ -78,16 +100,22 @@ import java.util.zip.ZipInputStream;
* @version $Id: TIFFImageReader.java,v 1.0 08.05.12 15:14 haraldk Exp$
*/
public class TIFFImageReader extends ImageReaderBase {
// TODO: Full BaseLine support
// TODO: Support ExtraSamples (an array!) (1: Associated Alpha (pre-multiplied), 2: Unassociated Alpha (non-multiplied)
// TODO: Handle SampleFormat (and give up if not == 1)
// TODOs ImageIO basic functionality:
// TODO: Subsampling (*tests should be failing*)
// TODO: Source region (*tests should be failing*)
// TODO: TIFFImageWriter + Spi
// TODOs ImageIO advanced functionality:
// TODO: Implement readAsRenderedImage to allow tiled renderImage?
// For some layouts, we could do reads super-fast with a memory mapped buffer.
// TODO: Implement readAsRaster directly
// TODOs Full BaseLine support:
// TODO: Support ExtraSamples (an array, if multiple extra samples!)
// (0: Unspecified (not alpha), 1: Associated Alpha (pre-multiplied), 2: Unassociated Alpha (non-multiplied)
// TODO: Support Compression 2 (CCITT Modified Huffman) for bi-level images
// TODO: ImageIO functionality
// TODO: Subsampling
// TODO: Source region
// TODO: Extension support
// TODOs Extension support
// TODO: Support PlanarConfiguration 2
// TODO: Support ICCProfile (fully)
// TODO: Support Compression 3 & 4 (CCITT T.4 & T.6)
@ -96,11 +124,11 @@ public class TIFFImageReader extends ImageReaderBase {
// TODO: Support Compression 34661 (JBIG)? Depends on JBIG ImageReader
// DONE:
// Delete the old Batik-based TIFFImageReader/Spi
// Handle SampleFormat (and give up if not == 1)
private final static boolean DEBUG = "true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.tiff.debug"));
private CompoundDirectory ifds;
private CompoundDirectory IFDs;
private Directory currentIFD;
TIFFImageReader(final TIFFImageReaderSpi provider) {
@ -109,7 +137,7 @@ public class TIFFImageReader extends ImageReaderBase {
@Override
protected void resetMembers() {
ifds = null;
IFDs = null;
currentIFD = null;
}
@ -118,16 +146,16 @@ public class TIFFImageReader extends ImageReaderBase {
throw new IllegalStateException("input not set");
}
if (ifds == null) {
ifds = (CompoundDirectory) new EXIFReader().read(imageInput); // NOTE: Sets byte order as a side effect
if (IFDs == null) {
IFDs = (CompoundDirectory) new EXIFReader().read(imageInput); // NOTE: Sets byte order as a side effect
if (DEBUG) {
for (int i = 0; i < ifds.directoryCount(); i++) {
System.err.printf("ifd[%d]: %s\n", i, ifds.getDirectory(i));
for (int i = 0; i < IFDs.directoryCount(); i++) {
System.err.printf("ifd[%d]: %s\n", i, IFDs.getDirectory(i));
}
System.err.println("Byte order: " + imageInput.getByteOrder());
System.err.println("numImages: " + ifds.directoryCount());
System.err.println("numImages: " + IFDs.directoryCount());
}
}
}
@ -135,13 +163,14 @@ public class TIFFImageReader extends ImageReaderBase {
private void readIFD(final int imageIndex) throws IOException {
readMetadata();
checkBounds(imageIndex);
currentIFD = ifds.getDirectory(imageIndex);
currentIFD = IFDs.getDirectory(imageIndex);
}
@Override
public int getNumImages(final boolean allowSearch) throws IOException {
readMetadata();
return ifds.directoryCount();
return IFDs.directoryCount();
}
private int getValueAsIntWithDefault(final int tag, String tagName, Integer defaultValue) throws IIOException {
@ -184,6 +213,7 @@ public class TIFFImageReader extends ImageReaderBase {
public ImageTypeSpecifier getRawImageType(int imageIndex) throws IOException {
readIFD(imageIndex);
getSampleFormat(); // We don't support anything but SAMPLEFORMAT_UINT at the moment, just sanity checking input
int planarConfiguration = getValueAsIntWithDefault(TIFF.TAG_PLANAR_CONFIGURATION, TIFFExtension.PLANARCONFIG_PLANAR);
int interpretation = getValueAsInt(TIFF.TAG_PHOTOMETRIC_INTERPRETATION, "PhotometricInterpretation");
int samplesPerPixel = getValueAsIntWithDefault(TIFF.TAG_SAMPLES_PER_PIXELS, 1);
@ -206,7 +236,7 @@ public class TIFFImageReader extends ImageReaderBase {
switch (samplesPerPixel) {
case 1:
// TIFF 6.0 Spec says: 1, 4 or 8 for baseline (1 for bi-level, 4/8 for gray)
// ImageTypeSpecifier supports only 1, 2, 4, 8 or 16 bits, we'll go with that for now
// ImageTypeSpecifier supports 1, 2, 4, 8 or 16 bits, we'll go with that for now
cs = profile == null ? ColorSpace.getInstance(ColorSpace.CS_GRAY) : ColorSpaces.createColorSpace(profile);
if (cs == ColorSpace.getInstance(ColorSpace.CS_GRAY) && (bitsPerSample == 1 || bitsPerSample == 2 || bitsPerSample == 4 || bitsPerSample == 8 || bitsPerSample == 16)) {
@ -216,13 +246,14 @@ public class TIFFImageReader extends ImageReaderBase {
return ImageTypeSpecifier.createInterleaved(cs, new int[] {0}, dataType, false, false);
}
default:
// TODO: If ExtraSamples is used, PlanarConfiguration must be taken into account also for gray data
throw new IIOException(String.format("Unsupported SamplesPerPixel/BitsPerSample combination for Bi-level/Gray TIFF (expected 1/1, 1/2, 1/4, 1/8 or 1/16): %d/%d", samplesPerPixel, bitsPerSample));
}
case TIFFExtension.PHOTOMETRIC_YCBCR:
// JPEG reader will handle YCbCr to RGB for us, we'll have to do it ourselves if not JPEG...
// TODO: Handle YCbCrSubsampling (up-scaler stream, or read data as-is + up-sample (sub-)raster after read? Apply smoothing?)
// TODO: We might want to handle USHORT_565 type, and allow different samplesPerPixel in that case especially
case TIFFBaseline.PHOTOMETRIC_RGB:
// RGB
cs = profile == null ? ColorSpace.getInstance(ColorSpace.CS_sRGB) : ColorSpaces.createColorSpace(profile);
@ -269,6 +300,7 @@ public class TIFFImageReader extends ImageReaderBase {
else if (bitsPerSample <= 0 || bitsPerSample > 16) {
throw new IIOException("Bad BitsPerSample value for Palette TIFF (expected <= 16): " + bitsPerSample);
}
// NOTE: If ExtraSamples is used, PlanarConfiguration must be taken into account also for pixel data
Entry colorMap = currentIFD.getEntryById(TIFF.TAG_COLOR_MAP);
if (colorMap == null) {
@ -291,11 +323,12 @@ public class TIFFImageReader extends ImageReaderBase {
case TIFFExtension.PHOTOMETRIC_SEPARATED:
// Separated (CMYK etc)
// TODO: Consult the 332/InkSet (1=CMYK, 2=Not CMYK; see InkNames), 334/NumberOfInks (def=4) and optionally 333/InkNames
// If "Not CMYK" we'll need an ICC profile to be able to display (in a useful way), readAsRaster should still work.
cs = profile == null ? ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK) : ColorSpaces.createColorSpace(profile);
switch (samplesPerPixel) {
case 4:
// TODO: Consult the 332/InkSet (1=CMYK, 2=Not CMYK; see InkNames), 334/NumberOfInks (def=4) and optionally 333/InkNames
if (bitsPerSample == 8 || bitsPerSample == 16) {
switch (planarConfiguration) {
case TIFFBaseline.PLANARCONFIG_CHUNKY:
@ -322,6 +355,27 @@ public class TIFFImageReader extends ImageReaderBase {
}
}
private int getSampleFormat() throws IIOException {
long[] value = getValueAsLongArray(TIFF.TAG_SAMPLE_FORMAT, "SampleFormat", false);
if (value != null) {
long sampleFormat = value[0];
for (int i = 1; i < value.length; i++) {
if (value[i] != sampleFormat) {
throw new IIOException("Variable TIFF SampleFormat not supported: " + Arrays.toString(value));
}
}
if (sampleFormat != TIFFBaseline.SAMPLEFORMAT_UINT) {
throw new IIOException("Unsupported TIFF SampleFormat (expected 1/Unsigned Integer): " + sampleFormat);
}
}
// The default, and the only value we support
return TIFFBaseline.SAMPLEFORMAT_UINT;
}
private int getBitsPerSample() throws IIOException {
long[] value = getValueAsLongArray(TIFF.TAG_BITS_PER_SAMPLE, "BitsPerSample", false);
@ -331,11 +385,9 @@ public class TIFFImageReader extends ImageReaderBase {
else {
int bitsPerSample = (int) value[0];
if (value.length > 1) {
for (long bps : value) {
if (bps != bitsPerSample) {
throw new IIOException("Varying BitsPerSample not supported: " + Arrays.toString(value));
}
for (int i = 1; i < value.length; i++) {
if (value[i] != bitsPerSample) {
throw new IIOException("Variable BitsPerSample not supported: " + Arrays.toString(value));
}
}
@ -352,6 +404,18 @@ public class TIFFImageReader extends ImageReaderBase {
// TODO: Based on raw type, we can probably convert to most RGB types at least, maybe gray etc
// TODO: Planar to chunky by default
if (!rawType.getColorModel().getColorSpace().isCS_sRGB() && rawType.getColorModel().getColorSpace().getType() == ColorSpace.TYPE_RGB) {
if (rawType.getNumBands() == 3 && rawType.getBitsPerBand(0) == 8) {
specs.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR));
specs.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_BGR));
specs.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB));
}
else if (rawType.getNumBands() == 4 && rawType.getBitsPerBand(0) == 8) {
specs.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR));
specs.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB));
specs.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR_PRE));
}
}
specs.add(rawType);
@ -431,6 +495,8 @@ public class TIFFImageReader extends ImageReaderBase {
case TIFFExtension.COMPRESSION_ZLIB:
// 'Adobe-style' Deflate
// TODO: Read only tiles that lies within region
// General uncompressed/compressed reading
for (int y = 0; y < tilesDown; y++) {
int col = 0;
@ -482,6 +548,7 @@ public class TIFFImageReader extends ImageReaderBase {
case TIFFExtension.COMPRESSION_JPEG:
// JPEG ('new-style' JPEG)
// TODO: Refactor all JPEG reading out to separate JPEG support class?
// TIFF is strictly ISO JPEG, so we should probably stick to the standard reader
ImageReader jpegReader = new JPEGImageReader(getOriginatingProvider());
@ -500,8 +567,8 @@ public class TIFFImageReader extends ImageReaderBase {
jpegReader.setInput(new ByteArrayImageInputStream(tablesValue));
// NOTE: This initializes the tables AND MORE for the reader (as if by magic).
// This is probably a bug, as later setInput calls should clear/override the tables
// NOTE: This initializes the tables AND MORE secret internal settings for the reader (as if by magic).
// This is probably a bug, as later setInput calls should clear/override the tables.
// However, it would be extremely convenient, not having to actually fiddle with the stream meta data (as below)
/*IIOMetadata streamMetadata = */jpegReader.getStreamMetadata();
@ -568,7 +635,8 @@ public class TIFFImageReader extends ImageReaderBase {
jpegParam.setSourceRegion(new Rectangle(0, 0, colsInTile, rowsInTile));
jpegParam.setDestinationOffset(new Point(col, row));
jpegParam.setDestination(destination);
// TODO: This works only if Gray/YCbCr/RGB, not CMYK...
// TODO: This works only if Gray/YCbCr/RGB, not CMYK/LAB/etc...
// In the latter case we will have to use readAsRaster
jpegReader.read(0, jpegParam);
}
finally {
@ -596,14 +664,13 @@ public class TIFFImageReader extends ImageReaderBase {
case TIFFBaseline.COMPRESSION_CCITT_HUFFMAN:
// CCITT modified Huffman
// Additionally, the specification defines these values as part of the TIFF extensions:
// Additionally, the specification defines these values as part of the TIFF extensions:
case TIFFExtension.COMPRESSION_CCITT_T4:
// CCITT Group 3 fax encoding
// CCITT Group 3 fax encoding
case TIFFExtension.COMPRESSION_CCITT_T6:
// CCITT Group 4 fax encoding
case TIFFExtension.COMPRESSION_OLD_JPEG:
// JPEG ('old-style' JPEG, later overridden in Technote2)
// JPEG ('old-style' JPEG, later overridden in Technote2)
throw new IIOException("Unsupported TIFF Compression value: " + compression);
default:
@ -625,27 +692,33 @@ public class TIFFImageReader extends ImageReaderBase {
for (int j = 0; j < rowsInStrip; j++) {
int row = startRow + j;
// input.readFully(rowData);
for (int k = 0; k < rowData.length; k++) {
try {
rowData[k] = input.readByte();
}
catch (IOException e) {
Arrays.fill(rowData, k, rowData.length, (byte) -1);
System.err.printf("Unexpected EOF or bad data at [%d %d]\n", col + k, row);
break;
}
if (row >= raster.getHeight()) {
break;
}
input.readFully(rowData);
// for (int k = 0; k < rowData.length; k++) {
// try {
// rowData[k] = input.readByte();
// }
// catch (IOException e) {
// Arrays.fill(rowData, k, rowData.length, (byte) -1);
// System.err.printf("Unexpected EOF or bad data at [%d %d]\n", col + k, row);
// break;
// }
// }
unPredict(predictor, colsInStrip, 1, numBands, rowData);
normalizeBlack(interpretation, rowData);
if (colsInStrip == rowRaster.getWidth()) {
if (colsInStrip == rowRaster.getWidth() && col + colsInStrip <= raster.getWidth()) {
raster.setDataElements(col, row, rowRaster);
}
else {
raster.setDataElements(col, row, rowRaster.createChild(0, 0, colsInStrip, 1, 0, 0, null));
else if (col >= raster.getMinX() && col < raster.getWidth()) {
raster.setDataElements(col, row, rowRaster.createChild(0, 0, Math.min(colsInStrip, raster.getWidth() - col), 1, 0, 0, null));
}
// Else skip data
}
break;
@ -654,19 +727,24 @@ public class TIFFImageReader extends ImageReaderBase {
for (int j = 0; j < rowsInStrip; j++) {
int row = startRow + j;
if (row >= raster.getHeight()) {
break;
}
for (int k = 0; k < rowDataShort.length; k++) {
rowDataShort[k] = input.readShort();
}
// TODO: Not sure how this works for USHORT... unpredict on byte level? In that case, we'll have to rewrite...
unPredict(predictor, colsInStrip, 1, numBands, rowDataShort);
normalizeBlack(interpretation, rowDataShort);
if (colsInStrip == rowRaster.getWidth()) {
if (colsInStrip == rowRaster.getWidth() && col + colsInStrip <= raster.getWidth()) {
raster.setDataElements(col, row, rowRaster);
}
else {
raster.setDataElements(col, row, rowRaster.createChild(0, 0, colsInStrip, 1, 0, 0, null));
else if (col >= raster.getMinX() && col < raster.getWidth()) {
raster.setDataElements(col, row, rowRaster.createChild(0, 0, Math.min(colsInStrip, raster.getWidth() - col), 1, 0, 0, null));
}
// Else skip data
}
break;
@ -675,19 +753,24 @@ public class TIFFImageReader extends ImageReaderBase {
for (int j = 0; j < rowsInStrip; j++) {
int row = startRow + j;
if (row >= raster.getHeight()) {
break;
}
for (int k = 0; k < rowDataInt.length; k++) {
rowDataInt[k] = input.readInt();
}
// TODO: Not sure how this works for USHORT... unpredict on byte level? In that case, we'll have to rewrite...
// unPredict(predictor, colsInStrip, 1, numBands, rowDataInt);
unPredict(predictor, colsInStrip, 1, numBands, rowDataInt);
normalizeBlack(interpretation, rowDataInt);
if (colsInStrip == rowRaster.getWidth()) {
if (colsInStrip == rowRaster.getWidth() && col + colsInStrip <= raster.getWidth()) {
raster.setDataElements(col, row, rowRaster);
}
else {
raster.setDataElements(col, row, rowRaster.createChild(0, 0, colsInStrip, 1, 0, 0, null));
else if (col >= raster.getMinX() && col < raster.getWidth()) {
raster.setDataElements(col, row, rowRaster.createChild(0, 0, Math.min(colsInStrip, raster.getWidth() - col), 1, 0, 0, null));
}
// Else skip data
}
break;
@ -725,7 +808,7 @@ public class TIFFImageReader extends ImageReaderBase {
private void unPredict(final int predictor, int scanLine, int rows, int bands, int[] data) throws IIOException {
// See TIFF 6.0 Specification, Section 14: "Differencing Predictor", page 64.
switch (predictor) {
case TIFFExtension.PREDICTOR_NONE:
case TIFFBaseline.PREDICTOR_NONE:
break;
case TIFFExtension.PREDICTOR_HORIZONTAL_DIFFERENCING:
// TODO: Implement
@ -740,7 +823,7 @@ public class TIFFImageReader extends ImageReaderBase {
private void unPredict(final int predictor, int scanLine, int rows, int bands, short[] data) throws IIOException {
// See TIFF 6.0 Specification, Section 14: "Differencing Predictor", page 64.
switch (predictor) {
case TIFFExtension.PREDICTOR_NONE:
case TIFFBaseline.PREDICTOR_NONE:
break;
case TIFFExtension.PREDICTOR_HORIZONTAL_DIFFERENCING:
// TODO: Implement
@ -754,7 +837,7 @@ public class TIFFImageReader extends ImageReaderBase {
private void unPredict(final int predictor, int scanLine, int rows, final int bands, byte[] data) throws IIOException {
// See TIFF 6.0 Specification, Section 14: "Differencing Predictor", page 64.
switch (predictor) {
case TIFFExtension.PREDICTOR_NONE:
case TIFFBaseline.PREDICTOR_NONE:
break;
case TIFFExtension.PREDICTOR_HORIZONTAL_DIFFERENCING:
for (int y = 0; y < rows; y++) {
@ -782,9 +865,9 @@ public class TIFFImageReader extends ImageReaderBase {
case TIFFExtension.COMPRESSION_LZW:
return new DecoderStream(stream, new LZWDecoder(LZWDecoder.isOldBitReversedStream(stream)), 1024);
case TIFFExtension.COMPRESSION_ZLIB:
return new InflaterInputStream(stream, new Inflater(), 1024);
case TIFFExtension.COMPRESSION_DEFLATE:
return new ZipInputStream(stream);
// TIFFphotoshop.pdf (aka TIFF specification, supplement 2) says ZLIB (8) and DEFLATE (32946) algorithms are identical
return new InflaterInputStream(stream, new Inflater(), 1024);
default:
throw new IllegalArgumentException("Unsupported TIFF compression: " + compression);
}
@ -913,6 +996,9 @@ public class TIFFImageReader extends ImageReaderBase {
// }
long start = System.currentTimeMillis();
// param.setSourceRegion(new Rectangle(100, 100, 100, 100));
// param.setDestinationOffset(new Point(50, 150));
// param.setSourceSubsampling(2, 2, 0, 0);
BufferedImage image = reader.read(imageNo, param);
System.err.println("Read time: " + (System.currentTimeMillis() - start) + " ms");
// System.err.println("image: " + image);