JDK-7131823 : bug in GIFImageReader
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.imageio
  • Affected Version: 7
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: windows_7
  • CPU: x86
  • Submitted: 2012-01-20
  • Updated: 2018-09-05
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdUnresolved
Description
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

Comments
Please re-open if - if fix is in progress or on the plan to fix soon - if this is a P3 (file as P3, not P4)
14-03-2014