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: + *

+ * In addition, it supports many common TIFF extensions such as: + * * * @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);