1package com.bumptech.glide.gifdecoder;
2
3
4/**
5 * Copyright (c) 2013 Xcellent Creations, Inc.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 */
26
27import android.graphics.Bitmap;
28import android.graphics.Color;
29import android.util.Log;
30
31import java.io.ByteArrayOutputStream;
32import java.io.IOException;
33import java.io.InputStream;
34import java.nio.ByteBuffer;
35import java.nio.ByteOrder;
36
37/**
38 * Reads frame data from a GIF image source and decodes it into individual frames
39 * for animation purposes.  Image data can be read from either and InputStream source
40 * or a byte[].
41 *
42 * This class is optimized for running animations with the frames, there
43 * are no methods to get individual frame images, only to decode the next frame in the
44 * animation sequence.  Instead, it lowers its memory footprint by only housing the minimum
45 * data necessary to decode the next frame in the animation sequence.
46 *
47 * The animation must be manually moved forward using {@link #advance()} before requesting the next
48 * frame.  This method must also be called before you request the first frame or an error will
49 * occur.
50 *
51 * Implementation adapted from sample code published in Lyons. (2004). <em>Java for Programmers</em>,
52 * republished under the MIT Open Source License
53 */
54public class GifDecoder {
55    private static final String TAG = GifDecoder.class.getSimpleName();
56
57    /**
58     * File read status: No errors.
59     */
60    public static final int STATUS_OK = 0;
61    /**
62     * File read status: Error decoding file (may be partially decoded)
63     */
64    public static final int STATUS_FORMAT_ERROR = 1;
65    /**
66     * File read status: Unable to open source.
67     */
68    public static final int STATUS_OPEN_ERROR = 2;
69    /**
70     * max decoder pixel stack size
71     */
72    private static final int MAX_STACK_SIZE = 4096;
73
74    /**
75     * GIF Disposal Method meaning take no action
76     */
77    private static final int DISPOSAL_UNSPECIFIED = 0;
78    /**
79     * GIF Disposal Method meaning leave canvas from previous frame
80     */
81    private static final int DISPOSAL_NONE = 1;
82    /**
83     * GIF Disposal Method meaning clear canvas to background color
84     */
85    private static final int DISPOSAL_BACKGROUND = 2;
86    /**
87     * GIF Disposal Method meaning clear canvas to frame before last
88     */
89    private static final int DISPOSAL_PREVIOUS = 3;
90
91    //Global File Header values and parsing flags
92    private int[] act; // active color table
93
94    // Raw GIF data from input source
95    private ByteBuffer rawData;
96
97    // Raw data read working array
98    private byte[] block = new byte[256]; // current data block
99    // LZW decoder working arrays
100    private short[] prefix;
101    private byte[] suffix;
102    private byte[] pixelStack;
103    private byte[] mainPixels;
104    private int[] mainScratch;
105
106    private int framePointer = -1;
107    private byte[] data;
108    private GifHeader header;
109    private String id;
110    private BitmapProvider bitmapProvider;
111
112    public interface BitmapProvider {
113        public Bitmap obtain(int width, int height, Bitmap.Config config);
114    }
115
116    public GifDecoder(BitmapProvider provider) {
117        this.bitmapProvider = provider;
118        header = new GifHeader();
119    }
120
121    public int getWidth() {
122        return header.width;
123    }
124
125    public int getHeight() {
126        return header.height;
127    }
128
129    public boolean isTransparent() {
130        return header.isTransparent;
131    }
132
133    public int getGifByteSize() {
134        return data.length;
135    }
136
137    public byte[] getData() {
138        return data;
139    }
140
141    public int getDecodedFramesByteSizeSum() {
142        // 4 == ARGB_8888, 2 == RGB_565
143        return header.frameCount * header.width * header.height * (header.isTransparent ? 4 : 2);
144    }
145
146    /**
147     * Move the animation frame counter forward
148     */
149    public void advance() {
150        framePointer = (framePointer + 1) % header.frameCount;
151    }
152
153    /**
154     * Gets display duration for specified frame.
155     *
156     * @param n int index of frame
157     * @return delay in milliseconds
158     */
159    public int getDelay(int n) {
160        int delay = -1;
161        if ((n >= 0) && (n < header.frameCount)) {
162            delay = header.frames.get(n).delay;
163        }
164        return delay;
165    }
166
167    /**
168     * Gets display duration for the upcoming frame
169     */
170    public int getNextDelay() {
171        if (header.frameCount <= 0 || framePointer < 0) {
172            return -1;
173        }
174
175        return getDelay(framePointer);
176    }
177
178    /**
179     * Gets the number of frames read from file.
180     *
181     * @return frame count
182     */
183    public int getFrameCount() {
184        return header.frameCount;
185    }
186
187    /**
188     * Gets the current index of the animation frame, or -1 if animation hasn't not yet started
189     *
190     * @return frame index
191     */
192    public int getCurrentFrameIndex() {
193        return framePointer;
194    }
195
196    /**
197     * Gets the "Netscape" iteration count, if any. A count of 0 means repeat indefinitiely.
198     *
199     * @return iteration count if one was specified, else 1.
200     */
201    public int getLoopCount() {
202        return header.loopCount;
203    }
204
205    public String getId() {
206        return id;
207    }
208
209    /**
210     * Get the next frame in the animation sequence.
211     *
212     * @return Bitmap representation of frame
213     */
214    public Bitmap getNextFrame() {
215        if (header.frameCount <= 0 || framePointer < 0 ) {
216            return null;
217        }
218
219        GifFrame frame = header.frames.get(framePointer);
220
221        //Set the appropriate color table
222        if (frame.lct == null) {
223            act = header.gct;
224        } else {
225            act = frame.lct;
226            if (header.bgIndex == frame.transIndex) {
227                header.bgColor = 0;
228            }
229        }
230
231        int save = 0;
232        if (frame.transparency) {
233            save = act[frame.transIndex];
234            act[frame.transIndex] = 0; // set transparent color if specified
235        }
236        if (act == null) {
237            Log.w(TAG, "No Valid Color Table");
238            header.status = STATUS_FORMAT_ERROR; // no color table defined
239            return null;
240        }
241
242        Bitmap result = setPixels(framePointer); // transfer pixel data to image
243
244        // Reset the transparent pixel in the color table
245        if (frame.transparency) {
246            act[frame.transIndex] = save;
247        }
248
249        return result;
250    }
251
252    /**
253     * Reads GIF image from stream
254     *
255     * @param is containing GIF file.
256     * @return read status code (0 = no errors)
257     */
258    public int read(InputStream is, int contentLength) {
259        if (is != null) {
260            try {
261                int capacity = (contentLength > 0) ? (contentLength + 4096) : 16384;
262                ByteArrayOutputStream buffer = new ByteArrayOutputStream(capacity);
263                int nRead;
264                byte[] data = new byte[16384];
265                while ((nRead = is.read(data, 0, data.length)) != -1) {
266                    buffer.write(data, 0, nRead);
267                }
268                buffer.flush();
269
270                read(buffer.toByteArray());
271            } catch (IOException e) {
272                Log.w(TAG, "Error reading data from stream", e);
273            }
274        } else {
275            header.status = STATUS_OPEN_ERROR;
276        }
277
278        try {
279            if (is != null) {
280                is.close();
281            }
282        } catch (IOException e) {
283            Log.w(TAG, "Error closing stream", e);
284        }
285
286        return header.status;
287    }
288
289    public void setData(String id, GifHeader header, byte[] data) {
290        this.id = id;
291        this.header = header;
292        this.data = data;
293        //Initialize the raw data buffer
294        rawData = ByteBuffer.wrap(data);
295        rawData.rewind();
296        rawData.order(ByteOrder.LITTLE_ENDIAN);
297
298        //Now that we know the size, init scratch arrays
299        mainPixels = new byte[header.width * header.height];
300        mainScratch = new int[header.width * header.height];
301    }
302
303    /**
304     * Reads GIF image from byte array
305     *
306     * @param data containing GIF file.
307     * @return read status code (0 = no errors)
308     */
309    public int read(byte[] data) {
310        this.data = data;
311        this.header = new GifHeaderParser(data).parseHeader();
312        if (data != null) {
313            //Initialize the raw data buffer
314            rawData = ByteBuffer.wrap(data);
315            rawData.rewind();
316            rawData.order(ByteOrder.LITTLE_ENDIAN);
317
318            //Now that we know the size, init scratch arrays
319            mainPixels = new byte[header.width * header.height];
320            mainScratch = new int[header.width * header.height];
321        }
322
323        return header.status;
324    }
325
326    /**
327     * Creates new frame image from current data (and previous frames as specified by their disposition codes).
328     */
329    private Bitmap setPixels(int frameIndex) {
330        GifFrame currentFrame = header.frames.get(frameIndex);
331        GifFrame previousFrame = null;
332        int previousIndex = frameIndex - 1;
333        if (previousIndex >= 0) {
334            previousFrame = header.frames.get(previousIndex);
335        }
336
337        // final location of blended pixels
338        final int[] dest = mainScratch;
339
340        // fill in starting image contents based on last image's dispose code
341        if (previousFrame != null && previousFrame.dispose > DISPOSAL_UNSPECIFIED) {
342//            if (previousFrame.dispose == DISPOSAL_NONE) {
343//                We don't need to do anything for this case, mainScratch should already have the pixels of the
344//                previous image.
345//                currentImage.getPixels(dest, 0, header.width, 0, 0, header.width, header.height);
346//            }
347            if (previousFrame.dispose == DISPOSAL_BACKGROUND) {
348                // Start with a canvas filled with the background color
349                int c = 0;
350                if (!currentFrame.transparency) {
351                    c = header.bgColor;
352                }
353                for (int i = 0; i < previousFrame.ih; i++) {
354                    int n1 = (previousFrame.iy + i) * header.width + previousFrame.ix;
355                    int n2 = n1 + previousFrame.iw;
356                    for (int k = n1; k < n2; k++) {
357                        dest[k] = c;
358                    }
359                }
360            }
361        } else {
362            int c = 0;
363            if (!currentFrame.transparency) {
364                c = header.bgColor;
365            }
366            for (int i = 0; i < dest.length; i++) {
367                dest[i] = c;
368            }
369        }
370
371        // Decode pixels for this frame  into the global pixels[] scratch
372        decodeBitmapData(currentFrame, mainPixels); // decode pixel data
373
374        // copy each source line to the appropriate place in the destination
375        int pass = 1;
376        int inc = 8;
377        int iline = 0;
378        for (int i = 0; i < currentFrame.ih; i++) {
379            int line = i;
380            if (currentFrame.interlace) {
381                if (iline >= currentFrame.ih) {
382                    pass++;
383                    switch (pass) {
384                        case 2:
385                            iline = 4;
386                            break;
387                        case 3:
388                            iline = 2;
389                            inc = 4;
390                            break;
391                        case 4:
392                            iline = 1;
393                            inc = 2;
394                            break;
395                        default:
396                            break;
397                    }
398                }
399                line = iline;
400                iline += inc;
401            }
402            line += currentFrame.iy;
403            if (line < header.height) {
404                int k = line * header.width;
405                int dx = k + currentFrame.ix; // start of line in dest
406                int dlim = dx + currentFrame.iw; // end of dest line
407                if ((k + header.width) < dlim) {
408                    dlim = k + header.width; // past dest edge
409                }
410                int sx = i * currentFrame.iw; // start of line in source
411                while (dx < dlim) {
412                    // map color and insert in destination
413                    int index = ((int) mainPixels[sx++]) & 0xff;
414                    int c = act[index];
415                    if (c != 0) {
416                        dest[dx] = c;
417                    }
418                    dx++;
419                }
420            }
421        }
422
423        //Set pixels for current image
424        Bitmap result = getNextBitmap();
425        result.setPixels(dest, 0, header.width, 0, 0, header.width, header.height);
426        return result;
427    }
428
429    /**
430     * Decodes LZW image data into pixel array. Adapted from John Cristy's BitmapMagick.
431     */
432    private void decodeBitmapData(GifFrame frame, byte[] dstPixels) {
433        if (frame != null) {
434            //Jump to the frame start position
435            rawData.position(frame.bufferFrameStart);
436        }
437
438        int nullCode = -1;
439        int npix = (frame == null) ? header.width * header.height : frame.iw * frame.ih;
440        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;
441
442        if (dstPixels == null || dstPixels.length < npix) {
443            dstPixels = new byte[npix]; // allocate new pixel array
444        }
445        if (prefix == null) {
446            prefix = new short[MAX_STACK_SIZE];
447        }
448        if (suffix == null) {
449            suffix = new byte[MAX_STACK_SIZE];
450        }
451        if (pixelStack == null) {
452            pixelStack = new byte[MAX_STACK_SIZE + 1];
453        }
454
455        // Initialize GIF data stream decoder.
456        data_size = read();
457        clear = 1 << data_size;
458        end_of_information = clear + 1;
459        available = clear + 2;
460        old_code = nullCode;
461        code_size = data_size + 1;
462        code_mask = (1 << code_size) - 1;
463        for (code = 0; code < clear; code++) {
464            prefix[code] = 0; // XXX ArrayIndexOutOfBoundsException
465            suffix[code] = (byte) code;
466        }
467
468        // Decode GIF pixel stream.
469        datum = bits = count = first = top = pi = bi = 0;
470        for (i = 0; i < npix; ) {
471            if (top == 0) {
472                if (bits < code_size) {
473                    // Load bytes until there are enough bits for a code.
474                    if (count == 0) {
475                        // Read a new data block.
476                        count = readBlock();
477                        if (count <= 0) {
478                            break;
479                        }
480                        bi = 0;
481                    }
482                    datum += (((int) block[bi]) & 0xff) << bits;
483                    bits += 8;
484                    bi++;
485                    count--;
486                    continue;
487                }
488                // Get the next code.
489                code = datum & code_mask;
490                datum >>= code_size;
491                bits -= code_size;
492                // Interpret the code
493                if ((code > available) || (code == end_of_information)) {
494                    break;
495                }
496                if (code == clear) {
497                    // Reset decoder.
498                    code_size = data_size + 1;
499                    code_mask = (1 << code_size) - 1;
500                    available = clear + 2;
501                    old_code = nullCode;
502                    continue;
503                }
504                if (old_code == nullCode) {
505                    pixelStack[top++] = suffix[code];
506                    old_code = code;
507                    first = code;
508                    continue;
509                }
510                in_code = code;
511                if (code == available) {
512                    pixelStack[top++] = (byte) first;
513                    code = old_code;
514                }
515                while (code > clear) {
516                    pixelStack[top++] = suffix[code];
517                    code = prefix[code];
518                }
519                first = ((int) suffix[code]) & 0xff;
520                // Add a new string to the string table,
521                if (available >= MAX_STACK_SIZE) {
522                    break;
523                }
524                pixelStack[top++] = (byte) first;
525                prefix[available] = (short) old_code;
526                suffix[available] = (byte) first;
527                available++;
528                if (((available & code_mask) == 0) && (available < MAX_STACK_SIZE)) {
529                    code_size++;
530                    code_mask += available;
531                }
532                old_code = in_code;
533            }
534            // Pop a pixel off the pixel stack.
535            top--;
536            dstPixels[pi++] = pixelStack[top];
537            i++;
538        }
539
540        for (i = pi; i < npix; i++) {
541            dstPixels[i] = 0; // clear missing pixels
542        }
543    }
544
545    /**
546     * Reads a single byte from the input stream.
547     */
548    private int read() {
549        int curByte = 0;
550        try {
551            curByte = (rawData.get() & 0xFF);
552        } catch (Exception e) {
553            header.status = STATUS_FORMAT_ERROR;
554        }
555        return curByte;
556    }
557
558    /**
559     * Reads next variable length block from input.
560     *
561     * @return number of bytes stored in "buffer"
562     */
563    private int readBlock() {
564        int blockSize = read();
565        int n = 0;
566        if (blockSize > 0) {
567            try {
568                int count;
569                while (n < blockSize) {
570                    count = blockSize - n;
571                    rawData.get(block, n, count);
572
573                    n += count;
574                }
575            } catch (Exception e) {
576                Log.w(TAG, "Error Reading Block", e);
577                header.status = STATUS_FORMAT_ERROR;
578            }
579        }
580        return n;
581    }
582
583    private Bitmap getNextBitmap() {
584        Bitmap.Config targetConfig = header.isTransparent ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
585        Bitmap result = bitmapProvider.obtain(header.width, header.height, targetConfig);
586        if (result == null) {
587            result = Bitmap.createBitmap(header.width, header.height, targetConfig);
588        } else {
589            // If we're reusing a bitmap it may have other things drawn in it which we need to remove.
590            result.eraseColor(Color.TRANSPARENT);
591        }
592        return result;
593    }
594}
595