mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-02 19:15:29 -04:00
TMI-TIFF: Fixed minor bug in type spec for ARGB images + implemented support for "old-style" (reversed) LZW compression from libtiff.
This commit is contained in:
parent
7846f497af
commit
59b91918e0
@ -43,6 +43,8 @@ import java.util.Arrays;
|
||||
* @version $Id: LZWDecoder.java,v 1.0 08.05.12 21:11 haraldk Exp$
|
||||
*/
|
||||
final class LZWDecoder implements Decoder {
|
||||
// TODO: Break out compatibility handling to subclass, to avoid code branching?
|
||||
|
||||
/** Clear: Re-initialize tables. */
|
||||
static final int CLEAR_CODE = 256;
|
||||
/** End of Information. */
|
||||
@ -53,17 +55,16 @@ final class LZWDecoder implements Decoder {
|
||||
|
||||
private final boolean reverseBitOrder;
|
||||
|
||||
private int currentByte = -1;
|
||||
private int bitPos;
|
||||
|
||||
// TODO: Consider speeding things up with a "string" type (instead of the inner byte[]),
|
||||
// that uses variable size/dynamic allocation, to avoid the excessive array copying?
|
||||
// private final byte[][] table = new byte[4096][0]; // libTiff adds another 1024 "for compatibility"...
|
||||
private final byte[][] table = new byte[4096 + 1024][0]; // libTiff adds another 1024 "for compatibility"...
|
||||
// private final Entry[] tableToo = new Entry[4096 + 1024];
|
||||
private int tableLength;
|
||||
private int bitsPerCode;
|
||||
private int oldCode = CLEAR_CODE;
|
||||
private int maxCode;
|
||||
private int bitMask;
|
||||
private int maxString;
|
||||
private boolean eofReached;
|
||||
|
||||
@ -74,6 +75,10 @@ final class LZWDecoder implements Decoder {
|
||||
table[i] = new byte[] {(byte) i};
|
||||
}
|
||||
|
||||
// for (int i = 0; i < 256; i++) {
|
||||
// tableToo[i] = new Entry((byte) i);
|
||||
// }
|
||||
//
|
||||
init();
|
||||
}
|
||||
|
||||
@ -81,14 +86,15 @@ final class LZWDecoder implements Decoder {
|
||||
this(false);
|
||||
}
|
||||
|
||||
private int maxCodeFor(final int bits) {
|
||||
return reverseBitOrder ? (1 << bits) - 2 : (1 << bits) - 1;
|
||||
private static int maxCodeFor(final int bits) {
|
||||
return (1 << bits) - 1;
|
||||
}
|
||||
|
||||
private void init() {
|
||||
tableLength = 258;
|
||||
bitsPerCode = MIN_BITS;
|
||||
maxCode = maxCodeFor(bitsPerCode);
|
||||
bitMask = maxCodeFor(bitsPerCode);
|
||||
maxCode = reverseBitOrder ? bitMask : bitMask - 1;
|
||||
maxString = 1;
|
||||
}
|
||||
|
||||
@ -151,7 +157,7 @@ final class LZWDecoder implements Decoder {
|
||||
private void addStringToTable(final byte[] string) throws IOException {
|
||||
table[tableLength++] = string;
|
||||
|
||||
if (tableLength >= maxCode) {
|
||||
if (tableLength > maxCode) {
|
||||
bitsPerCode++;
|
||||
|
||||
if (bitsPerCode > MAX_BITS) {
|
||||
@ -163,7 +169,8 @@ final class LZWDecoder implements Decoder {
|
||||
}
|
||||
}
|
||||
|
||||
maxCode = maxCodeFor(bitsPerCode);
|
||||
bitMask = maxCodeFor(bitsPerCode);
|
||||
maxCode = reverseBitOrder ? bitMask : bitMask - 1;
|
||||
}
|
||||
|
||||
if (string.length > maxString) {
|
||||
@ -191,34 +198,20 @@ final class LZWDecoder implements Decoder {
|
||||
return code < tableLength;
|
||||
}
|
||||
|
||||
|
||||
int nextData, nextBits;
|
||||
|
||||
private int getNextCode(final InputStream stream) throws IOException {
|
||||
if (eofReached) {
|
||||
return EOI_CODE;
|
||||
}
|
||||
|
||||
int bitsToFill = bitsPerCode;
|
||||
int value = 0;
|
||||
|
||||
while (bitsToFill > 0) {
|
||||
int nextBits;
|
||||
if (bitPos == 0) {
|
||||
nextBits = stream.read();
|
||||
|
||||
if (nextBits == -1) {
|
||||
// This is really a bad stream, but should be safe to handle this way, rather than throwing an EOFException.
|
||||
// An EOFException will be thrown by the decoder stream later, if further reading is attempted.
|
||||
int code;
|
||||
int read = stream.read();
|
||||
if (read < 0) {
|
||||
eofReached = true;
|
||||
return EOI_CODE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
nextBits = currentByte;
|
||||
}
|
||||
|
||||
int bitsFromHere = 8 - bitPos;
|
||||
if (bitsFromHere > bitsToFill) {
|
||||
bitsFromHere = bitsToFill;
|
||||
}
|
||||
|
||||
if (reverseBitOrder) {
|
||||
// NOTE: This is a spec violation. However, libTiff reads such files.
|
||||
@ -226,33 +219,44 @@ final class LZWDecoder implements Decoder {
|
||||
// "LZW compression codes are stored into bytes in high-to-low-order fashion, i.e., FillOrder
|
||||
// is assumed to be 1. The compressed codes are written as bytes (not words) so that the
|
||||
// compressed data will be identical whether it is an ‘II’ or ‘MM’ file."
|
||||
nextData |= read << nextBits;
|
||||
nextBits += 8;
|
||||
|
||||
// Fill bytes from right-to-left
|
||||
for (int i = 0; i < bitsFromHere; i++) {
|
||||
int destBitPos = bitsPerCode - bitsToFill + i;
|
||||
int srcBitPos = bitPos + i;
|
||||
value |= ((nextBits & (1 << srcBitPos)) >> srcBitPos) << destBitPos;
|
||||
if (nextBits < bitsPerCode) {
|
||||
read = stream.read();
|
||||
if (read < 0) {
|
||||
eofReached = true;
|
||||
return EOI_CODE;
|
||||
}
|
||||
|
||||
nextData |= read << nextBits;
|
||||
nextBits += 8;
|
||||
}
|
||||
|
||||
code = (nextData & bitMask);
|
||||
nextData >>= bitsPerCode;
|
||||
nextBits -= bitsPerCode;
|
||||
}
|
||||
else {
|
||||
value |= (nextBits >> 8 - bitPos - bitsFromHere & 0xff >> 8 - bitsFromHere) << bitsToFill - bitsFromHere;
|
||||
}
|
||||
nextData = (nextData << 8) | read;
|
||||
nextBits += 8;
|
||||
|
||||
bitsToFill -= bitsFromHere;
|
||||
bitPos += bitsFromHere;
|
||||
|
||||
if (bitPos >= 8) {
|
||||
bitPos = 0;
|
||||
}
|
||||
|
||||
currentByte = nextBits;
|
||||
}
|
||||
|
||||
if (value == EOI_CODE) {
|
||||
if (nextBits < bitsPerCode) {
|
||||
read = stream.read();
|
||||
if (read < 0) {
|
||||
eofReached = true;
|
||||
return EOI_CODE;
|
||||
}
|
||||
|
||||
return value;
|
||||
nextData = (nextData << 8) | read;
|
||||
nextBits += 8;
|
||||
}
|
||||
|
||||
code = ((nextData >> (nextBits - bitsPerCode)) & bitMask);
|
||||
nextBits -= bitsPerCode;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
static boolean isOldBitReversedStream(final InputStream stream) throws IOException {
|
||||
@ -267,5 +271,24 @@ final class LZWDecoder implements Decoder {
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
private class Entry {
|
||||
final Entry next;
|
||||
|
||||
final int length;
|
||||
final byte value;
|
||||
final byte firstChar;
|
||||
|
||||
public Entry(byte code) {
|
||||
this(code, code, 1, null);
|
||||
}
|
||||
|
||||
public Entry(byte value, byte firstChar, int length, Entry next) {
|
||||
this.length = length;
|
||||
this.value = value;
|
||||
this.firstChar = firstChar;
|
||||
this.next = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,6 +109,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
// 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
|
||||
// TODO: IIOMetadata
|
||||
|
||||
// TODOs Full BaseLine support:
|
||||
// TODO: Support ExtraSamples (an array, if multiple extra samples!)
|
||||
@ -282,7 +283,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
return ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR);
|
||||
}
|
||||
|
||||
return ImageTypeSpecifier.createInterleaved(cs, new int[] {0, 1, 2, 3}, dataType, false, false);
|
||||
return ImageTypeSpecifier.createInterleaved(cs, new int[] {0, 1, 2, 3}, dataType, true, false);
|
||||
|
||||
case TIFFExtension.PLANARCONFIG_PLANAR:
|
||||
return ImageTypeSpecifier.createBanded(cs, new int[] {0, 1, 2, 3}, new int[] {0, 0, 0, 0}, dataType, false, false);
|
||||
@ -704,7 +705,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
// }
|
||||
// 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);
|
||||
// System.err.printf("Unexpected EOF or bad data at [%d, %d]\n", col + k, row);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
@ -30,7 +30,6 @@ import com.twelvemonkeys.io.enc.Decoder;
|
||||
import com.twelvemonkeys.io.enc.DecoderAbstractTestCase;
|
||||
import com.twelvemonkeys.io.enc.DecoderStream;
|
||||
import com.twelvemonkeys.io.enc.Encoder;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@ -66,15 +65,6 @@ public class LZWDecoderTest extends DecoderAbstractTestCase {
|
||||
assertSameStreamContents(unpacked, stream);
|
||||
}
|
||||
|
||||
@Ignore("Known issue")
|
||||
@Test
|
||||
public void testShortBitReversedStreamLine45To49() throws IOException {
|
||||
InputStream stream = new DecoderStream(getClass().getResourceAsStream("/lzw/lzw-short-45-49.bin"), new LZWDecoder(true), 128);
|
||||
InputStream unpacked = getClass().getResourceAsStream("/lzw/unpacked-short-45-49.bin");
|
||||
|
||||
assertSameStreamContents(unpacked, stream);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLongStream() throws IOException {
|
||||
InputStream stream = new DecoderStream(getClass().getResourceAsStream("/lzw/lzw-long.bin"), new LZWDecoder(), 1024);
|
||||
|
@ -52,6 +52,7 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTestCase<TIFFImageRe
|
||||
new TestData(getClassLoaderResource("/tiff/sm_colors_tile.tif"), new Dimension(64, 64)), // RGB, uncompressed, tiled
|
||||
new TestData(getClassLoaderResource("/tiff/sm_colors_pb_tile.tif"), new Dimension(64, 64)), // RGB, PackBits compressed, tiled
|
||||
new TestData(getClassLoaderResource("/tiff/galaxy.tif"), new Dimension(965, 965)), // RGB, LZW compressed
|
||||
new TestData(getClassLoaderResource("/tiff/quad-lzw.tif"), new Dimension(512, 384)), // RGB, OLD LZW compressed, tiled
|
||||
new TestData(getClassLoaderResource("/tiff/bali.tif"), new Dimension(725, 489)), // Palette-based, LZW compressed
|
||||
new TestData(getClassLoaderResource("/tiff/f14.tif"), new Dimension(640, 480)), // Gray, uncompressed
|
||||
new TestData(getClassLoaderResource("/tiff/marbles.tif"), new Dimension(1419, 1001)), // RGB, LZW compressed w/predictor
|
||||
|
Binary file not shown.
Binary file not shown.
BIN
imageio/imageio-tiff/src/test/resources/tiff/quad-lzw.tif
Normal file
BIN
imageio/imageio-tiff/src/test/resources/tiff/quad-lzw.tif
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user