FileDocCategorySizeDatePackage
GIFImageDecoder.javaAPI DocphoneME MR2 API (J2ME)15527Wed May 02 16:47:10 BST 2007com.sun.mmedia

GIFImageDecoder

public class GIFImageDecoder extends Object
Decodes single images after parsing headers. Helper for GIFPlayer. Performs LZW decoding, palette mapping, interlacing, tranparency follow-up

Fields Summary
static final int
UNDRAW_LEAVE
static final int
UNDRAW_RESTORE_BACKGROUND
static final int
UNDRAW_RESTORE_PREVIOUS
private int
width
private int
height
private int
colorDepth
private int
globalColorDepth
private int
curColorDepth
private byte[]
globalPalette
private byte[]
curPalette
private int[]
argb
private int[]
curArgb
private int
backgroundIndex
private int
transparentColorIndex
private int
undrawFlag
private int
framePosX
private int
framePosY
private int
frameWidth
private int
frameHeight
private boolean
interlace
private short[]
prefix
private byte[]
suffix
private byte[]
outCode
Constructors Summary
GIFImageDecoder(int width, int height, int colorDepth)


    /// Initialize with Global Descriptor data
          
        this.width = width;
        this.height = height;
        this.colorDepth = colorDepth;

        globalColorDepth = colorDepth;
        globalPalette = new byte [3 * (1 << globalColorDepth)];

        // default global color map - increasing grays
        globalPalette[0] = 0;
        globalPalette[1] = 0;
        globalPalette[2] = 0;
        int shift = 8 - globalColorDepth;
        int add = (1 << shift) - 1;
        for (int i = 1; i < (1 << globalColorDepth); ++i) {
            byte gray = (byte)((i << shift) | add);
            globalPalette[3 * i + 0] = gray;
            globalPalette[3 * i + 1] = gray;
            globalPalette[3 * i + 2] = gray;
        }

        backgroundIndex = -1;

        curColorDepth = globalColorDepth;
        curPalette = globalPalette;
        
        argb = new int [width * height];
        for (int i = 0; i < width * height; ++i)
            argb[i] = 0x00badbad; // transparent
    
Methods Summary
voidclearImage()

        if (globalPalette != null && backgroundIndex >= 0) {
            int bkg = getColor(backgroundIndex, globalPalette);
            for (int i = 0; i < width * height; ++i)
                argb[i] = bkg;
        }
    
booleandecodeImage(int lzwCodeSize, int dataSize, byte[] data, int[] out)

        curArgb = out;                                            

        // Get saved image
        System.arraycopy(argb, 0, curArgb, 0, width * height);

        // LZW decode
        boolean result = lzwImage(lzwCodeSize, dataSize, data);

        // Undraw
        switch (undrawFlag) {
        case UNDRAW_LEAVE:
        {
            int pixel = framePosX + framePosY * width;
            for (int i = 0; i < frameHeight; ++i, pixel += width)
                System.arraycopy(curArgb, pixel, argb, pixel, frameWidth);
            break;
        }
        case UNDRAW_RESTORE_BACKGROUND:
        if (globalPalette != null && backgroundIndex >= 0) {
            int pixel = framePosX + framePosY * width;
            int wdif = width - frameWidth;
            int bkg = getColor(backgroundIndex, globalPalette);

            for (int i = 0; i < frameHeight; ++i, pixel += wdif)
                for (int j = 0; j < frameWidth; ++j, ++pixel)
                    argb[pixel] = bkg;
            break;
        }
        case UNDRAW_RESTORE_PREVIOUS:
            // Do nothing
            break;
        }
        resetLocalData();

        return result;
    
private voiddrawLine(byte[] line, int y)

        if (y > frameHeight)
            return;
        int pixel = (framePosY + y) * width + framePosX;

        for (int i = 0; i < frameWidth; ++i, ++pixel) {
            int idx = line[i] & 0xff;
            if (idx != transparentColorIndex)
                curArgb[pixel] = getColor(idx, curPalette);
        }
    
