#626 TIFF CCITT detection only once per IFD

(cherry picked from commit 9d3f271867ebe2babfb7cb0ff1a3bc7f57b2e2b9)
This commit is contained in:
Harald Kuhr 2021-09-16 22:25:06 +02:00
parent b55c623b87
commit df068e350d
2 changed files with 62 additions and 45 deletions

View File

@ -30,14 +30,14 @@
package com.twelvemonkeys.imageio.plugins.tiff; package com.twelvemonkeys.imageio.plugins.tiff;
import com.twelvemonkeys.lang.Validate;
import java.io.EOFException; import java.io.EOFException;
import java.io.FilterInputStream; import java.io.FilterInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Arrays; import java.util.Arrays;
import com.twelvemonkeys.lang.Validate;
/** /**
* CCITT Modified Huffman RLE, Group 3 (T4) and Group 4 (T6) fax compression. * CCITT Modified Huffman RLE, Group 3 (T4) and Group 4 (T6) fax compression.
* *
@ -149,23 +149,23 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
this(stream, columns, type, fillOrder, options, type == TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE); this(stream, columns, type, fillOrder, options, type == TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE);
} }
static int findCompressionType(final int type, final InputStream in) throws IOException { static int findCompressionType(final int encodedType, final InputStream stream) throws IOException {
// Discover possible incorrect type, revert to RLE // Discover possible incorrect compression type, revert to RLE if no EOLs found
if (type == TIFFExtension.COMPRESSION_CCITT_T4 && in.markSupported()) { if (encodedType == TIFFExtension.COMPRESSION_CCITT_T4 && stream.markSupported()) {
int limit = 500; int limit = 512;
try { try {
in.mark(limit); stream.mark(limit);
int first = in.read(); int first = stream.read();
int second = in.read(); int second = stream.read();
if (first == -1 || second == -1) { if (first == -1 || second == -1) {
// stream to short // stream to short
return type; return encodedType;
} }
else if (first == 0 && (((byte) second) >> 4 == 1 || ((byte) second) == 1)) { else if (first == 0 && (((byte) second) >> 4 == 1 || ((byte) second) == 1)) {
// correct, starts with EOL or byte aligned EOL // correct, starts with EOL or byte aligned EOL
return type; return encodedType;
} }
short b = (short) (((((byte) first) << 8) + ((byte) second)) >> 4); short b = (short) (((((byte) first) << 8) + ((byte) second)) >> 4);
int limitBits = limit * 8; int limitBits = limit * 8;
@ -173,7 +173,7 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
byte streamByte = (byte) read; byte streamByte = (byte) read;
for (int i = 12; i < limitBits; i++) { for (int i = 12; i < limitBits; i++) {
if (i % 8 == 0) { if (i % 8 == 0) {
read = in.read(); read = stream.read();
if (read == -1) { if (read == -1) {
// no EOL before stream end // no EOL before stream end
return TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE; return TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE;
@ -192,11 +192,11 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
return TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE; return TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE;
} }
finally { finally {
in.reset(); stream.reset();
} }
} }
return type; return encodedType;
} }
private void fetch() throws IOException { private void fetch() throws IOException {
@ -212,7 +212,7 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
throw e; throw e;
} }
// ..otherwise, just let client code try to read past the // ...otherwise, just let client code try to read past the
// end of stream // end of stream
decodedLength = -1; decodedLength = -1;
} }

View File

@ -30,6 +30,45 @@
package com.twelvemonkeys.imageio.plugins.tiff; package com.twelvemonkeys.imageio.plugins.tiff;
import static com.twelvemonkeys.imageio.util.IIOUtil.createStreamAdapter;
import static com.twelvemonkeys.imageio.util.IIOUtil.subsampleRow;
import static java.util.Arrays.asList;
import java.awt.*;
import java.awt.color.CMMException;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.image.*;
import java.io.*;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.event.IIOReadWarningListener;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.plugins.jpeg.JPEGImageReadParam;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.twelvemonkeys.imageio.ImageReaderBase; import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.color.CIELabColorConverter; import com.twelvemonkeys.imageio.color.CIELabColorConverter;
import com.twelvemonkeys.imageio.color.CIELabColorConverter.Illuminant; import com.twelvemonkeys.imageio.color.CIELabColorConverter.Illuminant;
@ -60,35 +99,6 @@ import com.twelvemonkeys.io.enc.PackBitsDecoder;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.xml.XMLSerializer; import com.twelvemonkeys.xml.XMLSerializer;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.imageio.*;
import javax.imageio.event.IIOReadWarningListener;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.plugins.jpeg.JPEGImageReadParam;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.color.CMMException;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.image.*;
import java.io.*;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import static com.twelvemonkeys.imageio.util.IIOUtil.createStreamAdapter;
import static com.twelvemonkeys.imageio.util.IIOUtil.subsampleRow;
import static java.util.Arrays.asList;
/** /**
* ImageReader implementation for Aldus/Adobe Tagged Image File Format (TIFF). * ImageReader implementation for Aldus/Adobe Tagged Image File Format (TIFF).
* <p> * <p>
@ -164,6 +174,7 @@ public final class TIFFImageReader extends ImageReaderBase {
private CompoundDirectory IFDs; private CompoundDirectory IFDs;
private Directory currentIFD; private Directory currentIFD;
private int overrideCCITTCompression = -1;
TIFFImageReader(final ImageReaderSpi provider) { TIFFImageReader(final ImageReaderSpi provider) {
super(provider); super(provider);
@ -173,6 +184,7 @@ public final class TIFFImageReader extends ImageReaderBase {
protected void resetMembers() { protected void resetMembers() {
IFDs = null; IFDs = null;
currentIFD = null; currentIFD = null;
overrideCCITTCompression = -1;
} }
private void readMetadata() throws IOException { private void readMetadata() throws IOException {
@ -377,6 +389,7 @@ public final class TIFFImageReader extends ImageReaderBase {
readMetadata(); readMetadata();
checkBounds(imageIndex); checkBounds(imageIndex);
currentIFD = IFDs.getDirectory(imageIndex); currentIFD = IFDs.getDirectory(imageIndex);
overrideCCITTCompression = -1; // Reset override for next image
} }
@Override @Override
@ -2330,7 +2343,11 @@ public final class TIFFImageReader extends ImageReaderBase {
case TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE: case TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE:
case TIFFExtension.COMPRESSION_CCITT_T4: case TIFFExtension.COMPRESSION_CCITT_T4:
case TIFFExtension.COMPRESSION_CCITT_T6: case TIFFExtension.COMPRESSION_CCITT_T6:
return new CCITTFaxDecoderStream(stream, width, findCCITTType(compression, stream), fillOrder, getCCITTOptions(compression), compression == TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE); // TODO: Find a better way to test for incorrect CCITT type ONCE per IFD
if (overrideCCITTCompression == -1) {
overrideCCITTCompression = findCCITTType(compression, stream);
}
return new CCITTFaxDecoderStream(stream, width, overrideCCITTCompression, fillOrder, getCCITTOptions(compression), compression == TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE);
default: default:
throw new IllegalArgumentException("Unsupported TIFF compression: " + compression); throw new IllegalArgumentException("Unsupported TIFF compression: " + compression);
} }