FULL PRODUCT VERSION :
java version "1.7.0_02"
Java(TM) SE Runtime Environment (build 1.7.0_02-b13)
Java HotSpot(TM) 64-Bit Server VM (build 22.0-b10, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]
A DESCRIPTION OF THE PROBLEM :
GIFImageReader can not read some GIF images,
because GIFImageReader has two bugs in LZW decoder.
1. LZW encoder can output the codes after the tree becomes full, without outputting the clearCode(for example it is 256, in 256-color gif image).
Good LZW encoder determines the timing of outputting the clearCode by the compression rate. if useing the old tree becomes good compression rate, Good LZW encoder does not output the clearCode. if not so, Good LZW encoder outputs the clearCode.
however, GIFImageReader is based on the wrong assumption, that is LZW encoder always outputs the clearCode before tree becomes overflow.
when GIFImageReader reads a image which is created by Good LZW encoder, it causes ArrayIndexOutOfBoundsException.
2. textbookish LZW decoder is drop the first code. it does not attach to the tree.
however, GIFImageReader attachs first code to the tree. so tree is always broken.
I understand the part which should be corrected.
however I don't understand why GIFImageReader can read images with broken tree.
show other implementation of GIF decoder, you will be able to be judged whether I am right or wrong.
http://www.java2s.com/Code/Java/2D-Graphics-GUI/GiffileEncoder.htm
http://mxr.mozilla.org/mozilla-central/source/image/decoders/nsGIFDecoder2.cpp
both implementations assign NullCode(-1) to oldCode at first, and they drop the first code.
paste part of corrected GIFImageReader#read(int, ImageReadParam)
final int NULL_CODE = -1;
int code, oldCode = NULL_CODE;
int[] prefix = new int[4096];
byte[] suffix = new byte[4096];
byte[] initial = new byte[4096];
int[] length = new int[4096];
byte[] string = new byte[4096];
initializeStringTable(prefix, suffix, initial, length);
int tableIndex = (1 << initCodeSize) + 2;
int codeSize = initCodeSize + 1;
int codeMask = (1 << codeSize) - 1;
while (!abortRequested()) {
code = getCode(codeSize, codeMask);
if (code == clearCode) {
initializeStringTable(prefix, suffix, initial, length);
tableIndex = (1 << initCodeSize) + 2;
codeSize = initCodeSize + 1;
codeMask = (1 << codeSize) - 1;
code = getCode(codeSize, codeMask);
oldCode = NULL_CODE;
if (code == eofCode) {
// Inform IIOReadProgressListeners of end of image
processImageComplete();
return theImage;
}
} else if (code == eofCode) {
// Inform IIOReadProgressListeners of end of image
processImageComplete();
return theImage;
} else {
int newSuffixIndex;
if (code < tableIndex) {
newSuffixIndex = code;
} else { // code == tableIndex
newSuffixIndex = oldCode;
if (code != tableIndex) {
// warning - code out of sequence
// possibly data corruption
processWarningOccurred("Out-of-sequence code!");
}
}
if (NULL_CODE != oldCode && tableIndex < 4096) {
int ti = tableIndex;
int oc = oldCode;
prefix[ti] = oc;
suffix[ti] = initial[newSuffixIndex];
initial[ti] = initial[oc];
length[ti] = length[oc] + 1;
++tableIndex;
if ((tableIndex == (1 << codeSize)) &&
(tableIndex < 4096)) {
++codeSize;
codeMask = (1 << codeSize) - 1;
}
}
}
// Reverse code
int c = code;
int len = length[c];
for (int i = len - 1; i >= 0; i--) {
string[i] = suffix[c];
c = prefix[c];
}
outputPixels(string, len);
oldCode = code;
}
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
I have some gif images which reproduce this bug.
I can mail it.
REPRODUCIBILITY :
This bug can be reproduced always.
CUSTOMER SUBMITTED WORKAROUND :
case 1 no workaround.
case 2 use GifDecoder in
http://www.java2s.com/Code/Java/2D-Graphics-GUI/GiffileEncoder.htm