private intgetColor(int index, byte[] palette)

        index *= 3;
        return 0xff000000 | ((palette[index + 0] & 0xff) << 16) |
                            ((palette[index + 1] & 0xff) << 8 ) |
                             (palette[index + 2] & 0xff);
    
private booleanlzwImage(int initCodeSize, int dataSize, byte[] block)
Parses image, using current palette and graphics control. Returns true if the image has been wholly parsed, false if it was truncated.


                               
            

        byte [] rasline = new byte[frameWidth];
        
        int OUTCODELENGTH = 4097;

        int clearCode = (1 << initCodeSize);
        int eofCode = clearCode + 1;
        int bitMask;
        int curCode;
        int outCount = OUTCODELENGTH;

        /* Variables used to walk read data */
        int remain = dataSize;
        int byteoff = 0;
        int accumbits = 0;
        int accumdata = 0;

        /* Variables used to decompress the data */
        int codeSize = initCodeSize + 1;
        int maxCode = 1 << codeSize;
        int codeMask = maxCode - 1;
        int freeCode = clearCode + 2;
        int code = 0;
        int oldCode = 0;
        char prevChar = 0;

        /* Variables used for writing pixels */
        int x = frameWidth;
        int y = 0;
        int off = 0;
        int passinc = interlace ? 8 : 1;
        int passht = passinc;

        bitMask = (1 << curColorDepth) - 1;

        /* Read codes until the eofCode is encountered */
        for (;;) {
            if (accumbits < codeSize) {
                while (remain < 2) {
                    /* Sometimes we have one last byte to process... */
                    if (remain == 1 && accumbits + 8 >= codeSize)
                        break;

                    if (off > 0)
                        drawLine(rasline, y/*, passht*/);
                        
                    /* quietly accept truncated GIF images */
                    return false;
                }

                /* 2 bytes at a time saves checking for accumbits < codeSize.
                 * We know we'll get enough and also that we can't overflow
                 * since codeSize <= 12.
                 */
                if (remain >= 2) {
                    remain -= 2;
                    accumdata += (block[byteoff++] & 0xff) << accumbits;
                    accumbits += 8;
                    accumdata += (block[byteoff++] & 0xff) << accumbits;
                    accumbits += 8;
                } else {
                    remain--;
                    accumdata += (block[byteoff++] & 0xff) << accumbits;
                    accumbits += 8;
                }
            }

            /* Compute the code */
            code = accumdata & codeMask;
            accumdata >>= codeSize;
            accumbits -= codeSize;

            /*
             * Interpret the code
             */
            if (code == clearCode) {
                /* Clear code sets everything back to its initial value, then
                 * reads the immediately subsequent code as uncompressed data.
                 */

                /* Note that freeCode is one less than it is supposed to be,
                 * this is because it will be incremented next time round the 
                 * loop
                 */
                freeCode = clearCode + 1;
                codeSize = initCodeSize + 1;
                maxCode = 1 << codeSize;
                codeMask = maxCode - 1;

                /* Continue if we've NOT reached the end, some Gif images
                 * contain bogus codes after the last clear code.
                 */
                if (y < height)
                    continue;

                return true;
            }

            if (code == eofCode)
                return true;

            /* It must be data: save code in CurCode */
            curCode = code;
            /* Whenever it gets here outCount is always equal to 
               OUTCODELENGTH, so no need to reset outCount. */
            //outCount = OUTCODELENGTH;

            /* If greater or equal to freeCode, not in the hash table
             * yet; repeat the last character decoded
             */
            if (curCode >= freeCode) {
                if (curCode > freeCode)
                    /*
                     * if we get a code too far outside our range, it
                     * could case the parser to start traversing parts
                     * of our data structure that are out of range...
                     */
                    return true;

                curCode = oldCode;
                outCode[--outCount] = (byte)prevChar;
            }

            /* Unless this code is raw data, pursue the chain pointed
             * to by curCode through the hash table to its end; each
             * code in the chain puts its associated output code on
             * the output queue.
             */
             while (curCode > bitMask) {
                 outCode[--outCount] = suffix[curCode];
                 if (outCount == 0)
                     /*
                      * In theory this should never happen since our
                      * prefix and suffix arrays are monotonically
                      * decreasing and so outCode will only be filled
                      * as much as those arrays, but I don't want to
                      * take that chance and the test is probably
                      * cheap compared to the read and write operations.
                      * If we ever do overflow the array, we will just
                      * flush the rest of the data and quietly accept
                      * the GIF as truncated here.
                      */
                    return true;

                 curCode = prefix[curCode];
             }

            /* The last code in the chain is treated as raw data. */
            prevChar = (char)curCode;
            outCode[--outCount] = (byte)prevChar;

            /* Now we put the data out to the Output routine. It's
             * been stacked LIFO, so deal with it that way...
             */
            int len = OUTCODELENGTH - outCount; /* This is why I commented out
                                               the code that resets outCount. */
            while (--len >= 0) {
                rasline[off++] = outCode[outCount++];

                /* Update the X-coordinate, and if it overflows, update the
                 * Y-coordinate
                 */
                if (--x == 0) {
                    /* If a non-interlaced picture, just increment y to the next
                     * scan line.  If it's interlaced, deal with the interlace as
                     * described in the GIF spec.  Put the decoded scan line out
                     * to the screen if we haven't gone past the bottom of it
                     */
                    drawLine(rasline, y/*, passht*/);

                    x = frameWidth;
                    off = 0;
                    /*  pass        inc     ht      ystart */
                    /*   0           8      8          0   */
                    /*   1           8      4          4   */
                    /*   2           4      2          2   */
                    /*   3           2      1          1   */
                    y += passinc;
                    while (y >= height) {
                        passinc = passht;
                        passht >>= 1;
                        y = passht;
                        if (passht == 0)
                            return true;
                    }
                }
            }

            /* Build the hash table on-the-fly. No table is stored in the file. */
            prefix[freeCode] = (short)oldCode;
            suffix[freeCode] = (byte)prevChar;
            oldCode = code;
            /* Point to the next slot in the table.  If we exceed the
             * maxCode, increment the code size unless
             * it's already 12.  If it is, do nothing: the next code
             * decompressed better be CLEAR
             */
            if (++freeCode >= maxCode) {
                if (codeSize < 12) {
                    codeSize++;
                    maxCode <<= 1;
                    codeMask = maxCode - 1;
                } else
                    /* Just in case */
                    freeCode = maxCode - 1;
            }
        }
    
