PageRenderTime 47ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/third_party/gif_player/src/jp/tomorrowkey/android/gifplayer/BaseGifDrawable.java

https://gitlab.com/jonnialva90/iridium-browser
Java | 959 lines | 679 code | 133 blank | 147 comment | 149 complexity | 8b8808054b9e0b214270ca5c935eb1cc MD5 | raw file
  1. /*
  2. * Copyright (C) 2015 The Gifplayer Authors. All Rights Reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package jp.tomorrowkey.android.gifplayer;
  17. import android.graphics.Bitmap;
  18. import android.graphics.Canvas;
  19. import android.graphics.ColorFilter;
  20. import android.graphics.Paint;
  21. import android.graphics.PixelFormat;
  22. import android.graphics.Rect;
  23. import android.graphics.drawable.Animatable;
  24. import android.graphics.drawable.Drawable;
  25. import android.os.Handler;
  26. import android.os.HandlerThread;
  27. import android.os.Looper;
  28. import android.os.Message;
  29. import android.os.SystemClock;
  30. import android.util.Log;
  31. /**
  32. * A base GIF Drawable with support for animations.
  33. *
  34. * Inspired by http://code.google.com/p/android-gifview/
  35. */
  36. public class BaseGifDrawable extends Drawable implements Runnable, Animatable,
  37. android.os.Handler.Callback {
  38. private static final String TAG = "GifDrawable";
  39. // Max decoder pixel stack size
  40. private static final int MAX_STACK_SIZE = 4096;
  41. private static final int MAX_BITS = 4097;
  42. // Frame disposal methods
  43. private static final int DISPOSAL_METHOD_UNKNOWN = 0;
  44. private static final int DISPOSAL_METHOD_LEAVE = 1;
  45. private static final int DISPOSAL_METHOD_BACKGROUND = 2;
  46. private static final int DISPOSAL_METHOD_RESTORE = 3;
  47. // Message types
  48. private static final int READ_FRAME_REQ = 10;
  49. private static final int READ_FRAME_RESP = 11;
  50. private static final int RESET_DECODER = 12;
  51. // Specifies the minimum amount of time before a subsequent frame will be rendered.
  52. private static final int MIN_FRAME_SCHEDULE_DELAY_MS = 5;
  53. private static final byte[] NETSCAPE2_0 = "NETSCAPE2.0".getBytes();
  54. private static Paint sPaint;
  55. private static Paint sScalePaint;
  56. protected final BaseGifImage mGifImage;
  57. private final byte[] mData;
  58. private int mPosition;
  59. protected int mIntrinsicWidth;
  60. protected int mIntrinsicHeight;
  61. private int mWidth;
  62. private int mHeight;
  63. protected Bitmap mBitmap;
  64. protected int[] mColors;
  65. private boolean mScale;
  66. private float mScaleFactor;
  67. // The following are marked volatile because they are read/written in the background decoder
  68. // thread and read from the UI thread. No further synchronization is needed because their
  69. // values will only ever change from at most once, and it is safe to lazily detect the change
  70. // in the UI thread.
  71. private volatile boolean mError;
  72. private volatile boolean mDone;
  73. private volatile boolean mAnimateOnLoad = true;
  74. private int mBackgroundColor;
  75. private boolean mLocalColorTableUsed;
  76. private int mLocalColorTableSize;
  77. private int[] mLocalColorTable;
  78. private int[] mActiveColorTable;
  79. private boolean mInterlace;
  80. // Each frame specifies a sub-region of the image that should be updated. The values are
  81. // clamped to the GIF dimensions if they exceed the intrinsic dimensions.
  82. private int mFrameX, mFrameY, mFrameWidth, mFrameHeight;
  83. // This specifies the width of the actual data within a GIF frame. It will be equal to
  84. // mFrameWidth unless the frame sub-region was clamped to prevent exceeding the intrinsic
  85. // dimensions.
  86. private int mFrameStep;
  87. private byte[] mBlock = new byte[256];
  88. private int mDisposalMethod = DISPOSAL_METHOD_BACKGROUND;
  89. private boolean mTransparency;
  90. private int mTransparentColorIndex;
  91. // LZW decoder working arrays
  92. private short[] mPrefix = new short[MAX_STACK_SIZE];
  93. private byte[] mSuffix = new byte[MAX_STACK_SIZE];
  94. private byte[] mPixelStack = new byte[MAX_STACK_SIZE + 1];
  95. private byte[] mPixels;
  96. private boolean mBackupSaved;
  97. private int[] mBackup;
  98. private int mFrameCount;
  99. private long mLastFrameTime;
  100. private boolean mRunning;
  101. protected int mFrameDelay;
  102. private int mNextFrameDelay;
  103. protected boolean mScheduled;
  104. private boolean mAnimationEnabled = true;
  105. private final Handler mHandler = new Handler(Looper.getMainLooper(), this);
  106. private static DecoderThread sDecoderThread;
  107. private static Handler sDecoderHandler;
  108. private boolean mRecycled;
  109. protected boolean mFirstFrameReady;
  110. private boolean mEndOfFile;
  111. private int mLoopCount = 0; // 0 to repeat endlessly.
  112. private int mLoopIndex = 0;
  113. private final Bitmap.Config mBitmapConfig;
  114. private boolean mFirstFrame = true;
  115. public BaseGifDrawable(BaseGifImage gifImage, Bitmap.Config bitmapConfig) {
  116. this.mBitmapConfig = bitmapConfig;
  117. // Create the background decoder thread, if necessary.
  118. if (sDecoderThread == null) {
  119. sDecoderThread = new DecoderThread();
  120. sDecoderThread.start();
  121. sDecoderHandler = new Handler(sDecoderThread.getLooper(), sDecoderThread);
  122. }
  123. if (sPaint == null) {
  124. sPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
  125. sScalePaint = new Paint(Paint.FILTER_BITMAP_FLAG);
  126. sScalePaint.setFilterBitmap(true);
  127. }
  128. mGifImage = gifImage;
  129. mData = gifImage.getData();
  130. mPosition = mGifImage.mHeaderSize;
  131. mFrameWidth = mFrameStep = mIntrinsicWidth = gifImage.getWidth();
  132. mFrameHeight = mIntrinsicHeight = gifImage.getHeight();
  133. mBackgroundColor = mGifImage.mBackgroundColor;
  134. mError = mGifImage.mError;
  135. if (!mError) {
  136. try {
  137. mBitmap = Bitmap.createBitmap(mIntrinsicWidth, mIntrinsicHeight, mBitmapConfig);
  138. if (mBitmap == null) {
  139. throw new OutOfMemoryError("Cannot allocate bitmap");
  140. }
  141. int pixelCount = mIntrinsicWidth * mIntrinsicHeight;
  142. mColors = new int[pixelCount];
  143. mPixels = new byte[pixelCount];
  144. mWidth = mIntrinsicHeight;
  145. mHeight = mIntrinsicHeight;
  146. // Read the first frame
  147. sDecoderHandler.sendMessage(sDecoderHandler.obtainMessage(READ_FRAME_REQ, this));
  148. } catch (OutOfMemoryError e) {
  149. mError = true;
  150. }
  151. }
  152. }
  153. /**
  154. * Sets the loop count for multi-frame animation.
  155. */
  156. public void setLoopCount(int loopCount) {
  157. mLoopCount = loopCount;
  158. }
  159. /**
  160. * Returns the loop count for multi-frame animation.
  161. */
  162. public int getLoopCount() {
  163. return mLoopCount;
  164. }
  165. /**
  166. * Sets whether to start animation on load or not.
  167. */
  168. public void setAnimateOnLoad(boolean animateOnLoad) {
  169. mAnimateOnLoad = animateOnLoad;
  170. }
  171. /**
  172. * Returns {@code true} if the GIF is valid and {@code false} otherwise.
  173. */
  174. public boolean isValid() {
  175. return !mError && mFirstFrameReady;
  176. }
  177. public void onRecycle() {
  178. if (mBitmap != null) {
  179. mBitmap.recycle();
  180. }
  181. mBitmap = null;
  182. mRecycled = true;
  183. }
  184. /**
  185. * Enables or disables the GIF from animating. GIF animations are enabled by default.
  186. */
  187. public void setAnimationEnabled(boolean animationEnabled) {
  188. if (mAnimationEnabled == animationEnabled) {
  189. return;
  190. }
  191. mAnimationEnabled = animationEnabled;
  192. if (mAnimationEnabled) {
  193. start();
  194. } else {
  195. stop();
  196. }
  197. }
  198. @Override
  199. protected void onBoundsChange(Rect bounds) {
  200. super.onBoundsChange(bounds);
  201. mWidth = bounds.width();
  202. mHeight = bounds.height();
  203. mScale = mWidth != mIntrinsicWidth && mHeight != mIntrinsicHeight;
  204. if (mScale) {
  205. mScaleFactor = Math.max((float) mWidth / mIntrinsicWidth,
  206. (float) mHeight / mIntrinsicHeight);
  207. }
  208. if (!mError && !mRecycled) {
  209. // Request that the decoder reset itself
  210. sDecoderHandler.sendMessage(sDecoderHandler.obtainMessage(RESET_DECODER, this));
  211. }
  212. }
  213. @Override
  214. public boolean setVisible(boolean visible, boolean restart) {
  215. boolean changed = super.setVisible(visible, restart);
  216. if (visible) {
  217. if (changed || restart) {
  218. start();
  219. }
  220. } else {
  221. stop();
  222. }
  223. return changed;
  224. }
  225. @Override
  226. public void draw(Canvas canvas) {
  227. if (mError || mWidth == 0 || mHeight == 0 || mRecycled || !mFirstFrameReady) {
  228. return;
  229. }
  230. if (mScale) {
  231. canvas.save();
  232. canvas.scale(mScaleFactor, mScaleFactor, 0, 0);
  233. canvas.drawBitmap(mBitmap, 0, 0, sScalePaint);
  234. canvas.restore();
  235. } else {
  236. canvas.drawBitmap(mBitmap, 0, 0, sPaint);
  237. }
  238. if (mRunning) {
  239. if (!mScheduled) {
  240. // Schedule the next frame at mFrameDelay milliseconds from the previous frame or
  241. // the minimum sceduling delay from now, whichever is later.
  242. mLastFrameTime = Math.max(
  243. mLastFrameTime + mFrameDelay,
  244. SystemClock.uptimeMillis() + MIN_FRAME_SCHEDULE_DELAY_MS);
  245. scheduleSelf(this, mLastFrameTime);
  246. }
  247. } else if (!mDone) {
  248. start();
  249. } else {
  250. unscheduleSelf(this);
  251. }
  252. }
  253. @Override
  254. public int getIntrinsicWidth() {
  255. return mIntrinsicWidth;
  256. }
  257. @Override
  258. public int getIntrinsicHeight() {
  259. return mIntrinsicHeight;
  260. }
  261. @Override
  262. public int getOpacity() {
  263. return PixelFormat.UNKNOWN;
  264. }
  265. @Override
  266. public void setAlpha(int alpha) {
  267. }
  268. @Override
  269. public void setColorFilter(ColorFilter cf) {
  270. }
  271. @Override
  272. public boolean isRunning() {
  273. return mRunning;
  274. }
  275. @Override
  276. public void start() {
  277. if (!isRunning()) {
  278. mRunning = true;
  279. if (!mAnimateOnLoad) {
  280. mDone = true;
  281. }
  282. mLastFrameTime = SystemClock.uptimeMillis();
  283. run();
  284. }
  285. }
  286. @Override
  287. public void stop() {
  288. if (isRunning()) {
  289. unscheduleSelf(this);
  290. }
  291. }
  292. @Override
  293. public void scheduleSelf(Runnable what, long when) {
  294. if (mAnimationEnabled) {
  295. super.scheduleSelf(what, when);
  296. mScheduled = true;
  297. }
  298. }
  299. @Override
  300. public void unscheduleSelf(Runnable what) {
  301. super.unscheduleSelf(what);
  302. mRunning = false;
  303. }
  304. /**
  305. * Moves to the next frame.
  306. */
  307. @Override
  308. public void run() {
  309. if (mRecycled) {
  310. return;
  311. }
  312. // Send request to decoder to read the next frame
  313. if (!mDone) {
  314. sDecoderHandler.sendMessage(sDecoderHandler.obtainMessage(READ_FRAME_REQ, this));
  315. }
  316. }
  317. /**
  318. * Restarts decoding the image from the beginning. Called from the background thread.
  319. */
  320. private void reset() {
  321. // Return to the position of the first image frame in the stream.
  322. mPosition = mGifImage.mHeaderSize;
  323. mBackupSaved = false;
  324. mFrameCount = 0;
  325. mDisposalMethod = DISPOSAL_METHOD_UNKNOWN;
  326. }
  327. /**
  328. * Restarts animation if a limited number of loops of animation have been previously done.
  329. */
  330. public void restartAnimation() {
  331. if (mDone && mLoopCount > 0) {
  332. reset();
  333. mDone = false;
  334. mLoopIndex = 0;
  335. run();
  336. }
  337. }
  338. /**
  339. * Reads color table as 256 RGB integer values. Called from the background thread.
  340. *
  341. * @param ncolors int number of colors to read
  342. */
  343. private void readColorTable(int[] colorTable, int ncolors) {
  344. for (int i = 0; i < ncolors; i++) {
  345. int r = mData[mPosition++] & 0xff;
  346. int g = mData[mPosition++] & 0xff;
  347. int b = mData[mPosition++] & 0xff;
  348. colorTable[i] = 0xff000000 | (r << 16) | (g << 8) | b;
  349. }
  350. }
  351. /**
  352. * Reads GIF content blocks. Called from the background thread.
  353. *
  354. * @return true if the next frame has been parsed successfully, false if EOF
  355. * has been reached
  356. */
  357. private void readNextFrame() {
  358. // Don't clear the image if it is a terminator.
  359. if ((mData[mPosition] & 0xff) == 0x3b) {
  360. mEndOfFile = true;
  361. return;
  362. }
  363. disposeOfLastFrame();
  364. mDisposalMethod = DISPOSAL_METHOD_UNKNOWN;
  365. mTransparency = false;
  366. mEndOfFile = false;
  367. mNextFrameDelay = 100;
  368. mLocalColorTable = null;
  369. while (true) {
  370. int code = mData[mPosition++] & 0xff;
  371. switch (code) {
  372. case 0: // Empty block, ignore
  373. break;
  374. case 0x21: // Extension. Extensions precede the corresponding image.
  375. code = mData[mPosition++] & 0xff;
  376. switch (code) {
  377. case 0xf9: // graphics control extension
  378. readGraphicControlExt();
  379. break;
  380. case 0xff: // application extension
  381. readBlock();
  382. boolean netscape = true;
  383. for (int i = 0; i < NETSCAPE2_0.length; i++) {
  384. if (mBlock[i] != NETSCAPE2_0[i]) {
  385. netscape = false;
  386. break;
  387. }
  388. }
  389. if (netscape) {
  390. readNetscapeExtension();
  391. } else {
  392. skip(); // don't care
  393. }
  394. break;
  395. case 0xfe:// comment extension
  396. skip();
  397. break;
  398. case 0x01:// plain text extension
  399. skip();
  400. break;
  401. default: // uninteresting extension
  402. skip();
  403. }
  404. break;
  405. case 0x2C: // Image separator
  406. readBitmap();
  407. return;
  408. case 0x3b: // Terminator
  409. mEndOfFile = true;
  410. return;
  411. default: // We don't know what this is. Just skip it.
  412. break;
  413. }
  414. }
  415. }
  416. /**
  417. * Disposes of the previous frame. Called from the background thread.
  418. */
  419. private void disposeOfLastFrame() {
  420. if (mFirstFrame) {
  421. mFirstFrame = false;
  422. return;
  423. }
  424. switch (mDisposalMethod) {
  425. case DISPOSAL_METHOD_UNKNOWN:
  426. case DISPOSAL_METHOD_LEAVE: {
  427. mBackupSaved = false;
  428. break;
  429. }
  430. case DISPOSAL_METHOD_RESTORE: {
  431. if (mBackupSaved) {
  432. System.arraycopy(mBackup, 0, mColors, 0, mBackup.length);
  433. }
  434. break;
  435. }
  436. case DISPOSAL_METHOD_BACKGROUND: {
  437. mBackupSaved = false;
  438. // Fill last image rect area with background color
  439. int color = 0;
  440. if (!mTransparency) {
  441. color = mBackgroundColor;
  442. }
  443. for (int i = 0; i < mFrameHeight; i++) {
  444. int n1 = (mFrameY + i) * mIntrinsicWidth + mFrameX;
  445. int n2 = n1 + mFrameWidth;
  446. for (int k = n1; k < n2; k++) {
  447. mColors[k] = color;
  448. }
  449. }
  450. break;
  451. }
  452. }
  453. }
  454. /**
  455. * Reads Graphics Control Extension values. Called from the background thread.
  456. */
  457. private void readGraphicControlExt() {
  458. mPosition++; // Block size, fixed
  459. int packed = mData[mPosition++] & 0xff; // Packed fields
  460. mDisposalMethod = (packed & 0x1c) >> 2; // Disposal method
  461. mTransparency = (packed & 1) != 0;
  462. mNextFrameDelay = readShort() * 10; // Delay in milliseconds
  463. // It seems that there are broken tools out there that set a 0ms or 10ms
  464. // timeout when they really want a "default" one.
  465. // Following WebKit's lead (http://trac.webkit.org/changeset/73295)
  466. // we use 10 frames per second as the default frame rate.
  467. if (mNextFrameDelay <= 10) {
  468. mNextFrameDelay = 100;
  469. }
  470. mTransparentColorIndex = mData[mPosition++] & 0xff;
  471. mPosition++; // Block terminator - ignore
  472. }
  473. /**
  474. * Reads Netscape extension to obtain iteration count. Called from the background thread.
  475. */
  476. private void readNetscapeExtension() {
  477. int count;
  478. do {
  479. count = readBlock();
  480. } while ((count > 0) && !mError);
  481. }
  482. /**
  483. * Reads next frame image. Called from the background thread.
  484. */
  485. private void readBitmap() {
  486. mFrameX = readShort(); // (sub)image position & size
  487. mFrameY = readShort();
  488. int width = readShort();
  489. int height = readShort();
  490. // Clamp the frame dimensions to the intrinsic dimensions.
  491. mFrameWidth = Math.min(width, mIntrinsicWidth - mFrameX);
  492. mFrameHeight = Math.min(height, mIntrinsicHeight - mFrameY);
  493. // The frame step is set to the specfied frame width before clamping.
  494. mFrameStep = width;
  495. // Increase the size of the decoding buffer if necessary.
  496. int framePixelCount = width * height;
  497. if (framePixelCount > mPixels.length) {
  498. mPixels = new byte[framePixelCount];
  499. }
  500. int packed = mData[mPosition++] & 0xff;
  501. // 3 - sort flag
  502. // 4-5 - reserved lctSize = 2 << (packed & 7);
  503. // 6-8 - local color table size
  504. mInterlace = (packed & 0x40) != 0;
  505. mLocalColorTableUsed = (packed & 0x80) != 0; // 1 - local color table flag interlace
  506. mLocalColorTableSize = (int) Math.pow(2, (packed & 0x07) + 1);
  507. if (mLocalColorTableUsed) {
  508. if (mLocalColorTable == null) {
  509. mLocalColorTable = new int[256];
  510. }
  511. readColorTable(mLocalColorTable, mLocalColorTableSize);
  512. mActiveColorTable = mLocalColorTable;
  513. } else {
  514. mActiveColorTable = mGifImage.mGlobalColorTable;
  515. if (mGifImage.mBackgroundIndex == mTransparentColorIndex) {
  516. mBackgroundColor = 0;
  517. }
  518. }
  519. int savedColor = 0;
  520. if (mTransparency) {
  521. savedColor = mActiveColorTable[mTransparentColorIndex];
  522. mActiveColorTable[mTransparentColorIndex] = 0;
  523. }
  524. if (mActiveColorTable == null) {
  525. mError = true;
  526. }
  527. if (mError) {
  528. return;
  529. }
  530. decodeBitmapData();
  531. skip();
  532. if (mError) {
  533. return;
  534. }
  535. if (mDisposalMethod == DISPOSAL_METHOD_RESTORE) {
  536. backupFrame();
  537. }
  538. populateImageData();
  539. if (mTransparency) {
  540. mActiveColorTable[mTransparentColorIndex] = savedColor;
  541. }
  542. mFrameCount++;
  543. }
  544. /**
  545. * Stores the relevant portion of the current frame so that it can be restored
  546. * before the next frame is rendered. Called from the background thread.
  547. */
  548. private void backupFrame() {
  549. if (mBackupSaved) {
  550. return;
  551. }
  552. if (mBackup == null) {
  553. mBackup = null;
  554. try {
  555. mBackup = new int[mColors.length];
  556. } catch (OutOfMemoryError e) {
  557. Log.e(TAG, "GifDrawable.backupFrame threw an OOME", e);
  558. }
  559. }
  560. if (mBackup != null) {
  561. System.arraycopy(mColors, 0, mBackup, 0, mColors.length);
  562. mBackupSaved = true;
  563. }
  564. }
  565. /**
  566. * Decodes LZW image data into pixel array. Called from the background thread.
  567. */
  568. private void decodeBitmapData() {
  569. int npix = mFrameWidth * mFrameHeight;
  570. // Initialize GIF data stream decoder.
  571. int dataSize = mData[mPosition++] & 0xff;
  572. int clear = 1 << dataSize;
  573. int endOfInformation = clear + 1;
  574. int available = clear + 2;
  575. int oldCode = -1;
  576. int codeSize = dataSize + 1;
  577. int codeMask = (1 << codeSize) - 1;
  578. for (int code = 0; code < clear; code++) {
  579. mPrefix[code] = 0; // XXX ArrayIndexOutOfBoundsException
  580. mSuffix[code] = (byte) code;
  581. }
  582. // Decode GIF pixel stream.
  583. int datum = 0;
  584. int bits = 0;
  585. int first = 0;
  586. int top = 0;
  587. int pi = 0;
  588. while (pi < npix) {
  589. int blockSize = mData[mPosition++] & 0xff;
  590. if (blockSize == 0) {
  591. break;
  592. }
  593. int blockEnd = mPosition + blockSize;
  594. while (mPosition < blockEnd) {
  595. datum += (mData[mPosition++] & 0xff) << bits;
  596. bits += 8;
  597. while (bits >= codeSize) {
  598. // Get the next code.
  599. int code = datum & codeMask;
  600. datum >>= codeSize;
  601. bits -= codeSize;
  602. // Interpret the code
  603. if (code == clear) {
  604. // Reset decoder.
  605. codeSize = dataSize + 1;
  606. codeMask = (1 << codeSize) - 1;
  607. available = clear + 2;
  608. oldCode = -1;
  609. continue;
  610. }
  611. // Check for explicit end-of-stream
  612. if (code == endOfInformation) {
  613. mPosition = blockEnd;
  614. return;
  615. }
  616. if (oldCode == -1) {
  617. mPixels[pi++] = mSuffix[code];
  618. oldCode = code;
  619. first = code;
  620. continue;
  621. }
  622. int inCode = code;
  623. if (code >= available) {
  624. mPixelStack[top++] = (byte) first;
  625. code = oldCode;
  626. if (top == MAX_BITS) {
  627. mError = true;
  628. return;
  629. }
  630. }
  631. while (code >= clear) {
  632. if (code >= MAX_BITS || code == mPrefix[code]) {
  633. mError = true;
  634. return;
  635. }
  636. mPixelStack[top++] = mSuffix[code];
  637. code = mPrefix[code];
  638. if (top == MAX_BITS) {
  639. mError = true;
  640. return;
  641. }
  642. }
  643. first = mSuffix[code];
  644. mPixelStack[top++] = (byte) first;
  645. // Add new code to the dictionary
  646. if (available < MAX_STACK_SIZE) {
  647. mPrefix[available] = (short) oldCode;
  648. mSuffix[available] = (byte) first;
  649. available++;
  650. if (((available & codeMask) == 0) && (available < MAX_STACK_SIZE)) {
  651. codeSize++;
  652. codeMask += available;
  653. }
  654. }
  655. oldCode = inCode;
  656. // Drain the pixel stack.
  657. do {
  658. mPixels[pi++] = mPixelStack[--top];
  659. } while (top > 0);
  660. }
  661. }
  662. }
  663. while (pi < npix) {
  664. mPixels[pi++] = 0; // clear missing pixels
  665. }
  666. }
  667. /**
  668. * Populates the color array with pixels for the next frame.
  669. */
  670. private void populateImageData() {
  671. // Copy each source line to the appropriate place in the destination
  672. int pass = 1;
  673. int inc = 8;
  674. int iline = 0;
  675. for (int i = 0; i < mFrameHeight; i++) {
  676. int line = i;
  677. if (mInterlace) {
  678. if (iline >= mFrameHeight) {
  679. pass++;
  680. switch (pass) {
  681. case 2:
  682. iline = 4;
  683. break;
  684. case 3:
  685. iline = 2;
  686. inc = 4;
  687. break;
  688. case 4:
  689. iline = 1;
  690. inc = 2;
  691. break;
  692. default:
  693. break;
  694. }
  695. }
  696. line = iline;
  697. iline += inc;
  698. }
  699. line += mFrameY;
  700. if (line < mIntrinsicHeight) {
  701. int k = line * mIntrinsicWidth;
  702. int dx = k + mFrameX; // start of line in dest
  703. int dlim = dx + mFrameWidth; // end of dest line
  704. // It is unnecesary to test if dlim is beyond the edge of the destination line,
  705. // since mFrameWidth is clamped to a maximum of mIntrinsicWidth - mFrameX.
  706. int sx = i * mFrameStep; // start of line in source
  707. while (dx < dlim) {
  708. // map color and insert in destination
  709. int index = mPixels[sx++] & 0xff;
  710. int c = mActiveColorTable[index];
  711. if (c != 0) {
  712. mColors[dx] = c;
  713. }
  714. dx++;
  715. }
  716. }
  717. }
  718. }
  719. /**
  720. * Reads next variable length block from input. Called from the background thread.
  721. *
  722. * @return number of bytes stored in "buffer"
  723. */
  724. private int readBlock() {
  725. int blockSize = mData[mPosition++] & 0xff;
  726. if (blockSize > 0) {
  727. System.arraycopy(mData, mPosition, mBlock, 0, blockSize);
  728. mPosition += blockSize;
  729. }
  730. return blockSize;
  731. }
  732. /**
  733. * Reads next 16-bit value, LSB first. Called from the background thread.
  734. */
  735. private int readShort() {
  736. // read 16-bit value, LSB first
  737. int byte1 = mData[mPosition++] & 0xff;
  738. int byte2 = mData[mPosition++] & 0xff;
  739. return byte1 | (byte2 << 8);
  740. }
  741. /**
  742. * Skips variable length blocks up to and including next zero length block.
  743. * Called from the background thread.
  744. */
  745. private void skip() {
  746. int blockSize;
  747. do {
  748. blockSize = mData[mPosition++] & 0xff;
  749. mPosition += blockSize;
  750. } while (blockSize > 0);
  751. }
  752. @Override
  753. public boolean handleMessage(Message msg) {
  754. if (msg.what == BaseGifDrawable.READ_FRAME_RESP) {
  755. mFrameDelay = msg.arg1;
  756. if (mBitmap != null) {
  757. mBitmap.setPixels(mColors, 0, mIntrinsicWidth,
  758. 0, 0, mIntrinsicWidth, mIntrinsicHeight);
  759. postProcessFrame(mBitmap);
  760. mFirstFrameReady = true;
  761. mScheduled = false;
  762. invalidateSelf();
  763. }
  764. return true;
  765. }
  766. return false;
  767. }
  768. /**
  769. * Gives a subclass a chance to apply changes to the mutable bitmap
  770. * before showing the frame.
  771. */
  772. protected void postProcessFrame(Bitmap bitmap) {
  773. }
  774. /**
  775. * Background thread that handles reading and decoding frames of GIF images.
  776. */
  777. private static class DecoderThread extends HandlerThread
  778. implements android.os.Handler.Callback {
  779. private static final String DECODER_THREAD_NAME = "GifDecoder";
  780. public DecoderThread() {
  781. super(DECODER_THREAD_NAME);
  782. }
  783. @Override
  784. public boolean handleMessage(Message msg) {
  785. BaseGifDrawable gif = (BaseGifDrawable) msg.obj;
  786. if (gif == null || gif.mBitmap == null || gif.mRecycled) {
  787. return true;
  788. }
  789. switch (msg.what) {
  790. case READ_FRAME_REQ:
  791. // Processed on background thread
  792. do {
  793. try {
  794. gif.readNextFrame();
  795. } catch (ArrayIndexOutOfBoundsException e) {
  796. gif.mEndOfFile = true;
  797. }
  798. // Check for EOF
  799. if (gif.mEndOfFile) {
  800. if (gif.mFrameCount == 0) {
  801. // could not read first frame
  802. gif.mError = true;
  803. } else if (gif.mFrameCount > 1) {
  804. if (gif.mLoopCount == 0 || ++gif.mLoopIndex < gif.mLoopCount) {
  805. // Repeat the animation
  806. gif.reset();
  807. } else {
  808. gif.mDone = true;
  809. }
  810. } else {
  811. // Only one frame. Mark as done.
  812. gif.mDone = true;
  813. }
  814. }
  815. } while (gif.mEndOfFile && !gif.mError && !gif.mDone);
  816. gif.mHandler.sendMessage(gif.mHandler.obtainMessage(READ_FRAME_RESP,
  817. gif.mNextFrameDelay, 0));
  818. return true;
  819. case RESET_DECODER:
  820. gif.reset();
  821. return true;
  822. }
  823. return false;
  824. }
  825. }
  826. }