1a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik/* 2a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * Copyright (C) 2013 The Android Open Source Project 3a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * 4a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * Licensed under the Apache License, Version 2.0 (the "License"); 5a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * you may not use this file except in compliance with the License. 6a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * You may obtain a copy of the License at 7a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * 8a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * http://www.apache.org/licenses/LICENSE-2.0 9a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * 10a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * Unless required by applicable law or agreed to in writing, software 11a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * distributed under the License is distributed on an "AS IS" BASIS, 12a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * See the License for the specific language governing permissions and 14a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * limitations under the License. 15a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik */ 16a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 17a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikpackage android.support.rastermill; 18a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 19a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport android.graphics.Bitmap; 20537754df0d7348949e2324a91cc9e4df7571d7bfChris Craikimport android.graphics.BitmapShader; 21a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport android.graphics.Canvas; 22a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport android.graphics.ColorFilter; 23a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport android.graphics.Paint; 24a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport android.graphics.PixelFormat; 25a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport android.graphics.Rect; 26537754df0d7348949e2324a91cc9e4df7571d7bfChris Craikimport android.graphics.RectF; 27537754df0d7348949e2324a91cc9e4df7571d7bfChris Craikimport android.graphics.Shader; 28a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport android.graphics.drawable.Animatable; 29a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport android.graphics.drawable.Drawable; 30a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport android.os.Handler; 31a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport android.os.HandlerThread; 323105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craikimport android.os.Process; 33a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport android.os.SystemClock; 34769d2c1df936a328f324a953f3cdf5901dd99078Chris Craikimport android.util.Log; 35a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 36a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikpublic class FrameSequenceDrawable extends Drawable implements Animatable, Runnable { 37769d2c1df936a328f324a953f3cdf5901dd99078Chris Craik private static final String TAG = "FrameSequence"; 38e532fb97ba883a488bb751d51732274e03a052afChris Craik /** 39e532fb97ba883a488bb751d51732274e03a052afChris Craik * These constants are chosen to imitate common browser behavior for WebP/GIF. 40e532fb97ba883a488bb751d51732274e03a052afChris Craik * If other decoders are added, this behavior should be moved into the WebP/GIF decoders. 41e532fb97ba883a488bb751d51732274e03a052afChris Craik * 42e532fb97ba883a488bb751d51732274e03a052afChris Craik * Note that 0 delay is undefined behavior in the GIF standard. 43e532fb97ba883a488bb751d51732274e03a052afChris Craik */ 44e532fb97ba883a488bb751d51732274e03a052afChris Craik private static final long MIN_DELAY_MS = 20; 45e532fb97ba883a488bb751d51732274e03a052afChris Craik private static final long DEFAULT_DELAY_MS = 100; 46e532fb97ba883a488bb751d51732274e03a052afChris Craik 47a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static final Object sLock = new Object(); 48a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static HandlerThread sDecodingThread; 49a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static Handler sDecodingThreadHandler; 50a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static void initializeDecodingThread() { 51a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (sLock) { 52a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (sDecodingThread != null) return; 53a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 543105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik sDecodingThread = new HandlerThread("FrameSequence decoding thread", 553105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik Process.THREAD_PRIORITY_BACKGROUND); 56a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik sDecodingThread.start(); 57a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik sDecodingThreadHandler = new Handler(sDecodingThread.getLooper()); 58a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 59a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 60a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 61e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public static interface OnFinishedListener { 62e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 63e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Called when a FrameSequenceDrawable has finished looping. 64e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * 65e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Note that this is will not be called if the drawable is explicitly 66e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * stopped, or marked invisible. 67e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 68e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public abstract void onFinished(FrameSequenceDrawable drawable); 69e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 70e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 713105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik public static interface BitmapProvider { 723105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik /** 733105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik * Called by FrameSequenceDrawable to aquire an 8888 Bitmap with minimum dimensions. 743105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik */ 753105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik public abstract Bitmap acquireBitmap(int minWidth, int minHeight); 763105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 773105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik /** 783105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik * Called by FrameSequenceDrawable to release a Bitmap it no longer needs. The Bitmap 793105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik * will no longer be used at all by the drawable, so it is safe to reuse elsewhere. 804eb541aff092a057b27b917f09d33aba226dffedChris Craik * 814eb541aff092a057b27b917f09d33aba226dffedChris Craik * This method may be called by FrameSequenceDrawable on any thread. 823105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik */ 833105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik public abstract void releaseBitmap(Bitmap bitmap); 843105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 853105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 863105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik private static BitmapProvider sAllocatingBitmapProvider = new BitmapProvider() { 873105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik @Override 883105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik public Bitmap acquireBitmap(int minWidth, int minHeight) { 893105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik return Bitmap.createBitmap(minWidth, minHeight, Bitmap.Config.ARGB_8888); 903105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 913105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 923105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik @Override 93b19ecfd9cfa2c417034403adf7039edf9fe59327Chris Craik public void releaseBitmap(Bitmap bitmap) {} 943105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik }; 953105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 96e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 97e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Register a callback to be invoked when a FrameSequenceDrawable finishes looping. 98e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * 993105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik * @see #setLoopBehavior(int) 100e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 101e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public void setOnFinishedListener(OnFinishedListener onFinishedListener) { 102e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik mOnFinishedListener = onFinishedListener; 103e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 104e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 105e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 1063a208a6c6340cc636ed22919973264675da5eab0Chris Craik * Loop a finite number of times, which can be set using setLoopCount. Default to loop once. 107e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 1083a208a6c6340cc636ed22919973264675da5eab0Chris Craik public static final int LOOP_FINITE = 1; 109e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 110e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 111e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Loop continuously. The OnFinishedListener will never be called. 112e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 113e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public static final int LOOP_INF = 2; 114e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 115e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 116e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Use loop count stored in source data, or LOOP_ONCE if not present. 117e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 118e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public static final int LOOP_DEFAULT = 3; 119e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 120e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 1213a208a6c6340cc636ed22919973264675da5eab0Chris Craik * Loop only once. 1223a208a6c6340cc636ed22919973264675da5eab0Chris Craik * 1233a208a6c6340cc636ed22919973264675da5eab0Chris Craik * @deprecated Use LOOP_FINITE instead. 1243a208a6c6340cc636ed22919973264675da5eab0Chris Craik */ 1253a208a6c6340cc636ed22919973264675da5eab0Chris Craik @Deprecated 1263a208a6c6340cc636ed22919973264675da5eab0Chris Craik public static final int LOOP_ONCE = LOOP_FINITE; 1273a208a6c6340cc636ed22919973264675da5eab0Chris Craik 1283a208a6c6340cc636ed22919973264675da5eab0Chris Craik /** 129e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Define looping behavior of frame sequence. 130e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * 1313a208a6c6340cc636ed22919973264675da5eab0Chris Craik * Must be one of LOOP_ONCE, LOOP_INF, LOOP_DEFAULT, or LOOP_FINITE. 132e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 133e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public void setLoopBehavior(int loopBehavior) { 134e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik mLoopBehavior = loopBehavior; 135e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 136e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 1373a208a6c6340cc636ed22919973264675da5eab0Chris Craik /** 1383a208a6c6340cc636ed22919973264675da5eab0Chris Craik * Set the number of loops in LOOP_FINITE mode. The number must be a postive integer. 1393a208a6c6340cc636ed22919973264675da5eab0Chris Craik */ 1403a208a6c6340cc636ed22919973264675da5eab0Chris Craik public void setLoopCount(int loopCount) { 1413a208a6c6340cc636ed22919973264675da5eab0Chris Craik mLoopCount = loopCount; 1423a208a6c6340cc636ed22919973264675da5eab0Chris Craik } 1433a208a6c6340cc636ed22919973264675da5eab0Chris Craik 144a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private final FrameSequence mFrameSequence; 145a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private final FrameSequence.State mFrameSequenceState; 146a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 147a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private final Paint mPaint; 148537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik private BitmapShader mFrontBitmapShader; 149537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik private BitmapShader mBackBitmapShader; 150958761ceb97982b510d3059d31ba8c45700a1654Chris Craik private final Rect mSrcRect; 151537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik private boolean mCircleMaskEnabled; 152a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 153a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik //Protects the fields below 154a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private final Object mLock = new Object(); 155a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 1564eb541aff092a057b27b917f09d33aba226dffedChris Craik private final BitmapProvider mBitmapProvider; 1574eb541aff092a057b27b917f09d33aba226dffedChris Craik private boolean mDestroyed = false; 158a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private Bitmap mFrontBitmap; 159a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private Bitmap mBackBitmap; 160a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 161a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static final int STATE_SCHEDULED = 1; 162a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static final int STATE_DECODING = 2; 163a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static final int STATE_WAITING_TO_SWAP = 3; 164a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static final int STATE_READY_TO_SWAP = 4; 165a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 166a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private int mState; 167e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private int mCurrentLoop; 168e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private int mLoopBehavior = LOOP_DEFAULT; 1693a208a6c6340cc636ed22919973264675da5eab0Chris Craik private int mLoopCount = 1; 170a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 171a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private long mLastSwap; 1723105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik private long mNextSwap; 173a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private int mNextFrameToDecode; 174e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private OnFinishedListener mOnFinishedListener; 175a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 176958761ceb97982b510d3059d31ba8c45700a1654Chris Craik private RectF mTempRectF = new RectF(); 177958761ceb97982b510d3059d31ba8c45700a1654Chris Craik 178a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik /** 179a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * Runs on decoding thread, only modifies mBackBitmap's pixels 180a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik */ 181a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private Runnable mDecodeRunnable = new Runnable() { 182a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 183a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void run() { 184a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik int nextFrame; 185a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik Bitmap bitmap; 186a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 1874eb541aff092a057b27b917f09d33aba226dffedChris Craik if (mDestroyed) return; 1883105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 189a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik nextFrame = mNextFrameToDecode; 190a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (nextFrame < 0) { 191a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return; 192a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 193a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik bitmap = mBackBitmap; 194a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mState = STATE_DECODING; 195a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 196a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik int lastFrame = nextFrame - 2; 197769d2c1df936a328f324a953f3cdf5901dd99078Chris Craik boolean exceptionDuringDecode = false; 198769d2c1df936a328f324a953f3cdf5901dd99078Chris Craik long invalidateTimeMs = 0; 199769d2c1df936a328f324a953f3cdf5901dd99078Chris Craik try { 200769d2c1df936a328f324a953f3cdf5901dd99078Chris Craik invalidateTimeMs = mFrameSequenceState.getFrame(nextFrame, bitmap, lastFrame); 201769d2c1df936a328f324a953f3cdf5901dd99078Chris Craik } catch(Exception e) { 202769d2c1df936a328f324a953f3cdf5901dd99078Chris Craik // Exception during decode: continue, but delay next frame indefinitely. 203769d2c1df936a328f324a953f3cdf5901dd99078Chris Craik Log.e(TAG, "exception during decode: " + e); 204769d2c1df936a328f324a953f3cdf5901dd99078Chris Craik exceptionDuringDecode = true; 205769d2c1df936a328f324a953f3cdf5901dd99078Chris Craik } 206537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik 207e532fb97ba883a488bb751d51732274e03a052afChris Craik if (invalidateTimeMs < MIN_DELAY_MS) { 208e532fb97ba883a488bb751d51732274e03a052afChris Craik invalidateTimeMs = DEFAULT_DELAY_MS; 209e532fb97ba883a488bb751d51732274e03a052afChris Craik } 210a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 21138f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik boolean schedule = false; 21238f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik Bitmap bitmapToRelease = null; 213a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 21438f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik if (mDestroyed) { 21538f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik bitmapToRelease = mBackBitmap; 21638f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik mBackBitmap = null; 21738f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik } else if (mNextFrameToDecode >= 0 && mState == STATE_DECODING) { 21838f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik schedule = true; 219769d2c1df936a328f324a953f3cdf5901dd99078Chris Craik mNextSwap = exceptionDuringDecode ? Long.MAX_VALUE : invalidateTimeMs + mLastSwap; 22038f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik mState = STATE_WAITING_TO_SWAP; 22138f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik } 22238f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik } 22338f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik if (schedule) { 22438f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik scheduleSelf(FrameSequenceDrawable.this, mNextSwap); 22538f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik } 22638f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik if (bitmapToRelease != null) { 22738f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik // destroy the bitmap here, since there's no safe way to get back to 22838f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik // drawable thread - drawable is likely detached, so schedule is noop. 22938f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik mBitmapProvider.releaseBitmap(bitmapToRelease); 230a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 231a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 232a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik }; 233a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 2341527ca54acf175853ba6eb3b1e0a3a674789e209Chris Craik private Runnable mFinishedCallbackRunnable = new Runnable() { 235e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik @Override 236e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public void run() { 2371527ca54acf175853ba6eb3b1e0a3a674789e209Chris Craik synchronized (mLock) { 2381527ca54acf175853ba6eb3b1e0a3a674789e209Chris Craik mNextFrameToDecode = -1; 2391527ca54acf175853ba6eb3b1e0a3a674789e209Chris Craik mState = 0; 2401527ca54acf175853ba6eb3b1e0a3a674789e209Chris Craik } 241e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik if (mOnFinishedListener != null) { 242e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik mOnFinishedListener.onFinished(FrameSequenceDrawable.this); 243e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 244e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 245e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik }; 246a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 2473105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik private static Bitmap acquireAndValidateBitmap(BitmapProvider bitmapProvider, 2483105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik int minWidth, int minHeight) { 2493105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik Bitmap bitmap = bitmapProvider.acquireBitmap(minWidth, minHeight); 2503105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 2513105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik if (bitmap.getWidth() < minWidth 2523105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik || bitmap.getHeight() < minHeight 2533105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik || bitmap.getConfig() != Bitmap.Config.ARGB_8888) { 2543105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik throw new IllegalArgumentException("Invalid bitmap provided"); 2553105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 2563105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 2573105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik return bitmap; 2583105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 2593105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 260a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public FrameSequenceDrawable(FrameSequence frameSequence) { 2613105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik this(frameSequence, sAllocatingBitmapProvider); 2623105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 2633105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 2643105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik public FrameSequenceDrawable(FrameSequence frameSequence, BitmapProvider bitmapProvider) { 2654eb541aff092a057b27b917f09d33aba226dffedChris Craik if (frameSequence == null || bitmapProvider == null) throw new IllegalArgumentException(); 266a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 267a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mFrameSequence = frameSequence; 268a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mFrameSequenceState = frameSequence.createState(); 269a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik final int width = frameSequence.getWidth(); 270a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik final int height = frameSequence.getHeight(); 271a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 2724eb541aff092a057b27b917f09d33aba226dffedChris Craik mBitmapProvider = bitmapProvider; 2733105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik mFrontBitmap = acquireAndValidateBitmap(bitmapProvider, width, height); 2743105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik mBackBitmap = acquireAndValidateBitmap(bitmapProvider, width, height); 275a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mSrcRect = new Rect(0, 0, width, height); 276a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mPaint = new Paint(); 277a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mPaint.setFilterBitmap(true); 278a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 279537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik mFrontBitmapShader 280537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik = new BitmapShader(mFrontBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); 281537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik mBackBitmapShader 282537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik = new BitmapShader(mBackBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); 283537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik 284a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mLastSwap = 0; 285a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 286a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mNextFrameToDecode = -1; 287a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mFrameSequenceState.getFrame(0, mFrontBitmap, -1); 288a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik initializeDecodingThread(); 289a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 290a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 291537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik /** 292537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik * Pass true to mask the shape of the animated drawing content to a circle. 293537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik * 294537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik * <p> The masking circle will be the largest circle contained in the Drawable's bounds. 295537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik * Masking is done with BitmapShader, incurring minimal additional draw cost. 296537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik */ 297537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik public final void setCircleMaskEnabled(boolean circleMaskEnabled) { 298958761ceb97982b510d3059d31ba8c45700a1654Chris Craik if (mCircleMaskEnabled != circleMaskEnabled) { 299958761ceb97982b510d3059d31ba8c45700a1654Chris Craik mCircleMaskEnabled = circleMaskEnabled; 300958761ceb97982b510d3059d31ba8c45700a1654Chris Craik // Anti alias only necessary when using circular mask 301958761ceb97982b510d3059d31ba8c45700a1654Chris Craik mPaint.setAntiAlias(circleMaskEnabled); 302958761ceb97982b510d3059d31ba8c45700a1654Chris Craik invalidateSelf(); 303958761ceb97982b510d3059d31ba8c45700a1654Chris Craik } 304958761ceb97982b510d3059d31ba8c45700a1654Chris Craik } 305958761ceb97982b510d3059d31ba8c45700a1654Chris Craik 306958761ceb97982b510d3059d31ba8c45700a1654Chris Craik public final boolean getCircleMaskEnabled() { 307958761ceb97982b510d3059d31ba8c45700a1654Chris Craik return mCircleMaskEnabled; 308537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik } 309537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik 3104eb541aff092a057b27b917f09d33aba226dffedChris Craik private void checkDestroyedLocked() { 3114eb541aff092a057b27b917f09d33aba226dffedChris Craik if (mDestroyed) { 3123105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik throw new IllegalStateException("Cannot perform operation on recycled drawable"); 3133105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 3143105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 3153105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 3164eb541aff092a057b27b917f09d33aba226dffedChris Craik public boolean isDestroyed() { 3173105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik synchronized (mLock) { 3184eb541aff092a057b27b917f09d33aba226dffedChris Craik return mDestroyed; 3193105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 3203105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 3213105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 3223105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik /** 3233105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik * Marks the drawable as permanently recycled (and thus unusable), and releases any owned 3244eb541aff092a057b27b917f09d33aba226dffedChris Craik * Bitmaps drawable to its BitmapProvider, if attached. 3253105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik * 3264eb541aff092a057b27b917f09d33aba226dffedChris Craik * If no BitmapProvider is attached to the drawable, recycle() is called on the Bitmaps. 3273105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik */ 3284eb541aff092a057b27b917f09d33aba226dffedChris Craik public void destroy() { 32938f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik if (mBitmapProvider == null) { 3303105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik throw new IllegalStateException("BitmapProvider must be non-null"); 3313105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 3323105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 3333105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik Bitmap bitmapToReleaseA; 33438f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik Bitmap bitmapToReleaseB = null; 3353105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik synchronized (mLock) { 3364eb541aff092a057b27b917f09d33aba226dffedChris Craik checkDestroyedLocked(); 3373105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 3383105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik bitmapToReleaseA = mFrontBitmap; 3393105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik mFrontBitmap = null; 34038f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik 34138f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik if (mState != STATE_DECODING) { 34238f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik bitmapToReleaseB = mBackBitmap; 34338f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik mBackBitmap = null; 34438f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik } 34538f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik 3464eb541aff092a057b27b917f09d33aba226dffedChris Craik mDestroyed = true; 3473105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 3483105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 3494eb541aff092a057b27b917f09d33aba226dffedChris Craik // For simplicity and safety, we don't destroy the state object here 35038f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik mBitmapProvider.releaseBitmap(bitmapToReleaseA); 35138f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik if (bitmapToReleaseB != null) { 35238f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik mBitmapProvider.releaseBitmap(bitmapToReleaseB); 35338f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik } 3543105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 3553105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 356a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 357a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik protected void finalize() throws Throwable { 358a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik try { 3594eb541aff092a057b27b917f09d33aba226dffedChris Craik mFrameSequenceState.destroy(); 360a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } finally { 361a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik super.finalize(); 362a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 363a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 364a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 365a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 366a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void draw(Canvas canvas) { 367a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 3684eb541aff092a057b27b917f09d33aba226dffedChris Craik checkDestroyedLocked(); 3693105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik if (mState == STATE_WAITING_TO_SWAP) { 3703105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik // may have failed to schedule mark ready runnable, 3713105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik // so go ahead and swap if swapping is due 3723105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik if (mNextSwap - SystemClock.uptimeMillis() <= 0) { 3733105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik mState = STATE_READY_TO_SWAP; 3743105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 3753105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 3763105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 377a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (isRunning() && mState == STATE_READY_TO_SWAP) { 378a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik // Because draw has occurred, the view system is guaranteed to no longer hold a 379a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik // reference to the old mFrontBitmap, so we now use it to produce the next frame 380a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik Bitmap tmp = mBackBitmap; 381a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mBackBitmap = mFrontBitmap; 382a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mFrontBitmap = tmp; 383a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 384537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik BitmapShader tmpShader = mBackBitmapShader; 385537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik mBackBitmapShader = mFrontBitmapShader; 386537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik mFrontBitmapShader = tmpShader; 387537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik 388a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mLastSwap = SystemClock.uptimeMillis(); 389e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 390e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik boolean continueLooping = true; 391e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik if (mNextFrameToDecode == mFrameSequence.getFrameCount() - 1) { 392e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik mCurrentLoop++; 393b23d7abde766b0575d6a2592ea828d2d8596ecd4Chris Craik if ((mLoopBehavior == LOOP_FINITE && mCurrentLoop == mLoopCount) || 394b23d7abde766b0575d6a2592ea828d2d8596ecd4Chris Craik (mLoopBehavior == LOOP_DEFAULT && mCurrentLoop == mFrameSequence.getDefaultLoopCount())) { 395e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik continueLooping = false; 396e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 397e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 398e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 399e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik if (continueLooping) { 400e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik scheduleDecodeLocked(); 401e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } else { 4021527ca54acf175853ba6eb3b1e0a3a674789e209Chris Craik scheduleSelf(mFinishedCallbackRunnable, 0); 403e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 404a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 405a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 406a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 407537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik if (mCircleMaskEnabled) { 408958761ceb97982b510d3059d31ba8c45700a1654Chris Craik final Rect bounds = getBounds(); 409958761ceb97982b510d3059d31ba8c45700a1654Chris Craik final int bitmapWidth = getIntrinsicWidth(); 410958761ceb97982b510d3059d31ba8c45700a1654Chris Craik final int bitmapHeight = getIntrinsicHeight(); 411958761ceb97982b510d3059d31ba8c45700a1654Chris Craik final float scaleX = 1.0f * bounds.width() / bitmapWidth; 412958761ceb97982b510d3059d31ba8c45700a1654Chris Craik final float scaleY = 1.0f * bounds.height() / bitmapHeight; 413958761ceb97982b510d3059d31ba8c45700a1654Chris Craik 414958761ceb97982b510d3059d31ba8c45700a1654Chris Craik canvas.save(); 415958761ceb97982b510d3059d31ba8c45700a1654Chris Craik // scale and translate to account for bounds, so we can operate in intrinsic 416958761ceb97982b510d3059d31ba8c45700a1654Chris Craik // width/height (so it's valid to use an unscaled bitmap shader) 417958761ceb97982b510d3059d31ba8c45700a1654Chris Craik canvas.translate(bounds.left, bounds.top); 418958761ceb97982b510d3059d31ba8c45700a1654Chris Craik canvas.scale(scaleX, scaleY); 419958761ceb97982b510d3059d31ba8c45700a1654Chris Craik 420958761ceb97982b510d3059d31ba8c45700a1654Chris Craik final float unscaledCircleDiameter = Math.min(bounds.width(), bounds.height()); 421958761ceb97982b510d3059d31ba8c45700a1654Chris Craik final float scaledDiameterX = unscaledCircleDiameter / scaleX; 422958761ceb97982b510d3059d31ba8c45700a1654Chris Craik final float scaledDiameterY = unscaledCircleDiameter / scaleY; 423958761ceb97982b510d3059d31ba8c45700a1654Chris Craik 424958761ceb97982b510d3059d31ba8c45700a1654Chris Craik // Want to draw a circle, but we have to compensate for canvas scale 425958761ceb97982b510d3059d31ba8c45700a1654Chris Craik mTempRectF.set( 426958761ceb97982b510d3059d31ba8c45700a1654Chris Craik (bitmapWidth - scaledDiameterX) / 2.0f, 427958761ceb97982b510d3059d31ba8c45700a1654Chris Craik (bitmapHeight - scaledDiameterY) / 2.0f, 428958761ceb97982b510d3059d31ba8c45700a1654Chris Craik (bitmapWidth + scaledDiameterX) / 2.0f, 429958761ceb97982b510d3059d31ba8c45700a1654Chris Craik (bitmapHeight + scaledDiameterY) / 2.0f); 430537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik mPaint.setShader(mFrontBitmapShader); 431958761ceb97982b510d3059d31ba8c45700a1654Chris Craik canvas.drawOval(mTempRectF, mPaint); 432958761ceb97982b510d3059d31ba8c45700a1654Chris Craik canvas.restore(); 433537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik } else { 434537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik mPaint.setShader(null); 435537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik canvas.drawBitmap(mFrontBitmap, mSrcRect, getBounds(), mPaint); 436537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik } 437a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 438a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 439a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private void scheduleDecodeLocked() { 440a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mState = STATE_SCHEDULED; 441a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mNextFrameToDecode = (mNextFrameToDecode + 1) % mFrameSequence.getFrameCount(); 442a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik sDecodingThreadHandler.post(mDecodeRunnable); 443a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 444a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 445a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 446a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void run() { 44738f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik // set ready to swap as necessary 44838f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik boolean invalidate = false; 449a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 45038f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik if (mNextFrameToDecode >= 0 && mState == STATE_WAITING_TO_SWAP) { 45138f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik mState = STATE_READY_TO_SWAP; 45238f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik invalidate = true; 45338f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik } 45438f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik } 45538f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik if (invalidate) { 45638f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik invalidateSelf(); 457a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 458a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 459a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 460a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 461a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void start() { 462a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (!isRunning()) { 463a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 4644eb541aff092a057b27b917f09d33aba226dffedChris Craik checkDestroyedLocked(); 465a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (mState == STATE_SCHEDULED) return; // already scheduled 466e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik mCurrentLoop = 0; 467a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik scheduleDecodeLocked(); 468a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 469a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 470a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 471a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 472a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 473a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void stop() { 474a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (isRunning()) { 475a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik unscheduleSelf(this); 476a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 477a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 478a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 479a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 480a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public boolean isRunning() { 481a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 4824eb541aff092a057b27b917f09d33aba226dffedChris Craik return mNextFrameToDecode > -1 && !mDestroyed; 483a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 484a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 485a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 486a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 487a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void unscheduleSelf(Runnable what) { 488a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 489a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mNextFrameToDecode = -1; 490537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik mState = 0; 491a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 492a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik super.unscheduleSelf(what); 493a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 494a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 495a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 496a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public boolean setVisible(boolean visible, boolean restart) { 497a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik boolean changed = super.setVisible(visible, restart); 498a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 499a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (!visible) { 500a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik stop(); 501a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } else if (restart || changed) { 502a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik stop(); 503a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik start(); 504a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 505a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 506a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return changed; 507a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 508a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 509a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik // drawing properties 510a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 511a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 512a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void setFilterBitmap(boolean filter) { 513a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mPaint.setFilterBitmap(filter); 514a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 515a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 516a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 517a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void setAlpha(int alpha) { 518a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mPaint.setAlpha(alpha); 519a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 520a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 521a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 522a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void setColorFilter(ColorFilter colorFilter) { 523a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mPaint.setColorFilter(colorFilter); 524a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 525a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 526a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 527a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public int getIntrinsicWidth() { 528a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return mFrameSequence.getWidth(); 529a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 530a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 531a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 532a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public int getIntrinsicHeight() { 533a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return mFrameSequence.getHeight(); 534a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 535a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 536a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 537a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public int getOpacity() { 538a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return mFrameSequence.isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSPARENT; 539a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 540a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik} 541