mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-03 03:25:28 -04:00
TMI-TIFF: Getting close to full baseline support!
- Added Modified Huffman decoding (needs a proper test image) - Improved predictor support (16/32 bpp) - Fixed handling of bogus RowsPerStrip
This commit is contained in:
parent
cdce0aebff
commit
ff3fbc8bd2
@ -0,0 +1,452 @@
|
|||||||
|
/*
|
||||||
|
* 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.lang.Validate;
|
||||||
|
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.FilterInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CCITT Modified Huffman RLE, Group 3 (T4) and Group 4 (T6) fax compression.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by $Author: haraldk$
|
||||||
|
* @version $Id: CCITTFaxDecoderStream.java,v 1.0 23.05.12 15:55 haraldk Exp$
|
||||||
|
*/
|
||||||
|
final class CCITTFaxDecoderStream extends FilterInputStream {
|
||||||
|
// See TIFF 6.0 Specification, Section 10: "Modified Huffman Compression", page 43.
|
||||||
|
|
||||||
|
private final int columns;
|
||||||
|
private final byte[] decodedRow;
|
||||||
|
|
||||||
|
private int decodedLength;
|
||||||
|
private int decodedPos;
|
||||||
|
|
||||||
|
private int bitBuffer;
|
||||||
|
private int bitBufferLength;
|
||||||
|
|
||||||
|
// Need to take fill order into account (?) (use flip table?)
|
||||||
|
private final int fillOrder;
|
||||||
|
private final int type;
|
||||||
|
|
||||||
|
private final int[] changes;
|
||||||
|
private int changesCount;
|
||||||
|
|
||||||
|
private static final int EOL_CODE = 0x01; // 12 bit
|
||||||
|
|
||||||
|
public CCITTFaxDecoderStream(final InputStream stream, final int columns, final int type, final int fillOrder) {
|
||||||
|
super(Validate.notNull(stream, "stream"));
|
||||||
|
|
||||||
|
this.columns = Validate.isTrue(columns > 0, columns, "width must be greater than 0");
|
||||||
|
// We know this is only used for b/w (1 bit)
|
||||||
|
this.decodedRow = new byte[(columns + 7) / 8];
|
||||||
|
this.type = Validate.isTrue(type == TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE, type, "Only CCITT Modified Huffman RLE compression (2) supported: %s"); // TODO: Implement group 3 and 4
|
||||||
|
this.fillOrder = Validate.isTrue(fillOrder == 1, fillOrder, "Only fill order 1 supported: %s"); // TODO: Implement fillOrder == 2
|
||||||
|
|
||||||
|
this.changes = new int[columns];
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDEA: Would it be faster to keep all bit combos of each length (>=2) that is NOT a code, to find bit length, then look up value in table?
|
||||||
|
// -- If white run, start at 4 bits to determine length, if black, start at 2 bits
|
||||||
|
|
||||||
|
private void fetch() throws IOException {
|
||||||
|
if (decodedPos >= decodedLength) {
|
||||||
|
decodedLength = 0;
|
||||||
|
try {
|
||||||
|
decodeRow();
|
||||||
|
}
|
||||||
|
catch (EOFException e) {
|
||||||
|
// TODO: Rewrite to avoid throw/catch for normal flow...
|
||||||
|
if (decodedLength != 0) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ..otherwise, just client code trying to read past the end of stream
|
||||||
|
decodedLength = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
decodedPos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void decodeRow() throws IOException {
|
||||||
|
resetBuffer();
|
||||||
|
|
||||||
|
boolean literalRun = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (type == TIFFExtension.COMPRESSION_CCITT_T4) {
|
||||||
|
int eol = readBits(12);
|
||||||
|
System.err.println("eol: " + eol);
|
||||||
|
while (eol != EOL_CODE) {
|
||||||
|
eol = readBits(1);
|
||||||
|
System.err.println("eol: " + eol);
|
||||||
|
// throw new IOException("Missing EOL");
|
||||||
|
}
|
||||||
|
|
||||||
|
literalRun = readBits(1) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.err.println("literalRun: " + literalRun);
|
||||||
|
*/
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
if (literalRun) {
|
||||||
|
changesCount = 0;
|
||||||
|
boolean white = true;
|
||||||
|
|
||||||
|
do {
|
||||||
|
int completeRun = 0;
|
||||||
|
|
||||||
|
int run;
|
||||||
|
do {
|
||||||
|
if (white) {
|
||||||
|
run = decodeRun(WHITE_CODES, WHITE_RUN_LENGTHS, 4);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
run = decodeRun(BLACK_CODES, BLACK_RUN_LENGTHS, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
completeRun += run;
|
||||||
|
}
|
||||||
|
while (run >= 64); // Additional makeup codes are packed into both b/w codes, terminating codes are < 64 bytes
|
||||||
|
|
||||||
|
changes[changesCount++] = index + completeRun;
|
||||||
|
|
||||||
|
// System.err.printf("%s run: %d\n", white ? "white" : "black", run);
|
||||||
|
|
||||||
|
// TODO: Optimize with lookup for 0-7 bits?
|
||||||
|
// Fill bits to byte boundary...
|
||||||
|
while (index % 8 != 0 && completeRun-- > 0) {
|
||||||
|
decodedRow[index++ / 8] |= (white ? 1 << 8 - (index % 8) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...then fill complete bytes to either 0xff or 0x00...
|
||||||
|
if (index % 8 == 0) {
|
||||||
|
final byte value = (byte) (white ? 0xff : 0x00);
|
||||||
|
|
||||||
|
while (completeRun > 7) {
|
||||||
|
decodedRow[index / 8] = value;
|
||||||
|
completeRun -= 8;
|
||||||
|
index += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...finally fill any remaining bits
|
||||||
|
while (completeRun-- > 0) {
|
||||||
|
decodedRow[index++ / 8] |= (white ? 1 << 8 - (index % 8) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flip color for next run
|
||||||
|
white = !white;
|
||||||
|
}
|
||||||
|
while (index < columns);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// non-literal run
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE && index != columns) {
|
||||||
|
throw new IOException("Sum of run-lengths does not equal scan line width: " + index + " > " + columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
decodedLength = (index / 8) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int decodeRun(short[][] codes, short[][] runLengths, int minCodeSize) throws IOException {
|
||||||
|
// TODO: Optimize...
|
||||||
|
// Looping and comparing is the most straight-forward, but probably not the most effective way...
|
||||||
|
int code = readBits(minCodeSize);
|
||||||
|
|
||||||
|
for (int bits = 0; bits < codes.length; bits++) {
|
||||||
|
short[] bitCodes = codes[bits];
|
||||||
|
|
||||||
|
for (int i = 0; i < bitCodes.length; i++) {
|
||||||
|
if (bitCodes[i] == code) {
|
||||||
|
// System.err.println("code: " + code);
|
||||||
|
|
||||||
|
// Code found, return matching run length
|
||||||
|
return runLengths[bits][i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No code found, read one more bit and try again
|
||||||
|
code = fillOrder == 1 ? (code << 1) | readBits(1) : readBits(1) << (bits + minCodeSize) | code;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IOException("Unknown code in Huffman RLE stream");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetBuffer() {
|
||||||
|
for (int i = 0; i < decodedRow.length; i++) {
|
||||||
|
decodedRow[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitBuffer = 0;
|
||||||
|
bitBufferLength = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int readBits(int bitCount) throws IOException {
|
||||||
|
while (bitBufferLength < bitCount) {
|
||||||
|
int read = in.read();
|
||||||
|
if (read == -1) {
|
||||||
|
throw new EOFException("Unexpected end of Huffman RLE stream");
|
||||||
|
}
|
||||||
|
|
||||||
|
int bits = read & 0xff;
|
||||||
|
bitBuffer = (bitBuffer << 8) | bits;
|
||||||
|
bitBufferLength += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Take fill order into account
|
||||||
|
bitBufferLength -= bitCount;
|
||||||
|
int result = bitBuffer >> bitBufferLength;
|
||||||
|
bitBuffer &= (1 << bitBufferLength) - 1;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
if (decodedLength < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decodedPos >= decodedLength) {
|
||||||
|
fetch();
|
||||||
|
|
||||||
|
if (decodedLength < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return decodedRow[decodedPos++] & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
|
if (decodedLength < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decodedPos >= decodedLength) {
|
||||||
|
fetch();
|
||||||
|
|
||||||
|
if (decodedLength < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int read = Math.min(decodedLength - decodedPos, len);
|
||||||
|
System.arraycopy(decodedRow, decodedPos, b, off, read);
|
||||||
|
decodedPos += read;
|
||||||
|
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long skip(long n) throws IOException {
|
||||||
|
if (decodedLength < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decodedPos >= decodedLength) {
|
||||||
|
fetch();
|
||||||
|
|
||||||
|
if (decodedLength < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int skipped = (int) Math.min(decodedLength - decodedPos, n);
|
||||||
|
decodedPos += skipped;
|
||||||
|
|
||||||
|
return skipped;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean markSupported() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void reset() throws IOException {
|
||||||
|
throw new IOException("mark/reset not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
static final short[][] BLACK_CODES = {
|
||||||
|
{ // 2 bits
|
||||||
|
0x2, 0x3,
|
||||||
|
},
|
||||||
|
{ // 3 bits
|
||||||
|
0x2, 0x3,
|
||||||
|
},
|
||||||
|
{ // 4 bits
|
||||||
|
0x2, 0x3,
|
||||||
|
},
|
||||||
|
{ // 5 bits
|
||||||
|
0x3,
|
||||||
|
},
|
||||||
|
{ // 6 bits
|
||||||
|
0x4, 0x5,
|
||||||
|
},
|
||||||
|
{ // 7 bits
|
||||||
|
0x4, 0x5, 0x7,
|
||||||
|
},
|
||||||
|
{ // 8 bits
|
||||||
|
0x4, 0x7,
|
||||||
|
},
|
||||||
|
{ // 9 bits
|
||||||
|
0x18,
|
||||||
|
},
|
||||||
|
{ // 10 bits
|
||||||
|
0x17, 0x18, 0x37, 0x8, 0xf,
|
||||||
|
},
|
||||||
|
{ // 11 bits
|
||||||
|
0x17, 0x18, 0x28, 0x37, 0x67, 0x68, 0x6c, 0x8, 0xc, 0xd,
|
||||||
|
},
|
||||||
|
{ // 12 bits
|
||||||
|
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x1c, 0x1d, 0x1e, 0x1f, 0x24, 0x27, 0x28, 0x2b, 0x2c, 0x33,
|
||||||
|
0x34, 0x35, 0x37, 0x38, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x64, 0x65,
|
||||||
|
0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xd2, 0xd3,
|
||||||
|
0xd4, 0xd5, 0xd6, 0xd7, 0xda, 0xdb,
|
||||||
|
},
|
||||||
|
{ // 13 bits
|
||||||
|
0x4a, 0x4b, 0x4c, 0x4d, 0x52, 0x53, 0x54, 0x55, 0x5a, 0x5b, 0x64, 0x65, 0x6c, 0x6d, 0x72, 0x73,
|
||||||
|
0x74, 0x75, 0x76, 0x77,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static final short[][] BLACK_RUN_LENGTHS = {
|
||||||
|
{ // 2 bits
|
||||||
|
3, 2,
|
||||||
|
},
|
||||||
|
{ // 3 bits
|
||||||
|
1, 4,
|
||||||
|
},
|
||||||
|
{ // 4 bits
|
||||||
|
6, 5,
|
||||||
|
},
|
||||||
|
{ // 5 bits
|
||||||
|
7,
|
||||||
|
},
|
||||||
|
{ // 6 bits
|
||||||
|
9, 8,
|
||||||
|
},
|
||||||
|
{ // 7 bits
|
||||||
|
10, 11, 12,
|
||||||
|
},
|
||||||
|
{ // 8 bits
|
||||||
|
13, 14,
|
||||||
|
},
|
||||||
|
{ // 9 bits
|
||||||
|
15,
|
||||||
|
},
|
||||||
|
{ // 10 bits
|
||||||
|
16, 17, 0, 18, 64,
|
||||||
|
},
|
||||||
|
{ // 11 bits
|
||||||
|
24, 25, 23, 22, 19, 20, 21, 1792, 1856, 1920,
|
||||||
|
},
|
||||||
|
{ // 12 bits
|
||||||
|
1984, 2048, 2112, 2176, 2240, 2304, 2368, 2432, 2496, 2560, 52, 55, 56, 59, 60, 320,
|
||||||
|
384, 448, 53, 54, 50, 51, 44, 45, 46, 47, 57, 58, 61, 256, 48, 49,
|
||||||
|
62, 63, 30, 31, 32, 33, 40, 41, 128, 192, 26, 27, 28, 29, 34, 35,
|
||||||
|
36, 37, 38, 39, 42, 43,
|
||||||
|
},
|
||||||
|
{ // 13 bits
|
||||||
|
640, 704, 768, 832, 1280, 1344, 1408, 1472, 1536, 1600, 1664, 1728, 512, 576, 896, 960,
|
||||||
|
1024, 1088, 1152, 1216,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final short[][] WHITE_CODES = {
|
||||||
|
{ // 4 bits
|
||||||
|
0x7, 0x8, 0xb, 0xc, 0xe, 0xf,
|
||||||
|
},
|
||||||
|
{ // 5 bits
|
||||||
|
0x12, 0x13, 0x14, 0x1b, 0x7, 0x8,
|
||||||
|
},
|
||||||
|
{ // 6 bits
|
||||||
|
0x17, 0x18, 0x2a, 0x2b, 0x3, 0x34, 0x35, 0x7, 0x8,
|
||||||
|
},
|
||||||
|
{ // 7 bits
|
||||||
|
0x13, 0x17, 0x18, 0x24, 0x27, 0x28, 0x2b, 0x3, 0x37, 0x4, 0x8, 0xc,
|
||||||
|
},
|
||||||
|
{ // 8 bits
|
||||||
|
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x1a, 0x1b, 0x2, 0x24, 0x25, 0x28, 0x29, 0x2a, 0x2b, 0x2c,
|
||||||
|
0x2d, 0x3, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x4, 0x4a, 0x4b, 0x5, 0x52, 0x53, 0x54, 0x55,
|
||||||
|
0x58, 0x59, 0x5a, 0x5b, 0x64, 0x65, 0x67, 0x68, 0xa, 0xb,
|
||||||
|
},
|
||||||
|
{ // 9 bits
|
||||||
|
0x98, 0x99, 0x9a, 0x9b, 0xcc, 0xcd, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
|
||||||
|
},
|
||||||
|
{ // 10 bits
|
||||||
|
},
|
||||||
|
{ // 11 bits
|
||||||
|
0x8, 0xc, 0xd,
|
||||||
|
},
|
||||||
|
{ // 12 bits
|
||||||
|
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final short[][] WHITE_RUN_LENGTHS = {
|
||||||
|
{ // 4 bits
|
||||||
|
2, 3, 4, 5, 6, 7,
|
||||||
|
},
|
||||||
|
{ // 5 bits
|
||||||
|
128, 8, 9, 64, 10, 11,
|
||||||
|
},
|
||||||
|
{ // 6 bits
|
||||||
|
192, 1664, 16, 17, 13, 14, 15, 1, 12,
|
||||||
|
},
|
||||||
|
{ // 7 bits
|
||||||
|
26, 21, 28, 27, 18, 24, 25, 22, 256, 23, 20, 19,
|
||||||
|
},
|
||||||
|
{ // 8 bits
|
||||||
|
33, 34, 35, 36, 37, 38, 31, 32, 29, 53, 54, 39, 40, 41, 42, 43,
|
||||||
|
44, 30, 61, 62, 63, 0, 320, 384, 45, 59, 60, 46, 49, 50, 51,
|
||||||
|
52, 55, 56, 57, 58, 448, 512, 640, 576, 47, 48,
|
||||||
|
},
|
||||||
|
{ // 9 bits
|
||||||
|
1472, 1536, 1600, 1728, 704, 768, 832, 896, 960, 1024, 1088, 1152, 1216, 1280, 1344, 1408,
|
||||||
|
},
|
||||||
|
{ // 10 bits
|
||||||
|
},
|
||||||
|
{ // 11 bits
|
||||||
|
1792, 1856, 1920,
|
||||||
|
},
|
||||||
|
{ // 12 bits
|
||||||
|
1984, 2048, 2112, 2176, 2240, 2304, 2368, 2432, 2496, 2560,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
}
|
|
||||||
}
|
|
@ -37,7 +37,7 @@ package com.twelvemonkeys.imageio.plugins.tiff;
|
|||||||
*/
|
*/
|
||||||
interface TIFFBaseline {
|
interface TIFFBaseline {
|
||||||
int COMPRESSION_NONE = 1;
|
int COMPRESSION_NONE = 1;
|
||||||
int COMPRESSION_CCITT_HUFFMAN = 2;
|
int COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE = 2;
|
||||||
int COMPRESSION_PACKBITS = 32773;
|
int COMPRESSION_PACKBITS = 32773;
|
||||||
|
|
||||||
int PHOTOMETRIC_WHITE_IS_ZERO = 0;
|
int PHOTOMETRIC_WHITE_IS_ZERO = 0;
|
||||||
|
@ -49,7 +49,7 @@ interface TIFFCustom {
|
|||||||
int COMPRESSION_JBIG = 34661;
|
int COMPRESSION_JBIG = 34661;
|
||||||
int COMPRESSION_SGILOG = 34676;
|
int COMPRESSION_SGILOG = 34676;
|
||||||
int COMPRESSION_SGILOG24 = 34677;
|
int COMPRESSION_SGILOG24 = 34677;
|
||||||
int COMPRESSION_JP2000 = 34712;
|
int COMPRESSION_JPEG2000 = 34712;
|
||||||
|
|
||||||
int PHOTOMETRIC_LOGL = 32844;
|
int PHOTOMETRIC_LOGL = 32844;
|
||||||
int PHOTOMETRIC_LOGLUV = 32845;
|
int PHOTOMETRIC_LOGLUV = 32845;
|
||||||
|
@ -106,16 +106,16 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
// TODO: Source region (*tests should be failing*)
|
// TODO: Source region (*tests should be failing*)
|
||||||
// TODO: TIFFImageWriter + Spi
|
// TODO: TIFFImageWriter + Spi
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
// TODOs ImageIO advanced functionality:
|
// TODOs ImageIO advanced functionality:
|
||||||
// TODO: Implement readAsRenderedImage to allow tiled renderImage?
|
// TODO: Implement readAsRenderedImage to allow tiled renderImage?
|
||||||
// For some layouts, we could do reads super-fast with a memory mapped buffer.
|
// For some layouts, we could do reads super-fast with a memory mapped buffer.
|
||||||
// TODO: Implement readAsRaster directly
|
// TODO: Implement readAsRaster directly
|
||||||
// TODO: IIOMetadata (stay close to Sun's TIFF metadata)
|
// TODO: IIOMetadata (stay close to Sun's TIFF metadata)
|
||||||
|
// http://download.java.net/media/jai-imageio/javadoc/1.1/com/sun/media/imageio/plugins/tiff/package-summary.html#ImageMetadata
|
||||||
// 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
|
|
||||||
|
|
||||||
// TODOs Extension support
|
// TODOs Extension support
|
||||||
// TODO: Support PlanarConfiguration 2
|
// TODO: Support PlanarConfiguration 2
|
||||||
@ -127,6 +127,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
// DONE:
|
// DONE:
|
||||||
// Handle SampleFormat (and give up if not == 1)
|
// Handle SampleFormat (and give up if not == 1)
|
||||||
// Support Compression 6 ('Old-style' JPEG)
|
// Support Compression 6 ('Old-style' JPEG)
|
||||||
|
// Support Compression 2 (CCITT Modified Huffman RLE) for bi-level images
|
||||||
|
|
||||||
final static boolean DEBUG = "true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.tiff.debug"));
|
final static boolean DEBUG = "true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.tiff.debug"));
|
||||||
|
|
||||||
@ -175,7 +176,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
return IFDs.directoryCount();
|
return IFDs.directoryCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getValueAsIntWithDefault(final int tag, String tagName, Integer defaultValue) throws IIOException {
|
private Number getValueAsNumberWithDefault(final int tag, final String tagName, final Number defaultValue) throws IIOException {
|
||||||
Entry entry = currentIFD.getEntryById(tag);
|
Entry entry = currentIFD.getEntryById(tag);
|
||||||
|
|
||||||
if (entry == null) {
|
if (entry == null) {
|
||||||
@ -186,7 +187,19 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
throw new IIOException("Missing TIFF tag: " + (tagName != null ? tagName : tag));
|
throw new IIOException("Missing TIFF tag: " + (tagName != null ? tagName : tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ((Number) entry.getValue()).intValue();
|
return (Number) entry.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getValueAsLongWithDefault(final int tag, final String tagName, final Long defaultValue) throws IIOException {
|
||||||
|
return getValueAsNumberWithDefault(tag, tagName, defaultValue).longValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getValueAsLongWithDefault(final int tag, final Long defaultValue) throws IIOException {
|
||||||
|
return getValueAsLongWithDefault(tag, null, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getValueAsIntWithDefault(final int tag, final String tagName, final Integer defaultValue) throws IIOException {
|
||||||
|
return getValueAsNumberWithDefault(tag, tagName, defaultValue).intValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getValueAsIntWithDefault(final int tag, Integer defaultValue) throws IIOException {
|
private int getValueAsIntWithDefault(final int tag, Integer defaultValue) throws IIOException {
|
||||||
@ -364,7 +377,6 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
case TIFFBaseline.PHOTOMETRIC_MASK:
|
case TIFFBaseline.PHOTOMETRIC_MASK:
|
||||||
// Transparency mask
|
// Transparency mask
|
||||||
|
|
||||||
// TODO: Known extensions
|
|
||||||
throw new IIOException("Unsupported TIFF PhotometricInterpretation value: " + interpretation);
|
throw new IIOException("Unsupported TIFF PhotometricInterpretation value: " + interpretation);
|
||||||
default:
|
default:
|
||||||
throw new IIOException("Unknown TIFF PhotometricInterpretation value: " + interpretation);
|
throw new IIOException("Unknown TIFF PhotometricInterpretation value: " + interpretation);
|
||||||
@ -464,7 +476,9 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
// NOTE: We handle strips as tiles of tileWidth == width by tileHeight == rowsPerStrip
|
// NOTE: We handle strips as tiles of tileWidth == width by tileHeight == rowsPerStrip
|
||||||
// Strips are top/down, tiles are left/right, top/down
|
// Strips are top/down, tiles are left/right, top/down
|
||||||
int stripTileWidth = width;
|
int stripTileWidth = width;
|
||||||
int stripTileHeight = getValueAsIntWithDefault(TIFF.TAG_ROWS_PER_STRIP, height);
|
long rowsPerStrip = getValueAsLongWithDefault(TIFF.TAG_ROWS_PER_STRIP, (1l << 32) - 1);
|
||||||
|
int stripTileHeight = rowsPerStrip < height ? (int) rowsPerStrip : height;
|
||||||
|
|
||||||
long[] stripTileOffsets = getValueAsLongArray(TIFF.TAG_TILE_OFFSETS, "TileOffsets", false);
|
long[] stripTileOffsets = getValueAsLongArray(TIFF.TAG_TILE_OFFSETS, "TileOffsets", false);
|
||||||
long[] stripTileByteCounts;
|
long[] stripTileByteCounts;
|
||||||
|
|
||||||
@ -507,6 +521,13 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
// LZW
|
// LZW
|
||||||
case TIFFExtension.COMPRESSION_ZLIB:
|
case TIFFExtension.COMPRESSION_ZLIB:
|
||||||
// 'Adobe-style' Deflate
|
// 'Adobe-style' Deflate
|
||||||
|
case TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE:
|
||||||
|
// CCITT modified Huffman
|
||||||
|
// Additionally, the specification defines these values as part of the TIFF extensions:
|
||||||
|
// case TIFFExtension.COMPRESSION_CCITT_T4:
|
||||||
|
// CCITT Group 3 fax encoding
|
||||||
|
// case TIFFExtension.COMPRESSION_CCITT_T6:
|
||||||
|
// CCITT Group 4 fax encoding
|
||||||
|
|
||||||
int[] yCbCrSubsampling = null;
|
int[] yCbCrSubsampling = null;
|
||||||
int yCbCrPos = 1;
|
int yCbCrPos = 1;
|
||||||
@ -585,7 +606,8 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
? IIOUtil.createStreamAdapter(imageInput, stripTileByteCounts[i])
|
? IIOUtil.createStreamAdapter(imageInput, stripTileByteCounts[i])
|
||||||
: IIOUtil.createStreamAdapter(imageInput);
|
: IIOUtil.createStreamAdapter(imageInput);
|
||||||
|
|
||||||
adapter = createDecoderInputStream(compression, adapter);
|
adapter = createDecompressorStream(compression, width, adapter);
|
||||||
|
adapter = createUnpredictorStream(predictor, width, planarConfiguration == 2 ? 1 : raster.getNumBands(), getBitsPerSample(), adapter, imageInput.getByteOrder());
|
||||||
|
|
||||||
if (interpretation == TIFFExtension.PHOTOMETRIC_YCBCR) {
|
if (interpretation == TIFFExtension.PHOTOMETRIC_YCBCR) {
|
||||||
adapter = new YCbCrUpsamplerStream(adapter, yCbCrSubsampling, yCbCrPos, colsInTile, yCbCrCoefficients);
|
adapter = new YCbCrUpsamplerStream(adapter, yCbCrSubsampling, yCbCrPos, colsInTile, yCbCrCoefficients);
|
||||||
@ -598,7 +620,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read a full strip/tile
|
// Read a full strip/tile
|
||||||
readStripTileData(rowRaster, interpretation, predictor, raster, numBands, col, row, colsInTile, rowsInTile, input);
|
readStripTileData(rowRaster, interpretation, raster, col, row, colsInTile, rowsInTile, input);
|
||||||
|
|
||||||
if (abortRequested()) {
|
if (abortRequested()) {
|
||||||
break;
|
break;
|
||||||
@ -917,14 +939,28 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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:
|
case TIFFExtension.COMPRESSION_CCITT_T4:
|
||||||
// CCITT Group 3 fax encoding
|
// CCITT Group 3 fax encoding
|
||||||
case TIFFExtension.COMPRESSION_CCITT_T6:
|
case TIFFExtension.COMPRESSION_CCITT_T6:
|
||||||
// CCITT Group 4 fax encoding
|
// CCITT Group 4 fax encoding
|
||||||
|
|
||||||
|
// Known, but unsupported compression types
|
||||||
|
case TIFFCustom.COMPRESSION_NEXT:
|
||||||
|
case TIFFCustom.COMPRESSION_CCITTRLEW:
|
||||||
|
case TIFFCustom.COMPRESSION_THUNDERSCAN:
|
||||||
|
case TIFFCustom.COMPRESSION_IT8CTPAD:
|
||||||
|
case TIFFCustom.COMPRESSION_IT8LW:
|
||||||
|
case TIFFCustom.COMPRESSION_IT8MP:
|
||||||
|
case TIFFCustom.COMPRESSION_IT8BL:
|
||||||
|
case TIFFCustom.COMPRESSION_PIXARFILM:
|
||||||
|
case TIFFCustom.COMPRESSION_PIXARLOG:
|
||||||
|
case TIFFCustom.COMPRESSION_DCS:
|
||||||
|
case TIFFCustom.COMPRESSION_JBIG: // Doable with JBIG plugin?
|
||||||
|
case TIFFCustom.COMPRESSION_SGILOG:
|
||||||
|
case TIFFCustom.COMPRESSION_SGILOG24:
|
||||||
|
case TIFFCustom.COMPRESSION_JPEG2000: // Doable with JPEG2000 plugin?
|
||||||
|
|
||||||
throw new IIOException("Unsupported TIFF Compression value: " + compression);
|
throw new IIOException("Unsupported TIFF Compression value: " + compression);
|
||||||
default:
|
default:
|
||||||
throw new IIOException("Unknown TIFF Compression value: " + compression);
|
throw new IIOException("Unknown TIFF Compression value: " + compression);
|
||||||
@ -1004,8 +1040,8 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
return stream.createInputStream();
|
return stream.createInputStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readStripTileData(final WritableRaster rowRaster, final int interpretation, final int predictor,
|
private void readStripTileData(final WritableRaster rowRaster, final int interpretation,
|
||||||
final WritableRaster raster, final int numBands, final int col, final int startRow,
|
final WritableRaster raster, final int col, final int startRow,
|
||||||
final int colsInStrip, final int rowsInStrip, final DataInput input)
|
final int colsInStrip, final int rowsInStrip, final DataInput input)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
switch (rowRaster.getTransferType()) {
|
switch (rowRaster.getTransferType()) {
|
||||||
@ -1020,8 +1056,6 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
input.readFully(rowData);
|
input.readFully(rowData);
|
||||||
|
|
||||||
unPredict(predictor, colsInStrip, 1, numBands, rowData);
|
|
||||||
normalizeBlack(interpretation, rowData);
|
normalizeBlack(interpretation, rowData);
|
||||||
|
|
||||||
if (colsInStrip == rowRaster.getWidth() && col + colsInStrip <= raster.getWidth()) {
|
if (colsInStrip == rowRaster.getWidth() && col + colsInStrip <= raster.getWidth()) {
|
||||||
@ -1048,7 +1082,6 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
rowDataShort[k] = input.readShort();
|
rowDataShort[k] = input.readShort();
|
||||||
}
|
}
|
||||||
|
|
||||||
unPredict(predictor, colsInStrip, 1, numBands, rowDataShort);
|
|
||||||
normalizeBlack(interpretation, rowDataShort);
|
normalizeBlack(interpretation, rowDataShort);
|
||||||
|
|
||||||
if (colsInStrip == rowRaster.getWidth() && col + colsInStrip <= raster.getWidth()) {
|
if (colsInStrip == rowRaster.getWidth() && col + colsInStrip <= raster.getWidth()) {
|
||||||
@ -1075,7 +1108,6 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
rowDataInt[k] = input.readInt();
|
rowDataInt[k] = input.readInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
unPredict(predictor, colsInStrip, 1, numBands, rowDataInt);
|
|
||||||
normalizeBlack(interpretation, rowDataInt);
|
normalizeBlack(interpretation, rowDataInt);
|
||||||
|
|
||||||
if (colsInStrip == rowRaster.getWidth() && col + colsInStrip <= raster.getWidth()) {
|
if (colsInStrip == rowRaster.getWidth() && col + colsInStrip <= raster.getWidth()) {
|
||||||
@ -1118,61 +1150,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("UnusedParameters")
|
private InputStream createDecompressorStream(final int compression, final int width, final InputStream stream) throws IOException {
|
||||||
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 TIFFBaseline.PREDICTOR_NONE:
|
|
||||||
break;
|
|
||||||
case TIFFExtension.PREDICTOR_HORIZONTAL_DIFFERENCING:
|
|
||||||
// TODO: Implement
|
|
||||||
case TIFFExtension.PREDICTOR_HORIZONTAL_FLOATINGPOINT:
|
|
||||||
throw new IIOException("Unsupported TIFF Predictor value: " + predictor);
|
|
||||||
default:
|
|
||||||
throw new IIOException("Unknown TIFF Predictor value: " + predictor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("UnusedParameters")
|
|
||||||
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 TIFFBaseline.PREDICTOR_NONE:
|
|
||||||
break;
|
|
||||||
case TIFFExtension.PREDICTOR_HORIZONTAL_DIFFERENCING:
|
|
||||||
// TODO: Implement
|
|
||||||
case TIFFExtension.PREDICTOR_HORIZONTAL_FLOATINGPOINT:
|
|
||||||
throw new IIOException("Unsupported TIFF Predictor value: " + predictor);
|
|
||||||
default:
|
|
||||||
throw new IIOException("Unknown TIFF Predictor value: " + predictor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 TIFFBaseline.PREDICTOR_NONE:
|
|
||||||
break;
|
|
||||||
case TIFFExtension.PREDICTOR_HORIZONTAL_DIFFERENCING:
|
|
||||||
for (int y = 0; y < rows; y++) {
|
|
||||||
for (int x = 1; x < scanLine; x++) {
|
|
||||||
// TODO: For planar data (PlanarConfiguration == 2), treat as bands == 1
|
|
||||||
for (int b = 0; b < bands; b++) {
|
|
||||||
int off = y * scanLine + x;
|
|
||||||
data[off * bands + b] = (byte) (data[(off - 1) * bands + b] + data[off * bands + b]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case TIFFExtension.PREDICTOR_HORIZONTAL_FLOATINGPOINT:
|
|
||||||
throw new IIOException("Unsupported TIFF Predictor value: " + predictor);
|
|
||||||
default:
|
|
||||||
throw new IIOException("Unknown TIFF Predictor value: " + predictor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private InputStream createDecoderInputStream(final int compression, final InputStream stream) throws IOException {
|
|
||||||
switch (compression) {
|
switch (compression) {
|
||||||
case TIFFBaseline.COMPRESSION_NONE:
|
case TIFFBaseline.COMPRESSION_NONE:
|
||||||
return stream;
|
return stream;
|
||||||
@ -1181,14 +1159,31 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
case TIFFExtension.COMPRESSION_LZW:
|
case TIFFExtension.COMPRESSION_LZW:
|
||||||
return new DecoderStream(stream, LZWDecoder.create(LZWDecoder.isOldBitReversedStream(stream)), 1024);
|
return new DecoderStream(stream, LZWDecoder.create(LZWDecoder.isOldBitReversedStream(stream)), 1024);
|
||||||
case TIFFExtension.COMPRESSION_ZLIB:
|
case TIFFExtension.COMPRESSION_ZLIB:
|
||||||
case TIFFExtension.COMPRESSION_DEFLATE:
|
|
||||||
// TIFFphotoshop.pdf (aka TIFF specification, supplement 2) says ZLIB (8) and DEFLATE (32946) algorithms are identical
|
// TIFFphotoshop.pdf (aka TIFF specification, supplement 2) says ZLIB (8) and DEFLATE (32946) algorithms are identical
|
||||||
|
case TIFFExtension.COMPRESSION_DEFLATE:
|
||||||
return new InflaterInputStream(stream, new Inflater(), 1024);
|
return new InflaterInputStream(stream, new Inflater(), 1024);
|
||||||
|
case TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE:
|
||||||
|
case TIFFExtension.COMPRESSION_CCITT_T4:
|
||||||
|
case TIFFExtension.COMPRESSION_CCITT_T6:
|
||||||
|
return new CCITTFaxDecoderStream(stream, width, compression, getValueAsIntWithDefault(TIFF.TAG_FILL_ORDER, 1));
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unsupported TIFF compression: " + compression);
|
throw new IllegalArgumentException("Unsupported TIFF compression: " + compression);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private InputStream createUnpredictorStream(final int predictor, final int width, final int samplesPerPixel, final int bitsPerSample, final InputStream stream, final ByteOrder byteOrder) throws IOException {
|
||||||
|
switch (predictor) {
|
||||||
|
case TIFFBaseline.PREDICTOR_NONE:
|
||||||
|
return stream;
|
||||||
|
case TIFFExtension.PREDICTOR_HORIZONTAL_DIFFERENCING:
|
||||||
|
return new HorizontalDeDifferencingStream(stream, width, samplesPerPixel, bitsPerSample, byteOrder);
|
||||||
|
case TIFFExtension.PREDICTOR_HORIZONTAL_FLOATINGPOINT:
|
||||||
|
throw new IIOException("Unsupported TIFF Predictor value: " + predictor);
|
||||||
|
default:
|
||||||
|
throw new IIOException("Unknown TIFF Predictor value: " + predictor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private long[] getValueAsLongArray(final int tag, final String tagName, boolean required) throws IIOException {
|
private long[] getValueAsLongArray(final int tag, final String tagName, boolean required) throws IIOException {
|
||||||
Entry entry = currentIFD.getEntryById(tag);
|
Entry entry = currentIFD.getEntryById(tag);
|
||||||
if (entry == null) {
|
if (entry == null) {
|
||||||
|
@ -0,0 +1,137 @@
|
|||||||
|
<!DOCTYPE com_sun_media_imageio_plugins_tiff_image_1.0 [
|
||||||
|
<!ELEMENT com_sun_media_imageio_plugins_tiff_image_1.0 (TIFFIFD)*>
|
||||||
|
|
||||||
|
<!ELEMENT TIFFIFD (TIFFField | TIFFIFD)*>
|
||||||
|
<!-- An IFD (directory) containing fields -->
|
||||||
|
<!ATTLIST TIFFIFD "tagSets" CDATA #REQUIRED>
|
||||||
|
<!-- Data type: String -->
|
||||||
|
<!ATTLIST TIFFIFD "parentTagNumber" CDATA #IMPLIED>
|
||||||
|
<!-- The tag number of the field pointing to this IFD -->
|
||||||
|
<!-- Data type: Integer -->
|
||||||
|
<!ATTLIST TIFFIFD "parentTagName" CDATA #IMPLIED>
|
||||||
|
<!-- A mnemonic name for the field pointing to this IFD, if known
|
||||||
|
-->
|
||||||
|
<!-- Data type: String -->
|
||||||
|
|
||||||
|
<!ELEMENT TIFFField (TIFFBytes | TIFFAsciis | TIFFShorts | TIFFSShorts | TIFFLongs | TIFFSLongs | TIFFRationals | TIFFSRationals | TIFFFloats | TIFFDoubles | TIFFUndefined)>
|
||||||
|
<!-- A field containing data -->
|
||||||
|
<!ATTLIST TIFFField "number" CDATA #REQUIRED>
|
||||||
|
<!-- The tag number asociated with the field -->
|
||||||
|
<!-- Data type: String -->
|
||||||
|
<!ATTLIST TIFFField "name" CDATA #IMPLIED>
|
||||||
|
<!-- A mnemonic name associated with the field, if known -->
|
||||||
|
<!-- Data type: String -->
|
||||||
|
|
||||||
|
<!ELEMENT TIFFBytes (TIFFByte)*>
|
||||||
|
<!-- A sequence of TIFFByte nodes -->
|
||||||
|
|
||||||
|
<!ELEMENT TIFFByte EMPTY>
|
||||||
|
<!-- An integral value between 0 and 255 -->
|
||||||
|
<!ATTLIST TIFFByte "value" CDATA #IMPLIED>
|
||||||
|
<!-- The value -->
|
||||||
|
<!-- Data type: String -->
|
||||||
|
<!ATTLIST TIFFByte "description" CDATA #IMPLIED>
|
||||||
|
<!-- A description, if available -->
|
||||||
|
<!-- Data type: String -->
|
||||||
|
|
||||||
|
<!ELEMENT TIFFAsciis (TIFFAscii)*>
|
||||||
|
<!-- A sequence of TIFFAscii nodes -->
|
||||||
|
|
||||||
|
<!ELEMENT TIFFAscii EMPTY>
|
||||||
|
<!-- A String value -->
|
||||||
|
<!ATTLIST TIFFAscii "value" CDATA #IMPLIED>
|
||||||
|
<!-- The value -->
|
||||||
|
<!-- Data type: String -->
|
||||||
|
|
||||||
|
<!ELEMENT TIFFShorts (TIFFShort)*>
|
||||||
|
<!-- A sequence of TIFFShort nodes -->
|
||||||
|
|
||||||
|
<!ELEMENT TIFFShort EMPTY>
|
||||||
|
<!-- An integral value between 0 and 65535 -->
|
||||||
|
<!ATTLIST TIFFShort "value" CDATA #IMPLIED>
|
||||||
|
<!-- The value -->
|
||||||
|
<!-- Data type: String -->
|
||||||
|
<!ATTLIST TIFFShort "description" CDATA #IMPLIED>
|
||||||
|
<!-- A description, if available -->
|
||||||
|
<!-- Data type: String -->
|
||||||
|
|
||||||
|
<!ELEMENT TIFFSShorts (TIFFSShort)*>
|
||||||
|
<!-- A sequence of TIFFSShort nodes -->
|
||||||
|
|
||||||
|
<!ELEMENT TIFFSShort EMPTY>
|
||||||
|
<!-- An integral value between -32768 and 32767 -->
|
||||||
|
<!ATTLIST TIFFSShort "value" CDATA #IMPLIED>
|
||||||
|
<!-- The value -->
|
||||||
|
<!-- Data type: String -->
|
||||||
|
<!ATTLIST TIFFSShort "description" CDATA #IMPLIED>
|
||||||
|
<!-- A description, if available -->
|
||||||
|
<!-- Data type: String -->
|
||||||
|
|
||||||
|
<!ELEMENT TIFFLongs (TIFFLong)*>
|
||||||
|
<!-- A sequence of TIFFLong nodes -->
|
||||||
|
|
||||||
|
<!ELEMENT TIFFLong EMPTY>
|
||||||
|
<!-- An integral value between 0 and 4294967295 -->
|
||||||
|
<!ATTLIST TIFFLong "value" CDATA #IMPLIED>
|
||||||
|
<!-- The value -->
|
||||||
|
<!-- Data type: String -->
|
||||||
|
<!ATTLIST TIFFLong "description" CDATA #IMPLIED>
|
||||||
|
<!-- A description, if available -->
|
||||||
|
<!-- Data type: String -->
|
||||||
|
|
||||||
|
<!ELEMENT TIFFSLongs (TIFFSLong)*>
|
||||||
|
<!-- A sequence of TIFFSLong nodes -->
|
||||||
|
|
||||||
|
<!ELEMENT TIFFSLong EMPTY>
|
||||||
|
<!-- An integral value between -2147483648 and 2147482647 -->
|
||||||
|
<!ATTLIST TIFFSLong "value" CDATA #IMPLIED>
|
||||||
|
<!-- The value -->
|
||||||
|
<!-- Data type: String -->
|
||||||
|
<!ATTLIST TIFFSLong "description" CDATA #IMPLIED>
|
||||||
|
<!-- A description, if available -->
|
||||||
|
<!-- Data type: String -->
|
||||||
|
|
||||||
|
<!ELEMENT TIFFRationals (TIFFRational)*>
|
||||||
|
<!-- A sequence of TIFFRational nodes -->
|
||||||
|
|
||||||
|
<!ELEMENT TIFFRational EMPTY>
|
||||||
|
<!-- A rational value consisting of an unsigned numerator and
|
||||||
|
denominator -->
|
||||||
|
<!ATTLIST TIFFRational "value" CDATA #IMPLIED>
|
||||||
|
<!-- The numerator and denominator, separated by a slash -->
|
||||||
|
<!-- Data type: String -->
|
||||||
|
|
||||||
|
<!ELEMENT TIFFSRationals (TIFFSRational)*>
|
||||||
|
<!-- A sequence of TIFFSRational nodes -->
|
||||||
|
|
||||||
|
<!ELEMENT TIFFSRational EMPTY>
|
||||||
|
<!-- A rational value consisting of a signed numerator and
|
||||||
|
denominator -->
|
||||||
|
<!ATTLIST TIFFSRational "value" CDATA #IMPLIED>
|
||||||
|
<!-- The numerator and denominator, separated by a slash -->
|
||||||
|
<!-- Data type: String -->
|
||||||
|
|
||||||
|
<!ELEMENT TIFFFloats (TIFFFloat)*>
|
||||||
|
<!-- A sequence of TIFFFloat nodes -->
|
||||||
|
|
||||||
|
<!ELEMENT TIFFFloat EMPTY>
|
||||||
|
<!-- A single-precision floating-point value -->
|
||||||
|
<!ATTLIST TIFFFloat "value" CDATA #IMPLIED>
|
||||||
|
<!-- The value -->
|
||||||
|
<!-- Data type: String -->
|
||||||
|
|
||||||
|
<!ELEMENT TIFFDoubles (TIFFDouble)*>
|
||||||
|
<!-- A sequence of TIFFDouble nodes -->
|
||||||
|
|
||||||
|
<!ELEMENT TIFFDouble EMPTY>
|
||||||
|
<!-- A double-precision floating-point value -->
|
||||||
|
<!ATTLIST TIFFDouble "value" CDATA #IMPLIED>
|
||||||
|
<!-- The value -->
|
||||||
|
<!-- Data type: String -->
|
||||||
|
|
||||||
|
<!ELEMENT TIFFUndefined EMPTY>
|
||||||
|
<!-- Uninterpreted byte data -->
|
||||||
|
<!ATTLIST TIFFUndefined "value" CDATA #IMPLIED>
|
||||||
|
<!-- A list of comma-separated byte values -->
|
||||||
|
<!-- Data type: String -->
|
||||||
|
]>
|
@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE com_sun_media_imageio_plugins_tiff_stream_1.0 [
|
||||||
|
<!ELEMENT com_sun_media_imageio_plugins_tiff_stream_1.0 (ByteOrder)>
|
||||||
|
|
||||||
|
<!ELEMENT ByteOrder EMPTY>
|
||||||
|
<!-- The stream byte order -->
|
||||||
|
<!ATTLIST ByteOrder "value" CDATA #REQUIRED>
|
||||||
|
<!-- One of "BIG_ENDIAN" or "LITTLE_ENDIAN" -->
|
||||||
|
<!-- Data type: String -->
|
||||||
|
]>
|
Loading…
x
Reference in New Issue
Block a user