• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package 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 
27 import android.annotation.TargetApi;
28 import android.graphics.Bitmap;
29 import android.os.Build;
30 import android.util.Log;
31 
32 import java.io.ByteArrayOutputStream;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.nio.ByteBuffer;
36 import java.nio.ByteOrder;
37 import java.util.Arrays;
38 
39 /**
40  * Reads frame data from a GIF image source and decodes it into individual frames
41  * for animation purposes.  Image data can be read from either and InputStream source
42  * or a byte[].
43  *
44  * This class is optimized for running animations with the frames, there
45  * are no methods to get individual frame images, only to decode the next frame in the
46  * animation sequence.  Instead, it lowers its memory footprint by only housing the minimum
47  * data necessary to decode the next frame in the animation sequence.
48  *
49  * The animation must be manually moved forward using {@link #advance()} before requesting the next
50  * frame.  This method must also be called before you request the first frame or an error will
51  * occur.
52  *
53  * Implementation adapted from sample code published in Lyons. (2004). <em>Java for Programmers</em>,
54  * republished under the MIT Open Source License
55  */
56 public class GifDecoder {
57     private static final String TAG = GifDecoder.class.getSimpleName();
58 
59     /**
60      * File read status: No errors.
61      */
62     public static final int STATUS_OK = 0;
63     /**
64      * File read status: Error decoding file (may be partially decoded).
65      */
66     public static final int STATUS_FORMAT_ERROR = 1;
67     /**
68      * File read status: Unable to open source.
69      */
70     public static final int STATUS_OPEN_ERROR = 2;
71     /**
72      * Unable to fully decode the current frame.
73      */
74     public static final int STATUS_PARTIAL_DECODE = 3;
75     /**
76      * max decoder pixel stack size.
77      */
78     private static final int MAX_STACK_SIZE = 4096;
79 
80     /**
81      * GIF Disposal Method meaning take no action.
82      */
83     private static final int DISPOSAL_UNSPECIFIED = 0;
84     /**
85      * GIF Disposal Method meaning leave canvas from previous frame.
86      */
87     private static final int DISPOSAL_NONE = 1;
88     /**
89      * GIF Disposal Method meaning clear canvas to background color.
90      */
91     private static final int DISPOSAL_BACKGROUND = 2;
92     /**
93      * GIF Disposal Method meaning clear canvas to frame before last.
94      */
95     private static final int DISPOSAL_PREVIOUS = 3;
96 
97     private static final int NULL_CODE = -1;
98 
99     private static final int INITIAL_FRAME_POINTER = -1;
100 
101     // We can't tell if a gif has transparency to decode a partial frame on top of a previous frame, or if the final
102     // frame will actually have transparent pixels, so we must always use a format that supports transparency. We can't
103     // use ARGB_4444 because of framework issues drawing onto ARGB_4444 Bitmaps using Canvas.
104     private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
105 
106     // Global File Header values and parsing flags.
107     // Active color table.
108     private int[] act;
109 
110     // Raw GIF data from input source.
111     private ByteBuffer rawData;
112 
113     // Raw data read working array.
114     private final byte[] block = new byte[256];
115 
116     private GifHeaderParser parser;
117 
118     // LZW decoder working arrays.
119     private short[] prefix;
120     private byte[] suffix;
121     private byte[] pixelStack;
122     private byte[] mainPixels;
123     private int[] mainScratch;
124 
125     private int framePointer;
126     private byte[] data;
127     private GifHeader header;
128     private BitmapProvider bitmapProvider;
129     private Bitmap previousImage;
130     private boolean savePrevious;
131     private int status;
132 
133     /**
134      * An interface that can be used to provide reused {@link android.graphics.Bitmap}s to avoid GCs from constantly
135      * allocating {@link android.graphics.Bitmap}s for every frame.
136      */
137     public interface BitmapProvider {
138         /**
139          * Returns an {@link Bitmap} with exactly the given dimensions and config, or null if no such {@link Bitmap}
140          * could be obtained.
141          *
142          * @param width The width in pixels of the desired {@link android.graphics.Bitmap}.
143          * @param height The height in pixels of the desired {@link android.graphics.Bitmap}.
144          * @param config The {@link android.graphics.Bitmap.Config} of the desired {@link android.graphics.Bitmap}.
145          */
obtain(int width, int height, Bitmap.Config config)146         public Bitmap obtain(int width, int height, Bitmap.Config config);
147 
148         /**
149          * Releases the given Bitmap back to the pool.
150          */
release(Bitmap bitmap)151         public void release(Bitmap bitmap);
152     }
153 
GifDecoder(BitmapProvider provider)154     public GifDecoder(BitmapProvider provider) {
155         this.bitmapProvider = provider;
156         header = new GifHeader();
157     }
158 
getWidth()159     public int getWidth() {
160         return header.width;
161     }
162 
getHeight()163     public int getHeight() {
164         return header.height;
165     }
166 
getData()167     public byte[] getData() {
168         return data;
169     }
170 
171     /**
172      * Returns the current status of the decoder.
173      *
174      * <p>
175      *     Status will update per frame to allow the caller to tell whether or not the current frame was decoded
176      *     successfully and/or completely. Format and open failures persist across frames.
177      * </p>
178      */
getStatus()179     public int getStatus() {
180         return status;
181     }
182 
183     /**
184      * Move the animation frame counter forward.
185      */
advance()186     public void advance() {
187         framePointer = (framePointer + 1) % header.frameCount;
188     }
189 
190     /**
191      * Gets display duration for specified frame.
192      *
193      * @param n int index of frame.
194      * @return delay in milliseconds.
195      */
getDelay(int n)196     public int getDelay(int n) {
197         int delay = -1;
198         if ((n >= 0) && (n < header.frameCount)) {
199             delay = header.frames.get(n).delay;
200         }
201         return delay;
202     }
203 
204     /**
205      * Gets display duration for the upcoming frame in ms.
206      */
getNextDelay()207     public int getNextDelay() {
208         if (header.frameCount <= 0 || framePointer < 0) {
209             return -1;
210         }
211 
212         return getDelay(framePointer);
213     }
214 
215     /**
216      * Gets the number of frames read from file.
217      *
218      * @return frame count.
219      */
getFrameCount()220     public int getFrameCount() {
221         return header.frameCount;
222     }
223 
224     /**
225      * Gets the current index of the animation frame, or -1 if animation hasn't not yet started.
226      *
227      * @return frame index.
228      */
getCurrentFrameIndex()229     public int getCurrentFrameIndex() {
230         return framePointer;
231     }
232 
resetFrameIndex()233     public void resetFrameIndex() {
234         framePointer = -1;
235     }
236 
237     /**
238      * Gets the "Netscape" iteration count, if any. A count of 0 means repeat indefinitely.
239      *
240      * @return iteration count if one was specified, else 1.
241      */
getLoopCount()242     public int getLoopCount() {
243         return header.loopCount;
244     }
245 
246     /**
247      * Get the next frame in the animation sequence.
248      *
249      * @return Bitmap representation of frame.
250      */
getNextFrame()251     public synchronized Bitmap getNextFrame() {
252         if (header.frameCount <= 0 || framePointer < 0) {
253             if (Log.isLoggable(TAG, Log.DEBUG)) {
254                 Log.d(TAG, "unable to decode frame, frameCount=" + header.frameCount + " framePointer=" + framePointer);
255             }
256             status = STATUS_FORMAT_ERROR;
257         }
258         if (status == STATUS_FORMAT_ERROR || status == STATUS_OPEN_ERROR) {
259             if (Log.isLoggable(TAG, Log.DEBUG)) {
260                 Log.d(TAG, "Unable to decode frame, status=" + status);
261             }
262             return null;
263         }
264         status = STATUS_OK;
265 
266         GifFrame currentFrame = header.frames.get(framePointer);
267         GifFrame previousFrame = null;
268         int previousIndex = framePointer - 1;
269         if (previousIndex >= 0) {
270             previousFrame = header.frames.get(previousIndex);
271         }
272 
273         // Set the appropriate color table.
274         if (currentFrame.lct == null) {
275             act = header.gct;
276         } else {
277             act = currentFrame.lct;
278             if (header.bgIndex == currentFrame.transIndex) {
279                 header.bgColor = 0;
280             }
281         }
282 
283         int save = 0;
284         if (currentFrame.transparency) {
285             save = act[currentFrame.transIndex];
286             // Set transparent color if specified.
287             act[currentFrame.transIndex] = 0;
288         }
289         if (act == null) {
290             if (Log.isLoggable(TAG, Log.DEBUG)) {
291                 Log.d(TAG, "No Valid Color Table");
292             }
293             // No color table defined.
294             status = STATUS_FORMAT_ERROR;
295             return null;
296         }
297 
298         // Transfer pixel data to image.
299         Bitmap result = setPixels(currentFrame, previousFrame);
300 
301         // Reset the transparent pixel in the color table
302         if (currentFrame.transparency) {
303             act[currentFrame.transIndex] = save;
304         }
305 
306         return result;
307     }
308 
309     /**
310      * Reads GIF image from stream.
311      *
312      * @param is containing GIF file.
313      * @return read status code (0 = no errors).
314      */
read(InputStream is, int contentLength)315     public int read(InputStream is, int contentLength) {
316         if (is != null) {
317             try {
318                 int capacity = (contentLength > 0) ? (contentLength + 4096) : 16384;
319                 ByteArrayOutputStream buffer = new ByteArrayOutputStream(capacity);
320                 int nRead;
321                 byte[] data = new byte[16384];
322                 while ((nRead = is.read(data, 0, data.length)) != -1) {
323                     buffer.write(data, 0, nRead);
324                 }
325                 buffer.flush();
326 
327                 read(buffer.toByteArray());
328             } catch (IOException e) {
329                 Log.w(TAG, "Error reading data from stream", e);
330             }
331         } else {
332             status = STATUS_OPEN_ERROR;
333         }
334 
335         try {
336             if (is != null) {
337                 is.close();
338             }
339         } catch (IOException e) {
340             Log.w(TAG, "Error closing stream", e);
341         }
342 
343         return status;
344     }
345 
clear()346     public void clear() {
347         header = null;
348         data = null;
349         mainPixels = null;
350         mainScratch = null;
351         if (previousImage != null) {
352             bitmapProvider.release(previousImage);
353         }
354         previousImage = null;
355     }
356 
setData(GifHeader header, byte[] data)357     public void setData(GifHeader header, byte[] data) {
358         this.header = header;
359         this.data = data;
360         this.status = STATUS_OK;
361         framePointer = INITIAL_FRAME_POINTER;
362         // Initialize the raw data buffer.
363         rawData = ByteBuffer.wrap(data);
364         rawData.rewind();
365         rawData.order(ByteOrder.LITTLE_ENDIAN);
366 
367 
368         // No point in specially saving an old frame if we're never going to use it.
369         savePrevious = false;
370         for (GifFrame frame : header.frames) {
371             if (frame.dispose == DISPOSAL_PREVIOUS) {
372                 savePrevious = true;
373                 break;
374             }
375         }
376 
377         // Now that we know the size, init scratch arrays.
378         mainPixels = new byte[header.width * header.height];
379         mainScratch = new int[header.width * header.height];
380     }
381 
getHeaderParser()382     private GifHeaderParser getHeaderParser() {
383         if (parser == null) {
384             parser = new GifHeaderParser();
385         }
386         return parser;
387     }
388 
389     /**
390      * Reads GIF image from byte array.
391      *
392      * @param data containing GIF file.
393      * @return read status code (0 = no errors).
394      */
read(byte[] data)395     public int read(byte[] data) {
396         this.data = data;
397         this.header = getHeaderParser().setData(data).parseHeader();
398         if (data != null) {
399             // Initialize the raw data buffer.
400             rawData = ByteBuffer.wrap(data);
401             rawData.rewind();
402             rawData.order(ByteOrder.LITTLE_ENDIAN);
403 
404             // Now that we know the size, init scratch arrays.
405             mainPixels = new byte[header.width * header.height];
406             mainScratch = new int[header.width * header.height];
407 
408             // No point in specially saving an old frame if we're never going to use it.
409             savePrevious = false;
410             for (GifFrame frame : header.frames) {
411                 if (frame.dispose == DISPOSAL_PREVIOUS) {
412                     savePrevious = true;
413                     break;
414                 }
415             }
416         }
417 
418         return status;
419     }
420 
421     /**
422      * Creates new frame image from current data (and previous frames as specified by their disposition codes).
423      */
setPixels(GifFrame currentFrame, GifFrame previousFrame)424     private Bitmap setPixels(GifFrame currentFrame, GifFrame previousFrame) {
425 
426         int width = header.width;
427         int height = header.height;
428 
429         // Final location of blended pixels.
430         final int[] dest = mainScratch;
431 
432         // fill in starting image contents based on last image's dispose code
433         if (previousFrame != null && previousFrame.dispose > DISPOSAL_UNSPECIFIED) {
434             // We don't need to do anything for DISPOSAL_NONE, if it has the correct pixels so will our mainScratch
435             // and therefore so will our dest array.
436             if (previousFrame.dispose == DISPOSAL_BACKGROUND) {
437                 // Start with a canvas filled with the background color
438                 int c = 0;
439                 if (!currentFrame.transparency) {
440                     c = header.bgColor;
441                 }
442                 Arrays.fill(dest, c);
443             } else if (previousFrame.dispose == DISPOSAL_PREVIOUS && previousImage != null) {
444                 // Start with the previous frame
445                 previousImage.getPixels(dest, 0, width, 0, 0, width, height);
446             }
447         }
448 
449         // Decode pixels for this frame  into the global pixels[] scratch.
450         decodeBitmapData(currentFrame);
451 
452         // Copy each source line to the appropriate place in the destination.
453         int pass = 1;
454         int inc = 8;
455         int iline = 0;
456         for (int i = 0; i < currentFrame.ih; i++) {
457             int line = i;
458             if (currentFrame.interlace) {
459                 if (iline >= currentFrame.ih) {
460                     pass++;
461                     switch (pass) {
462                         case 2:
463                             iline = 4;
464                             break;
465                         case 3:
466                             iline = 2;
467                             inc = 4;
468                             break;
469                         case 4:
470                             iline = 1;
471                             inc = 2;
472                             break;
473                         default:
474                             break;
475                     }
476                 }
477                 line = iline;
478                 iline += inc;
479             }
480             line += currentFrame.iy;
481             if (line < header.height) {
482                 int k = line * header.width;
483                 // Start of line in dest.
484                 int dx = k + currentFrame.ix;
485                 // End of dest line.
486                 int dlim = dx + currentFrame.iw;
487                 if ((k + header.width) < dlim) {
488                     // Past dest edge.
489                     dlim = k + header.width;
490                 }
491                 // Start of line in source.
492                 int sx = i * currentFrame.iw;
493                 while (dx < dlim) {
494                     // Map color and insert in destination.
495                     int index = ((int) mainPixels[sx++]) & 0xff;
496                     int c = act[index];
497                     if (c != 0) {
498                         dest[dx] = c;
499                     }
500                     dx++;
501                 }
502             }
503         }
504 
505         // Copy pixels into previous image
506         if (savePrevious && currentFrame.dispose == DISPOSAL_UNSPECIFIED || currentFrame.dispose == DISPOSAL_NONE) {
507             if (previousImage == null) {
508                 previousImage = getNextBitmap();
509             }
510             previousImage.setPixels(dest, 0, width, 0, 0, width, height);
511         }
512 
513         // Set pixels for current image.
514         Bitmap result = getNextBitmap();
515         result.setPixels(dest, 0, width, 0, 0, width, height);
516         return result;
517     }
518 
519     /**
520      * Decodes LZW image data into pixel array. Adapted from John Cristy's BitmapMagick.
521      */
decodeBitmapData(GifFrame frame)522     private void decodeBitmapData(GifFrame frame) {
523         if (frame != null) {
524             // Jump to the frame start position.
525             rawData.position(frame.bufferFrameStart);
526         }
527 
528         int npix = (frame == null) ? header.width * header.height : frame.iw * frame.ih;
529         int available, clear, codeMask, codeSize, endOfInformation, inCode, oldCode, bits, code, count, i, datum,
530                 dataSize, first, top, bi, pi;
531 
532         if (mainPixels == null || mainPixels.length < npix) {
533             // Allocate new pixel array.
534             mainPixels = new byte[npix];
535         }
536         if (prefix == null) {
537             prefix = new short[MAX_STACK_SIZE];
538         }
539         if (suffix == null) {
540             suffix = new byte[MAX_STACK_SIZE];
541         }
542         if (pixelStack == null) {
543             pixelStack = new byte[MAX_STACK_SIZE + 1];
544         }
545 
546         // Initialize GIF data stream decoder.
547         dataSize = read();
548         clear = 1 << dataSize;
549         endOfInformation = clear + 1;
550         available = clear + 2;
551         oldCode = NULL_CODE;
552         codeSize = dataSize + 1;
553         codeMask = (1 << codeSize) - 1;
554         for (code = 0; code < clear; code++) {
555             // XXX ArrayIndexOutOfBoundsException.
556             prefix[code] = 0;
557             suffix[code] = (byte) code;
558         }
559 
560         // Decode GIF pixel stream.
561         datum = bits = count = first = top = pi = bi = 0;
562         for (i = 0; i < npix; ) {
563             // Load bytes until there are enough bits for a code.
564             if (count == 0) {
565                 // Read a new data block.
566                 count = readBlock();
567                 if (count <= 0) {
568                     status = STATUS_PARTIAL_DECODE;
569                     break;
570                 }
571                 bi = 0;
572             }
573 
574             datum += (((int) block[bi]) & 0xff) << bits;
575             bits += 8;
576             bi++;
577             count--;
578 
579             while (bits >= codeSize) {
580                 // Get the next code.
581                 code = datum & codeMask;
582                 datum >>= codeSize;
583                 bits -= codeSize;
584 
585                 // Interpret the code.
586                 if (code == clear) {
587                     // Reset decoder.
588                     codeSize = dataSize + 1;
589                     codeMask = (1 << codeSize) - 1;
590                     available = clear + 2;
591                     oldCode = NULL_CODE;
592                     continue;
593                 }
594 
595                 if (code > available) {
596                     status = STATUS_PARTIAL_DECODE;
597                     break;
598                 }
599 
600                 if (code == endOfInformation) {
601                     break;
602                 }
603 
604                 if (oldCode == NULL_CODE) {
605                     pixelStack[top++] = suffix[code];
606                     oldCode = code;
607                     first = code;
608                     continue;
609                 }
610                 inCode = code;
611                 if (code >= available) {
612                     pixelStack[top++] = (byte) first;
613                     code = oldCode;
614                 }
615                 while (code >= clear) {
616                     pixelStack[top++] = suffix[code];
617                     code = prefix[code];
618                 }
619                 first = ((int) suffix[code]) & 0xff;
620                 pixelStack[top++] = (byte) first;
621 
622                 // Add a new string to the string table.
623                 if (available < MAX_STACK_SIZE) {
624                     prefix[available] = (short) oldCode;
625                     suffix[available] = (byte) first;
626                     available++;
627                     if (((available & codeMask) == 0) && (available < MAX_STACK_SIZE)) {
628                         codeSize++;
629                         codeMask += available;
630                     }
631                 }
632                 oldCode = inCode;
633 
634                 while (top > 0) {
635                     // Pop a pixel off the pixel stack.
636                     top--;
637                     mainPixels[pi++] = pixelStack[top];
638                     i++;
639                 }
640             }
641         }
642 
643         // Clear missing pixels.
644         for (i = pi; i < npix; i++) {
645             mainPixels[i] = 0;
646         }
647     }
648 
649     /**
650      * Reads a single byte from the input stream.
651      */
read()652     private int read() {
653         int curByte = 0;
654         try {
655             curByte = rawData.get() & 0xFF;
656         } catch (Exception e) {
657             status = STATUS_FORMAT_ERROR;
658         }
659         return curByte;
660     }
661 
662     /**
663      * Reads next variable length block from input.
664      *
665      * @return number of bytes stored in "buffer".
666      */
readBlock()667     private int readBlock() {
668         int blockSize = read();
669         int n = 0;
670         if (blockSize > 0) {
671             try {
672                 int count;
673                 while (n < blockSize) {
674                     count = blockSize - n;
675                     rawData.get(block, n, count);
676 
677                     n += count;
678                 }
679             } catch (Exception e) {
680                 Log.w(TAG, "Error Reading Block", e);
681                 status = STATUS_FORMAT_ERROR;
682             }
683         }
684         return n;
685     }
686 
getNextBitmap()687     private Bitmap getNextBitmap() {
688         Bitmap result = bitmapProvider.obtain(header.width, header.height, BITMAP_CONFIG);
689         if (result == null) {
690             result = Bitmap.createBitmap(header.width, header.height, BITMAP_CONFIG);
691         }
692         setAlpha(result);
693         return result;
694     }
695 
696     @TargetApi(12)
setAlpha(Bitmap bitmap)697     private static void setAlpha(Bitmap bitmap) {
698         if (Build.VERSION.SDK_INT >= 12) {
699             bitmap.setHasAlpha(true);
700         }
701     }
702 }
703