diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/EXIFReader.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/EXIFReader.java
index 276cb873..8497da35 100644
--- a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/EXIFReader.java
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/EXIFReader.java
@@ -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);
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/TIFF.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/TIFF.java
index 361ba3b7..b60e2955 100644
--- a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/TIFF.java
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/exif/TIFF.java
@@ -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;
diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/G31DDecoder.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/G31DDecoder.java
new file mode 100644
index 00000000..cb02dd9b
--- /dev/null
+++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/G31DDecoder.java
@@ -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 Harald Kuhr
+ * @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
+ }
+}
diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/LZWDecoder.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/LZWDecoder.java
index 63af0f08..a8cf4617 100644
--- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/LZWDecoder.java
+++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/LZWDecoder.java
@@ -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;
}
diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFBaseline.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFBaseline.java
index c96316e8..3cc3e5f6 100644
--- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFBaseline.java
+++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFBaseline.java
@@ -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;
}
diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFCustom.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFCustom.java
index 67a07059..5f452b57 100644
--- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFCustom.java
+++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFCustom.java
@@ -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;
}
diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFExtension.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFExtension.java
index 44f9e9d7..4ee2b28e 100644
--- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFExtension.java
+++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFExtension.java
@@ -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;
}
diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java
index 2491f377..d45d73e5 100644
--- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java
+++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java
@@ -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).
+ *
+ * The reader is supposed to be fully "Baseline TIFF" compliant, and supports the following image types:
+ *
+ * - Class B (Bi-level), all relevant compression types, 1 bit per sample
+ * - Class G (Gray), all relevant compression types, 2, 4, 8, 16 or 32 bits per sample, unsigned integer
+ * - Class P (Palette/indexed color), all relevant compression types, 1, 2, 4, 8 or 16 bits per sample, unsigned integer
+ * - Class R (RGB), all relevant compression types, 8 or 16 bits per sample, unsigned integer
+ *
+ * In addition, it supports many common TIFF extensions such as:
+ *
+ * - Tiling
+ * - LZW Compression (type 5)
+ * - JPEG Compression (type 7)
+ * - ZLib (aka Adobe-style Deflate) Compression (type 8)
+ * - Deflate Compression (type 32946)
+ * - Horizontal differencing Predictor (type 2) for LZW, ZLib, Deflate and PackBits compression
+ * - Alpha channel (ExtraSamples type 1/Associated Alpha)
+ * - CMYK data (PhotometricInterpretation type 5/Separated)
+ * - YCbCr data (PhotometricInterpretation type 6/YCbCr) for JPEG
+ * - Planar data (PlanarConfiguration type 2/Planar)
+ * - ICC profiles (ICCProfile)
+ * - BitsPerSample values up to 16 for most PhotometricInterpretations
+ *
*
* @see Adobe TIFF developer resources
* @see Wikipedia
@@ -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);