All pastes #771362 Raw Edit

Stuff

public java v1 · immutable
·published 2007-11-12 22:54 UTC
rendered paste body
package com.cp.atheater.gif;import java.awt.*;import java.awt.image.BufferedImage;import java.awt.image.DataBufferInt;import java.io.BufferedInputStream;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.net.URL;import java.util.ArrayList;/** * Class GifDecoder - Decodes a GIF file into one or more frames. * <br><pre> * Example: *    GifDecoder d = new GifDecoder(); *    d.read("sample.gif"); *    int n = d.getFrameCount(); *    for (int i = 0; i < n; i++) { *       BufferedImage frame = d.getFrame(i);  // frame i *       int t = d.getDelay(i);  // display duration of frame in milliseconds *       // do something with frame *    } * </pre> * No copyright asserted on the source code of this class.  May be used for * any purpose, however, refer to the Unisys LZW patent for any additional * restrictions.  Please forward any corrections to kweiner@fmsware.com. * * @author Kevin Weiner, FM Software; LZW decoder adapted from John Cristy's ImageMagick. * @version 1.03 November 2003 */public class GifDecoder{    /**     * File read status: No errors.     */    public static final int STATUS_OK = 0;    /**     * File read status: Error decoding file (may be partially decoded)     */    public static final int STATUS_FORMAT_ERROR = 1;    /**     * File read status: Unable to open source.     */    public static final int STATUS_OPEN_ERROR = 2;    protected BufferedInputStream in;    protected int status;    protected int width; // full image width    protected int height; // full image height    protected boolean gctFlag; // global color table used    protected int gctSize; // size of global color table    protected int loopCount = 1; // iterations; 0 = repeat forever    protected int[] gct; // global color table    protected int[] lct; // local color table    protected int[] act; // active color table    protected int bgIndex; // background color index    protected int bgColor; // background color    protected int lastBgColor; // previous bg color    protected int pixelAspect; // pixel aspect ratio    protected boolean lctFlag; // local color table flag    protected boolean interlace; // interlace flag    protected int lctSize; // local color table size    protected int ix, iy, iw, ih; // current image rectangle    protected Rectangle lastRect; // last image rect    protected BufferedImage image; // current frame    protected BufferedImage lastImage; // previous frame    protected byte[] block = new byte[256]; // current data block    protected int blockSize = 0; // block size    // last graphic control extension info    protected int dispose = 0;    // 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev    protected int lastDispose = 0;    protected boolean transparency = false; // use transparent color    protected int delay = 0; // delay in milliseconds    protected int transIndex; // transparent color index    protected static final int MaxStackSize = 4096;    // max decoder pixel stack size    // LZW decoder working arrays    protected short[] prefix;    protected byte[] suffix;    protected byte[] pixelStack;    protected byte[] pixels;    protected ArrayList frames; // frames read from current file    protected int frameCount;    static class GifFrame    {        public GifFrame(BufferedImage im, int del)        {            image = im;            delay = del;        }        public BufferedImage image;        public int delay;    }    /**     * Gets display duration for specified frame.     *     * @param n int index of frame     * @return delay in milliseconds     */    public int getDelay(int n)    {        //        delay = -1;        if ((n >= 0) && (n < frameCount))        {            delay = ((GifFrame) frames.get(n)).delay;        }        return delay;    }    /**     * Gets the number of frames read from file.     *     * @return frame count     */    public int getFrameCount()    {        return frameCount;    }    /**     * Gets the first (or only) image read.     *     * @return BufferedImage containing first frame, or null if none.     */    public BufferedImage getImage()    {        return getFrame(0);    }    /**     * Gets the "Netscape" iteration count, if any.     * A count of 0 means repeat indefinitiely.     *     * @return iteration count if one was specified, else 1.     */    public int getLoopCount()    {        return loopCount;    }    /**     * Creates new frame image from current data (and previous     * frames as specified by their disposition codes).     */    protected void setPixels()    {        // expose destination image's pixels as int array        int[] dest =                ((DataBufferInt) image.getRaster().getDataBuffer()).getData();        // fill in starting image contents based on last image's dispose code        if (lastDispose > 0)        {            if (lastDispose == 3)            {                // use image before last                int n = frameCount - 2;                if (n > 0)                {                    lastImage = getFrame(n - 1);                } else                {                    lastImage = null;                }            }            if (lastImage != null)            {                int[] prev =                        ((DataBufferInt) lastImage.getRaster().getDataBuffer()).getData();                System.arraycopy(prev, 0, dest, 0, width * height);                // copy pixels                if (lastDispose == 2)                {                    // fill last image rect area with background color                    Graphics2D g = image.createGraphics();                    Color c = null;                    if (transparency)                    {                        c = new Color(0, 0, 0, 0);     // assume background is transparent                    } else                    {                        c = new Color(lastBgColor); // use given background color                    }                    g.setColor(c);                    g.setComposite(AlphaComposite.Src); // replace area                    g.fill(lastRect);                    g.dispose();                }            }        }        // copy each source line to the appropriate place in the destination        int pass = 1;        int inc = 8;        int iline = 0;        for (int i = 0; i < ih; i++)        {            int line = i;            if (interlace)            {                if (iline >= ih)                {                    pass++;                    switch (pass)                    {                        case 2:                            iline = 4;                            break;                        case 3:                            iline = 2;                            inc = 4;                            break;                        case 4:                            iline = 1;                            inc = 2;                    }                }                line = iline;                iline += inc;            }            line += iy;            if (line < height)            {                int k = line * width;                int dx = k + ix; // start of line in dest                int dlim = dx + iw; // end of dest line                if ((k + width) < dlim)                {                    dlim = k + width; // past dest edge                }                int sx = i * iw; // start of line in source                while (dx < dlim)                {                    // map color and insert in destination                    int index = ((int) pixels[sx++]) & 0xff;                    int c = act[index];                    if (c != 0)                    {                        dest[dx] = c;                    }                    dx++;                }            }        }    }    /**     * Gets the image contents of frame n.     *     * @return BufferedImage representation of frame, or null if n is invalid.     */    public BufferedImage getFrame(int n)    {        BufferedImage im = null;        if ((n >= 0) && (n < frameCount))        {            im = ((GifFrame) frames.get(n)).image;        }        return im;    }    /**     * Gets image size.     *     * @return GIF image dimensions     */    public Dimension getFrameSize()    {        return new Dimension(width, height);    }    /**     * Reads GIF image from stream     *     * param BufferedInputStream containing GIF file.     * @return read status code (0 = no errors)     */    public int read(BufferedInputStream is)    {        init();        if (is != null)        {            in = is;            readHeader();            if (!err())            {                readContents();                if (frameCount < 0)                {                    status = STATUS_FORMAT_ERROR;                }            }        } else        {            status = STATUS_OPEN_ERROR;        }        try        {            is.close();        } catch (IOException e)        {        }        return status;    }    /**     * Reads GIF image from stream     *     * param InputStream containing GIF file.     * @return read status code (0 = no errors)     */    public int read(InputStream is)    {        init();        if (is != null)        {            if (!(is instanceof BufferedInputStream))                is = new BufferedInputStream(is);            in = (BufferedInputStream) is;            readHeader();            if (!err())            {                readContents();                if (frameCount < 0)                {                    status = STATUS_FORMAT_ERROR;                }            }        } else        {            status = STATUS_OPEN_ERROR;        }        try        {            is.close();        } catch (IOException e)        {        }        return status;    }    /**     * Reads GIF file from specified file/URL source     * (URL assumed if name contains ":/" or "file:")     *     * @param name String containing source     * @return read status code (0 = no errors)     */    public int read(String name)    {        status = STATUS_OK;        try        {            name = name.trim().toLowerCase();            if ((name.indexOf("file:") >= 0) ||                (name.indexOf(":/") > 0))            {                URL url = new URL(name);                in = new BufferedInputStream(url.openStream());            } else            {                in = new BufferedInputStream(new FileInputStream(name));            }            status = read(in);        } catch (IOException e)        {            status = STATUS_OPEN_ERROR;        }        return status;    }    /**     * Decodes LZW image data into pixel array.     * Adapted from John Cristy's ImageMagick.     */    protected void decodeImageData()    {        int NullCode = -1;        int npix = iw * ih;        int available,                clear,                code_mask,                code_size,                end_of_information,                in_code,                old_code,                bits,                code,                count,                i,                datum,                data_size,                first,                top,                bi,                pi;        if ((pixels == null) || (pixels.length < npix))        {            pixels = new byte[npix]; // allocate new pixel array        }        if (prefix == null) prefix = new short[MaxStackSize];        if (suffix == null) suffix = new byte[MaxStackSize];        if (pixelStack == null) pixelStack = new byte[MaxStackSize + 1];        //  Initialize GIF data stream decoder.        data_size = read();        clear = 1 << data_size;        end_of_information = clear + 1;        available = clear + 2;        old_code = NullCode;        code_size = data_size + 1;        code_mask = (1 << code_size) - 1;        for (code = 0; code < clear; code++)        {            prefix[code] = 0;            suffix[code] = (byte) code;        }        //  Decode GIF pixel stream.        datum = bits = count = first = top = pi = bi = 0;        for (i = 0; i < npix;)        {            if (top == 0)            {                if (bits < code_size)                {                    //  Load bytes until there are enough bits for a code.                    if (count == 0)                    {                        // Read a new data block.                        count = readBlock();                        if (count <= 0)                            break;                        bi = 0;                    }                    datum += (((int) block[bi]) & 0xff) << bits;                    bits += 8;                    bi++;                    count--;                    continue;                }                //  Get the next code.                code = datum & code_mask;                datum >>= code_size;                bits -= code_size;                //  Interpret the code                if ((code > available) || (code == end_of_information))                    break;                if (code == clear)                {                    //  Reset decoder.                    code_size = data_size + 1;                    code_mask = (1 << code_size) - 1;                    available = clear + 2;                    old_code = NullCode;                    continue;                }                if (old_code == NullCode)                {                    pixelStack[top++] = suffix[code];                    old_code = code;                    first = code;                    continue;                }                in_code = code;                if (code == available)                {                    pixelStack[top++] = (byte) first;                    code = old_code;                }                while (code > clear)                {                    pixelStack[top++] = suffix[code];                    code = prefix[code];                }                first = ((int) suffix[code]) & 0xff;                //  Add a new string to the string table,                if (available >= MaxStackSize)                    break;                pixelStack[top++] = (byte) first;                prefix[available] = (short) old_code;                suffix[available] = (byte) first;                available++;                if (((available & code_mask) == 0)                    && (available < MaxStackSize))                {                    code_size++;                    code_mask += available;                }                old_code = in_code;            }            //  Pop a pixel off the pixel stack.            top--;            pixels[pi++] = pixelStack[top];            i++;        }        for (i = pi; i < npix; i++)        {            pixels[i] = 0; // clear missing pixels        }    }    /**     * Returns true if an error was encountered during reading/decoding     */    protected boolean err()    {        return status != STATUS_OK;    }    /**     * Initializes or re-initializes reader     */    protected void init()    {        status = STATUS_OK;        frameCount = 0;        frames = new ArrayList();        gct = null;        lct = null;    }    /**     * Reads a single byte from the input stream.     */    protected int read()    {        int curByte = 0;        try        {            curByte = in.read();        } catch (IOException e)        {            status = STATUS_FORMAT_ERROR;        }        return curByte;    }    /**     * Reads next variable length block from input.     *     * @return number of bytes stored in "buffer"     */    protected int readBlock()    {        blockSize = read();        int n = 0;        if (blockSize > 0)        {            try            {                int count = 0;                while (n < blockSize)                {                    count = in.read(block, n, blockSize - n);                    if (count == -1)                        break;                    n += count;                }            } catch (IOException e)            {            }            if (n < blockSize)            {                status = STATUS_FORMAT_ERROR;            }        }        return n;    }    /**     * Reads color table as 256 RGB integer values     *     * @param ncolors int number of colors to read     * @return int array containing 256 colors (packed ARGB with full alpha)     */    protected int[] readColorTable(int ncolors)    {        int nbytes = 3 * ncolors;        int[] tab = null;        byte[] c = new byte[nbytes];        int n = 0;        try        {            n = in.read(c);        } catch (IOException e)        {        }        if (n < nbytes)        {            status = STATUS_FORMAT_ERROR;        } else        {            tab = new int[256]; // max size to avoid bounds checks            int i = 0;            int j = 0;            while (i < ncolors)            {                int r = ((int) c[j++]) & 0xff;                int g = ((int) c[j++]) & 0xff;                int b = ((int) c[j++]) & 0xff;                tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b;            }        }        return tab;    }    /**     * Main file parser.  Reads GIF content blocks.     */    protected void readContents()    {        // read GIF file content blocks        boolean done = false;        while (!(done || err()))        {            int code = read();            switch (code)            {                case 0x2C: // image separator                    readImage();                    break;                case 0x21: // extension                    code = read();                    switch (code)                    {                        case 0xf9: // graphics control extension                            readGraphicControlExt();                            break;                        case 0xff: // application extension                            readBlock();                            String app = "";                            for (int i = 0; i < 11; i++)                            {                                app += (char) block[i];                            }                            if (app.equals("NETSCAPE2.0"))                            {                                readNetscapeExt();                            } else                                skip(); // don't care                            break;                        default: // uninteresting extension                            skip();                    }                    break;                case 0x3b: // terminator                    done = true;                    break;                case 0x00: // bad byte, but keep going and see what happens                    break;                default:                    status = STATUS_FORMAT_ERROR;            }        }    }    /**     * Reads Graphics Control Extension values     */    protected void readGraphicControlExt()    {        read(); // block size        int packed = read(); // packed fields        dispose = (packed & 0x1c) >> 2; // disposal method        if (dispose == 0)        {            dispose = 1; // elect to keep old image if discretionary        }        transparency = (packed & 1) != 0;        delay = readShort() * 10; // delay in milliseconds        transIndex = read(); // transparent color index        read(); // block terminator    }    /**     * Reads GIF file header information.     */    protected void readHeader()    {        String id = "";        for (int i = 0; i < 6; i++)        {            id += (char) read();        }        if (!id.startsWith("GIF"))        {            status = STATUS_FORMAT_ERROR;            return;        }        readLSD();        if (gctFlag && !err())        {            gct = readColorTable(gctSize);            bgColor = gct[bgIndex];        }    }    /**     * Reads next frame image     */    protected void readImage()    {        ix = readShort(); // (sub)image position & size        iy = readShort();        iw = readShort();        ih = readShort();        int packed = read();        lctFlag = (packed & 0x80) != 0; // 1 - local color table flag        interlace = (packed & 0x40) != 0; // 2 - interlace flag        // 3 - sort flag        // 4-5 - reserved        lctSize = 2 << (packed & 7); // 6-8 - local color table size        if (lctFlag)        {            lct = readColorTable(lctSize); // read table            act = lct; // make local table active        } else        {            act = gct; // make global table active            if (bgIndex == transIndex)                bgColor = 0;        }        int save = 0;        if (transparency)        {            save = act[transIndex];            act[transIndex] = 0; // set transparent color if specified        }        if (act == null)        {            status = STATUS_FORMAT_ERROR; // no color table defined        }        if (err()) return;        decodeImageData(); // decode pixel data        skip();        if (err()) return;        frameCount++;        // create new image to receive frame data        image =                new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);        setPixels(); // transfer pixel data to image        frames.add(new GifFrame(image, delay)); // add image to frame list        if (transparency)        {            act[transIndex] = save;        }        resetFrame();    }    /**     * Reads Logical Screen Descriptor     */    protected void readLSD()    {        // logical screen size        width = readShort();        height = readShort();        // packed fields        int packed = read();        gctFlag = (packed & 0x80) != 0; // 1   : global color table flag        // 2-4 : color resolution        // 5   : gct sort flag        gctSize = 2 << (packed & 7); // 6-8 : gct size        bgIndex = read(); // background color index        pixelAspect = read(); // pixel aspect ratio    }    /**     * Reads Netscape extenstion to obtain iteration count     */    protected void readNetscapeExt()    {        do        {            readBlock();            if (block[0] == 1)            {                // loop count sub-block                int b1 = ((int) block[1]) & 0xff;                int b2 = ((int) block[2]) & 0xff;                loopCount = (b2 << 8) | b1;            }        } while ((blockSize > 0) && !err());    }    /**     * Reads next 16-bit value, LSB first     */    protected int readShort()    {        // read 16-bit value, LSB first        return read() | (read() << 8);    }    /**     * Resets frame state for reading next image.     */    protected void resetFrame()    {        lastDispose = dispose;        lastRect = new Rectangle(ix, iy, iw, ih);        lastImage = image;        lastBgColor = bgColor;        int dispose = 0;        boolean transparency = false;        int delay = 0;        lct = null;    }    /**     * Skips variable length blocks up to and including     * next zero length block.     */    protected void skip()    {        do        {            readBlock();        } while ((blockSize > 0) && !err());	}}