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