10f49c87b2f26f3e086f021461b7e5409a7d42be0Sam Juddpackage com.bumptech.glide.gifdecoder;
24f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
3f7a6d65cf7c1a41908dd48e0dab68ee5b881387eSam Juddimport static com.bumptech.glide.gifdecoder.GifDecoder.STATUS_FORMAT_ERROR;
4f7a6d65cf7c1a41908dd48e0dab68ee5b881387eSam Judd
54f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Juddimport android.util.Log;
64f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
74f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Juddimport java.nio.BufferUnderflowException;
84f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Juddimport java.nio.ByteBuffer;
94f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Juddimport java.nio.ByteOrder;
105ce0c1172e076cd72af8efbde97cffca5f215ba1Sam Juddimport java.util.Arrays;
114f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
12b5419dc08eb0a0f82821d774435720e5a31bc936Sam Judd/**
13b5419dc08eb0a0f82821d774435720e5a31bc936Sam Judd * A class responsible for creating {@link com.bumptech.glide.gifdecoder.GifHeader}s from data representing animated
14b5419dc08eb0a0f82821d774435720e5a31bc936Sam Judd * gifs.
15b5419dc08eb0a0f82821d774435720e5a31bc936Sam Judd */
164f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Juddpublic class GifHeaderParser {
174f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    public static final String TAG = "GifHeaderParser";
184f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
194fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16Sam Judd    // The minimum frame delay in hundredths of a second.
204fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16Sam Judd    static final int MIN_FRAME_DELAY = 3;
214fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16Sam Judd    // The default frame delay in hundredths of a second for GIFs with frame delays less than the minimum.
224fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16Sam Judd    static final int DEFAULT_FRAME_DELAY = 10;
234fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16Sam Judd
243640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd    private static final int MAX_BLOCK_SIZE = 256;
25fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd    // Raw data read working array.
263640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd    private final byte[] block = new byte[MAX_BLOCK_SIZE];
27548ff09b7c987156a3f927e8988d798959a0e575Sam Judd
28548ff09b7c987156a3f927e8988d798959a0e575Sam Judd    private ByteBuffer rawData;
29548ff09b7c987156a3f927e8988d798959a0e575Sam Judd    private GifHeader header;
30fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd    private int blockSize = 0;
314f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
325ce0c1172e076cd72af8efbde97cffca5f215ba1Sam Judd    public GifHeaderParser setData(byte[] data) {
335ce0c1172e076cd72af8efbde97cffca5f215ba1Sam Judd        reset();
344f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        if (data != null) {
354f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            rawData = ByteBuffer.wrap(data);
364f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            rawData.rewind();
374f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            rawData.order(ByteOrder.LITTLE_ENDIAN);
384f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        } else {
394f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            rawData = null;
404f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            header.status = GifDecoder.STATUS_OPEN_ERROR;
414f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        }
425ce0c1172e076cd72af8efbde97cffca5f215ba1Sam Judd        return this;
435ce0c1172e076cd72af8efbde97cffca5f215ba1Sam Judd    }
445ce0c1172e076cd72af8efbde97cffca5f215ba1Sam Judd
45e4ac1f593a328f35ca17d8603a54db3826da2475Sam Judd    public void clear() {
46e4ac1f593a328f35ca17d8603a54db3826da2475Sam Judd        rawData = null;
47e4ac1f593a328f35ca17d8603a54db3826da2475Sam Judd        header = null;
48e4ac1f593a328f35ca17d8603a54db3826da2475Sam Judd    }
49e4ac1f593a328f35ca17d8603a54db3826da2475Sam Judd
505ce0c1172e076cd72af8efbde97cffca5f215ba1Sam Judd    private void reset() {
515ce0c1172e076cd72af8efbde97cffca5f215ba1Sam Judd        rawData = null;
525ce0c1172e076cd72af8efbde97cffca5f215ba1Sam Judd        Arrays.fill(block, (byte) 0);
535ce0c1172e076cd72af8efbde97cffca5f215ba1Sam Judd        header = new GifHeader();
545ce0c1172e076cd72af8efbde97cffca5f215ba1Sam Judd        blockSize = 0;
554f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    }
564f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
574f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    public GifHeader parseHeader() {
58855776275d8ca409f968c8fceff4d11f51bf8592Sam Judd        if (rawData == null) {
59855776275d8ca409f968c8fceff4d11f51bf8592Sam Judd            throw new IllegalStateException("You must call setData() before parseHeader()");
60855776275d8ca409f968c8fceff4d11f51bf8592Sam Judd        }
614f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        if (err()) {
624f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            return header;
634f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        }
644f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
654f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        readHeader();
664f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        if (!err()) {
674f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            readContents();
684f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            if (header.frameCount < 0) {
694f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                header.status = STATUS_FORMAT_ERROR;
704f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            }
714f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        }
724f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
734f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        return header;
744f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    }
754f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
764f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    /**
774f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd     * Main file parser. Reads GIF content blocks.
784f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd     */
793640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd    private void readContents() {
80fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        // Read GIF file content blocks.
814f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        boolean done = false;
824f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        while (!(done || err())) {
834f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            int code = read();
844f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            switch (code) {
85fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd                // Image separator.
86fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd                case 0x2C:
8734cde327bc68d61e7aa64ed122b1d60250bc28d7Sam Judd                    // The graphics control extension is optional, but will always come first if it exists. If one did
8834cde327bc68d61e7aa64ed122b1d60250bc28d7Sam Judd                    // exist, there will be a non-null current frame which we should use. However if one did not exist,
8934cde327bc68d61e7aa64ed122b1d60250bc28d7Sam Judd                    // the current frame will be null and we must create it here. See issue #134.
9034cde327bc68d61e7aa64ed122b1d60250bc28d7Sam Judd                    if (header.currentFrame == null) {
9134cde327bc68d61e7aa64ed122b1d60250bc28d7Sam Judd                        header.currentFrame = new GifFrame();
9234cde327bc68d61e7aa64ed122b1d60250bc28d7Sam Judd                    }
934f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                    readBitmap();
944f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                    break;
95fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd                // Extension.
96fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd                case 0x21:
974f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                    code = read();
984f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                    switch (code) {
99fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd                        // Graphics control extension.
100fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd                        case 0xf9:
101fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd                            // Start a new frame.
1024f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                            header.currentFrame = new GifFrame();
1034f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                            readGraphicControlExt();
1044f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                            break;
105fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd                        // Application extension.
106fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd                        case 0xff:
1074f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                            readBlock();
1084f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                            String app = "";
1094f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                            for (int i = 0; i < 11; i++) {
1104f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                                app += (char) block[i];
1114f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                            }
1124f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                            if (app.equals("NETSCAPE2.0")) {
1134f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                                readNetscapeExt();
1144f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                            } else {
115fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd                                // Don't care.
116fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd                                skip();
1174f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                            }
1184f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                            break;
119fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd                        // Comment extension.
120fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd                        case 0xfe:
1214f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                            skip();
1224f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                            break;
123fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd                        // Plain text extension.
124fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd                        case 0x01:
1254f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                            skip();
1264f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                            break;
127fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd                        // Uninteresting extension.
128fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd                        default:
1294f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                            skip();
1304f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                    }
1314f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                    break;
132fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd                // Terminator.
133fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd                case 0x3b:
1344f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                    done = true;
1354f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                    break;
136fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd                // Bad byte, but keep going and see what happens break;
137fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd                case 0x00:
1384f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                default:
1394f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                    header.status = STATUS_FORMAT_ERROR;
1404f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            }
1414f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        }
1424f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    }
1434f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
1444f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    /**
145fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd     * Reads Graphics Control Extension values.
1464f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd     */
1473640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd    private void readGraphicControlExt() {
148fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        // Block size.
149fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        read();
150fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        // Packed fields.
151fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        int packed = read();
152fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        // Disposal method.
153fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        header.currentFrame.dispose = (packed & 0x1c) >> 2;
1544f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        if (header.currentFrame.dispose == 0) {
155fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd            // Elect to keep old image if discretionary.
156fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd            header.currentFrame.dispose = 1;
1574f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        }
1584f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        header.currentFrame.transparency = (packed & 1) != 0;
159fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        // Delay in milliseconds.
1604fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16Sam Judd        int delayInHundredthsOfASecond = readShort();
1614fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16Sam Judd        // TODO: consider allowing -1 to indicate show forever.
1624fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16Sam Judd        if (delayInHundredthsOfASecond < MIN_FRAME_DELAY) {
1634fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16Sam Judd            delayInHundredthsOfASecond = DEFAULT_FRAME_DELAY;
1644fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16Sam Judd        }
1654fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16Sam Judd        header.currentFrame.delay = delayInHundredthsOfASecond * 10;
166fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        // Transparent color index
167fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        header.currentFrame.transIndex = read();
168fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        // Block terminator
169fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        read();
1704f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    }
1714f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
172fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd    /**
173fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd     * Reads next frame image.
1744f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd     */
1753640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd    private void readBitmap() {
176fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        // (sub)image position & size.
177fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        header.currentFrame.ix = readShort();
1784f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        header.currentFrame.iy = readShort();
1794f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        header.currentFrame.iw = readShort();
1804f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        header.currentFrame.ih = readShort();
1814f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
1824f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        int packed = read();
183fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        // 1 - local color table flag interlace
184fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        boolean lctFlag = (packed & 0x80) != 0;
185fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        int lctSize = (int) Math.pow(2, (packed & 0x07) + 1);
1864f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        // 3 - sort flag
1874f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        // 4-5 - reserved lctSize = 2 << (packed & 7); // 6-8 - local color
1884f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        // table size
1894f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        header.currentFrame.interlace = (packed & 0x40) != 0;
1904f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        if (lctFlag) {
191fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd            // Read table.
192fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd            header.currentFrame.lct = readColorTable(lctSize);
1934f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        } else {
194fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd            // No local color table.
195fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd            header.currentFrame.lct = null;
1964f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        }
1974f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
198fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        // Save this as the decoding position pointer.
199fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        header.currentFrame.bufferFrameStart = rawData.position();
2004f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
201fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        // False decode pixel data to advance buffer.
2023640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd        skipImageData();
2034f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
2044f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        if (err()) {
2054f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            return;
2064f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        }
2074f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
2084f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        header.frameCount++;
209fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        // Add image to frame.
210fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        header.frames.add(header.currentFrame);
2114f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    }
212fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd    /**
213fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd     * Reads Netscape extension to obtain iteration count.
2144f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd     */
2153640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd    private void readNetscapeExt() {
2164f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        do {
2174f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            readBlock();
2184f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            if (block[0] == 1) {
219fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd                // Loop count sub-block.
2204f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                int b1 = ((int) block[1]) & 0xff;
2214f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                int b2 = ((int) block[2]) & 0xff;
2224f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                header.loopCount = (b2 << 8) | b1;
2234f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            }
2244f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        } while ((blockSize > 0) && !err());
2254f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    }
2264f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
2274f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
228fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd    /**
2294f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd     * Reads GIF file header information.
2304f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd     */
2314f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    private void readHeader() {
2324f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        String id = "";
2334f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        for (int i = 0; i < 6; i++) {
2344f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            id += (char) read();
2354f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        }
2364f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        if (!id.startsWith("GIF")) {
2374f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            header.status = STATUS_FORMAT_ERROR;
2384f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            return;
2394f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        }
2404f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        readLSD();
2414f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        if (header.gctFlag && !err()) {
2424f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            header.gct = readColorTable(header.gctSize);
2434f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            header.bgColor = header.gct[header.bgIndex];
2444f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        }
2454f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    }
246fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd    /**
247fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd     * Reads Logical Screen Descriptor.
2484f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd     */
2493640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd    private void readLSD() {
250fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        // Logical screen size.
2514f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        header.width = readShort();
2524f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        header.height = readShort();
253fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        // Packed fields
2544f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        int packed = read();
255fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        // 1 : global color table flag.
256fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        header.gctFlag = (packed & 0x80) != 0;
257fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        // 2-4 : color resolution.
258fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        // 5 : gct sort flag.
259fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        // 6-8 : gct size.
260fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        header.gctSize = 2 << (packed & 7);
261fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        // Background color index.
262fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        header.bgIndex = read();
263fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        // Pixel aspect ratio
264fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        header.pixelAspect = read();
2654f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    }
266fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd
267fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd    /**
268fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd     * Reads color table as 256 RGB integer values.
2694f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd     *
270fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd     * @param ncolors int number of colors to read.
271fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd     * @return int array containing 256 colors (packed ARGB with full alpha).
2724f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd     */
2733640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd    private int[] readColorTable(int ncolors) {
2744f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        int nbytes = 3 * ncolors;
2754f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        int[] tab = null;
2764f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        byte[] c = new byte[nbytes];
2774f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
2784f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        try {
2794f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            rawData.get(c);
2804f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
2813640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd            // TODO: what bounds checks are we avoiding if we know the number of colors?
282fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd            // Max size to avoid bounds checks.
2833640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd            tab = new int[MAX_BLOCK_SIZE];
2844f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            int i = 0;
2854f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            int j = 0;
2864f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            while (i < ncolors) {
2874f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                int r = ((int) c[j++]) & 0xff;
2884f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                int g = ((int) c[j++]) & 0xff;
2894f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                int b = ((int) c[j++]) & 0xff;
2904f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b;
2914f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            }
2924f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        } catch (BufferUnderflowException e) {
293b1d9c53bacafb993dc4f264f76f396611e235bdbSam Judd            if (Log.isLoggable(TAG, Log.DEBUG)) {
294b1d9c53bacafb993dc4f264f76f396611e235bdbSam Judd                Log.d(TAG, "Format Error Reading Color Table", e);
295b1d9c53bacafb993dc4f264f76f396611e235bdbSam Judd            }
2964f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            header.status = STATUS_FORMAT_ERROR;
2974f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        }
2984f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
2994f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        return tab;
3004f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    }
3014f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
302fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd    /**
3033640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd     * Skips LZW image data for a single frame to advance buffer.
3044f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd     */
3053640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd    private void skipImageData() {
3063640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd        // lzwMinCodeSize
3073640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd        read();
3083640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd        // data sub-blocks
3093640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd        skip();
3104f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    }
3114f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
312fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd    /**
3134f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd     * Skips variable length blocks up to and including next zero length block.
3144f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd     */
3153640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd    private void skip() {
3163640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd        int blockSize;
3174f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        do {
3183640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd            blockSize = read();
3193640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd            rawData.position(rawData.position() + blockSize);
3203640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd        } while (blockSize > 0);
3214f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    }
3224f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
323fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd    /**
3244f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd     * Reads next variable length block from input.
3254f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd     *
3264f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd     * @return number of bytes stored in "buffer"
3274f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd     */
3283640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd    private int readBlock() {
3294f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        blockSize = read();
3304f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        int n = 0;
3314f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        if (blockSize > 0) {
33221c2822985058df655ee4874798297f74c5e367eSam Judd            int count = 0;
3334f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            try {
3344f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                while (n < blockSize) {
3354f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                    count = blockSize - n;
3364f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                    rawData.get(block, n, count);
3374f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
3384f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                    n += count;
3394f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                }
3404f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            } catch (Exception e) {
341b1d9c53bacafb993dc4f264f76f396611e235bdbSam Judd                if (Log.isLoggable(TAG, Log.DEBUG)) {
342b1d9c53bacafb993dc4f264f76f396611e235bdbSam Judd                    Log.d(TAG, "Error Reading Block n: " + n + " count: " + count + " blockSize: " + blockSize, e);
343b1d9c53bacafb993dc4f264f76f396611e235bdbSam Judd                }
3444f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd                header.status = STATUS_FORMAT_ERROR;
3454f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            }
3464f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        }
3474f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        return n;
3484f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    }
3494f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
350fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd    /**
3514f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd     * Reads a single byte from the input stream.
3524f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd     */
3534f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    private int read() {
3544f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        int curByte = 0;
3554f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        try {
356fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd            curByte = rawData.get() & 0xFF;
3574f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        } catch (Exception e) {
3584f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd            header.status = STATUS_FORMAT_ERROR;
3594f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        }
3604f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        return curByte;
3614f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    }
3624f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
3634f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    /**
364fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd     * Reads next 16-bit value, LSB first.
3654f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd     */
3663640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd    private int readShort() {
367fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        // Read 16-bit value.
3684f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        return rawData.getShort();
3694f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    }
3704f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd
3714f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    private boolean err() {
3724f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd        return header.status != GifDecoder.STATUS_OK;
3734f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd    }
3744f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd}
375