mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-02 11:05:29 -04:00
TMI-TIFF: More format support, more lenient TIFF (EXIF) parsing.
This commit is contained in:
parent
9ef8ac9930
commit
92690e1644
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user