voidnewFrame(int relx, int rely, int width, int height, boolean interlaceFlag)

        if (relx + width > this.width)
            framePosX = this.width - width;
        else
            framePosX = relx;

        if (rely + height > this.height)
            framePosY = this.height - height;
        else
            framePosY = rely;

        // Relying that frameWidth < this.width && frameHeight << this.height
        frameWidth = width;
        frameHeight = height;
        interlace = interlaceFlag;
    
private voidresetLocalData()

        undrawFlag = UNDRAW_LEAVE;
        transparentColorIndex = -1;
        curColorDepth = globalColorDepth;
        curPalette = globalPalette; 
    
voidsetGlobalPalette(int colorDepth, byte[] palette, int backgroundIndex)

        if (curPalette == globalPalette) {
            curPalette = palette;
            curColorDepth = colorDepth;
        }
        globalPalette = palette;
        globalColorDepth = colorDepth;

        this.backgroundIndex = backgroundIndex;
    
voidsetGraphicsControl(int undrawFlag, int transparentColorIndex)
Set Local Description Extension data (optional) udrawFlag: see UNDRAW_* constants tranparentColor: -1 - no transparent color, otherwise - index from color table

        this.undrawFlag = undrawFlag;
        this.transparentColorIndex = transparentColorIndex;
    
voidsetLocalPalette(int colorDepth, byte[] palette)

        curColorDepth = colorDepth;
        curPalette = palette;