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; 20a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport android.graphics.Canvas; 21a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport android.graphics.ColorFilter; 22a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport android.graphics.Paint; 23a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport android.graphics.PixelFormat; 24a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport android.graphics.Rect; 25a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport android.graphics.drawable.Animatable; 26a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport android.graphics.drawable.Drawable; 27a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport android.os.Handler; 28a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport android.os.HandlerThread; 293105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craikimport android.os.Process; 30a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport android.os.SystemClock; 31a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 32a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikpublic class FrameSequenceDrawable extends Drawable implements Animatable, Runnable { 33a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static final Object sLock = new Object(); 34a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static HandlerThread sDecodingThread; 35a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static Handler sDecodingThreadHandler; 36a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static void initializeDecodingThread() { 37a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (sLock) { 38a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (sDecodingThread != null) return; 39a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 403105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik sDecodingThread = new HandlerThread("FrameSequence decoding thread", 413105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik Process.THREAD_PRIORITY_BACKGROUND); 42a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik sDecodingThread.start(); 43a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik sDecodingThreadHandler = new Handler(sDecodingThread.getLooper()); 44a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 45a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 46a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 47e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public static interface OnFinishedListener { 48e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 49e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Called when a FrameSequenceDrawable has finished looping. 50e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * 51e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Note that this is will not be called if the drawable is explicitly 52e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * stopped, or marked invisible. 53e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 54e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public abstract void onFinished(FrameSequenceDrawable drawable); 55e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 56e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 573105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik public static interface BitmapProvider { 583105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik /** 593105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik * Called by FrameSequenceDrawable to aquire an 8888 Bitmap with minimum dimensions. 603105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik */ 613105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik public abstract Bitmap acquireBitmap(int minWidth, int minHeight); 623105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 633105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik /** 643105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik * Called by FrameSequenceDrawable to release a Bitmap it no longer needs. The Bitmap 653105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik * will no longer be used at all by the drawable, so it is safe to reuse elsewhere. 664eb541aff092a057b27b917f09d33aba226dffedChris Craik * 674eb541aff092a057b27b917f09d33aba226dffedChris Craik * This method may be called by FrameSequenceDrawable on any thread. 683105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik */ 693105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik public abstract void releaseBitmap(Bitmap bitmap); 703105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 713105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 723105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik private static BitmapProvider sAllocatingBitmapProvider = new BitmapProvider() { 733105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik @Override 743105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik public Bitmap acquireBitmap(int minWidth, int minHeight) { 753105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik return Bitmap.createBitmap(minWidth, minHeight, Bitmap.Config.ARGB_8888); 763105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 773105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 783105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik @Override 793105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik public void releaseBitmap(Bitmap bitmap) { 803105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik bitmap.recycle(); 813105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 823105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik }; 833105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 84e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 85e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Register a callback to be invoked when a FrameSequenceDrawable finishes looping. 86e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * 873105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik * @see #setLoopBehavior(int) 88e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 89e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public void setOnFinishedListener(OnFinishedListener onFinishedListener) { 90e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik mOnFinishedListener = onFinishedListener; 91e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 92e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 93e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 94e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Loop only once. 95e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 96e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public static final int LOOP_ONCE = 1; 97e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 98e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 99e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Loop continuously. The OnFinishedListener will never be called. 100e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 101e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public static final int LOOP_INF = 2; 102e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 103e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 104e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Use loop count stored in source data, or LOOP_ONCE if not present. 105e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 106e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public static final int LOOP_DEFAULT = 3; 107e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 108e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 109e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Define looping behavior of frame sequence. 110e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * 111e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Must be one of LOOP_ONCE, LOOP_INF, or LOOP_DEFAULT 112e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 113e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public void setLoopBehavior(int loopBehavior) { 114e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik mLoopBehavior = loopBehavior; 115e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 116e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 117a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private final FrameSequence mFrameSequence; 118a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private final FrameSequence.State mFrameSequenceState; 119a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 120a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private final Paint mPaint; 121a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private final Rect mSrcRect; 122a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 123a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik //Protects the fields below 124a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private final Object mLock = new Object(); 125a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 1264eb541aff092a057b27b917f09d33aba226dffedChris Craik private final BitmapProvider mBitmapProvider; 1274eb541aff092a057b27b917f09d33aba226dffedChris Craik private boolean mDestroyed = false; 128a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private Bitmap mFrontBitmap; 129a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private Bitmap mBackBitmap; 130a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 131a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static final int STATE_SCHEDULED = 1; 132a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static final int STATE_DECODING = 2; 133a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static final int STATE_WAITING_TO_SWAP = 3; 134a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static final int STATE_READY_TO_SWAP = 4; 135a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 136a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private int mState; 137e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private int mCurrentLoop; 138e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private int mLoopBehavior = LOOP_DEFAULT; 139a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 140a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private long mLastSwap; 1413105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik private long mNextSwap; 142a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private int mNextFrameToDecode; 143e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private OnFinishedListener mOnFinishedListener; 144a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 145a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik /** 146a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * Runs on decoding thread, only modifies mBackBitmap's pixels 147a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik */ 148a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private Runnable mDecodeRunnable = new Runnable() { 149a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 150a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void run() { 151a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik int nextFrame; 152a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik Bitmap bitmap; 153a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 1544eb541aff092a057b27b917f09d33aba226dffedChris Craik if (mDestroyed) return; 1553105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 156a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik nextFrame = mNextFrameToDecode; 157a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (nextFrame < 0) { 158a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return; 159a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 160a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik bitmap = mBackBitmap; 161a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mState = STATE_DECODING; 162a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 163a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik int lastFrame = nextFrame - 2; 164a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik long invalidateTimeMs = mFrameSequenceState.getFrame(nextFrame, bitmap, lastFrame); 165a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 166a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 167a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (mNextFrameToDecode < 0 || mState != STATE_DECODING) return; 1683105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik mNextSwap = invalidateTimeMs + mLastSwap; 169a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 170a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mState = STATE_WAITING_TO_SWAP; 171a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 1723105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik scheduleSelf(FrameSequenceDrawable.this, mNextSwap); 173a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 174a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik }; 175a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 176e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private Runnable mCallbackRunnable = new Runnable() { 177e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik @Override 178e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public void run() { 179e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik if (mOnFinishedListener != null) { 180e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik mOnFinishedListener.onFinished(FrameSequenceDrawable.this); 181e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 182e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 183e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik }; 184a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 1853105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik private static Bitmap acquireAndValidateBitmap(BitmapProvider bitmapProvider, 1863105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik int minWidth, int minHeight) { 1873105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik Bitmap bitmap = bitmapProvider.acquireBitmap(minWidth, minHeight); 1883105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 1893105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik if (bitmap.getWidth() < minWidth 1903105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik || bitmap.getHeight() < minHeight 1913105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik || bitmap.getConfig() != Bitmap.Config.ARGB_8888) { 1923105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik throw new IllegalArgumentException("Invalid bitmap provided"); 1933105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 1943105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 1953105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik return bitmap; 1963105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 1973105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 198a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public FrameSequenceDrawable(FrameSequence frameSequence) { 1993105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik this(frameSequence, sAllocatingBitmapProvider); 2003105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 2013105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 2023105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik public FrameSequenceDrawable(FrameSequence frameSequence, BitmapProvider bitmapProvider) { 2034eb541aff092a057b27b917f09d33aba226dffedChris Craik if (frameSequence == null || bitmapProvider == null) throw new IllegalArgumentException(); 204a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 205a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mFrameSequence = frameSequence; 206a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mFrameSequenceState = frameSequence.createState(); 207a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik final int width = frameSequence.getWidth(); 208a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik final int height = frameSequence.getHeight(); 209a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 2104eb541aff092a057b27b917f09d33aba226dffedChris Craik mBitmapProvider = bitmapProvider; 2113105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik mFrontBitmap = acquireAndValidateBitmap(bitmapProvider, width, height); 2123105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik mBackBitmap = acquireAndValidateBitmap(bitmapProvider, width, height); 213a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mSrcRect = new Rect(0, 0, width, height); 214a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mPaint = new Paint(); 215a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mPaint.setFilterBitmap(true); 216a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 217a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mLastSwap = 0; 218a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 219a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mNextFrameToDecode = -1; 220a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mFrameSequenceState.getFrame(0, mFrontBitmap, -1); 221a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik initializeDecodingThread(); 222a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 223a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 2244eb541aff092a057b27b917f09d33aba226dffedChris Craik private void checkDestroyedLocked() { 2254eb541aff092a057b27b917f09d33aba226dffedChris Craik if (mDestroyed) { 2263105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik throw new IllegalStateException("Cannot perform operation on recycled drawable"); 2273105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 2283105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 2293105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 2304eb541aff092a057b27b917f09d33aba226dffedChris Craik public boolean isDestroyed() { 2313105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik synchronized (mLock) { 2324eb541aff092a057b27b917f09d33aba226dffedChris Craik return mDestroyed; 2333105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 2343105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 2353105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 2363105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik /** 2373105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik * Marks the drawable as permanently recycled (and thus unusable), and releases any owned 2384eb541aff092a057b27b917f09d33aba226dffedChris Craik * Bitmaps drawable to its BitmapProvider, if attached. 2393105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik * 2404eb541aff092a057b27b917f09d33aba226dffedChris Craik * If no BitmapProvider is attached to the drawable, recycle() is called on the Bitmaps. 2413105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik */ 2424eb541aff092a057b27b917f09d33aba226dffedChris Craik public void destroy() { 2434eb541aff092a057b27b917f09d33aba226dffedChris Craik destroy(mBitmapProvider); 2444eb541aff092a057b27b917f09d33aba226dffedChris Craik } 2454eb541aff092a057b27b917f09d33aba226dffedChris Craik 2464eb541aff092a057b27b917f09d33aba226dffedChris Craik private void destroy(BitmapProvider bitmapProvider) { 2473105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik if (bitmapProvider == null) { 2483105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik throw new IllegalStateException("BitmapProvider must be non-null"); 2493105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 2503105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 2513105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik Bitmap bitmapToReleaseA; 2523105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik Bitmap bitmapToReleaseB; 2533105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik synchronized (mLock) { 2544eb541aff092a057b27b917f09d33aba226dffedChris Craik checkDestroyedLocked(); 2553105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 2563105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik bitmapToReleaseA = mFrontBitmap; 2573105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik bitmapToReleaseB = mBackBitmap; 2583105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 2593105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik mFrontBitmap = null; 2603105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik mBackBitmap = null; 2614eb541aff092a057b27b917f09d33aba226dffedChris Craik mDestroyed = true; 2623105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 2633105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 2644eb541aff092a057b27b917f09d33aba226dffedChris Craik // For simplicity and safety, we don't destroy the state object here 2653105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik bitmapProvider.releaseBitmap(bitmapToReleaseA); 2663105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik bitmapProvider.releaseBitmap(bitmapToReleaseB); 2673105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 2683105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 269a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 270a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik protected void finalize() throws Throwable { 271a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik try { 2724eb541aff092a057b27b917f09d33aba226dffedChris Craik mFrameSequenceState.destroy(); 2734eb541aff092a057b27b917f09d33aba226dffedChris Craik if (!mDestroyed) { 2744eb541aff092a057b27b917f09d33aba226dffedChris Craik destroy(); 2753105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 276a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } finally { 277a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik super.finalize(); 278a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 279a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 280a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 281a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 282a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void draw(Canvas canvas) { 283a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 2844eb541aff092a057b27b917f09d33aba226dffedChris Craik checkDestroyedLocked(); 2853105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik if (mState == STATE_WAITING_TO_SWAP) { 2863105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik // may have failed to schedule mark ready runnable, 2873105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik // so go ahead and swap if swapping is due 2883105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik if (mNextSwap - SystemClock.uptimeMillis() <= 0) { 2893105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik mState = STATE_READY_TO_SWAP; 2903105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 2913105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 2923105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 293a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (isRunning() && mState == STATE_READY_TO_SWAP) { 294a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik // Because draw has occurred, the view system is guaranteed to no longer hold a 295a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik // reference to the old mFrontBitmap, so we now use it to produce the next frame 296a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik Bitmap tmp = mBackBitmap; 297a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mBackBitmap = mFrontBitmap; 298a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mFrontBitmap = tmp; 299a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 300a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mLastSwap = SystemClock.uptimeMillis(); 301e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 302e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik boolean continueLooping = true; 303e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik if (mNextFrameToDecode == mFrameSequence.getFrameCount() - 1) { 304e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik mCurrentLoop++; 305e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik if ((mLoopBehavior == LOOP_ONCE && mCurrentLoop == 1) || 3063105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik (mLoopBehavior == LOOP_DEFAULT && mCurrentLoop == mFrameSequence.getDefaultLoopCount())) { 307e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik continueLooping = false; 308e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 309e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 310e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 311e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik if (continueLooping) { 312e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik scheduleDecodeLocked(); 313e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } else { 314e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik scheduleSelf(mCallbackRunnable, 0); 315e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 316a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 317a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 318a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 319a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik canvas.drawBitmap(mFrontBitmap, mSrcRect, getBounds(), mPaint); 320a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 321a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 322a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private void scheduleDecodeLocked() { 323a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mState = STATE_SCHEDULED; 324a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mNextFrameToDecode = (mNextFrameToDecode + 1) % mFrameSequence.getFrameCount(); 325a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik sDecodingThreadHandler.post(mDecodeRunnable); 326a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 327a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 328a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 329a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void run() { 330a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik // set ready to swap 331a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 332a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (mState != STATE_WAITING_TO_SWAP || mNextFrameToDecode < 0) return; 333a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mState = STATE_READY_TO_SWAP; 334a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 335a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik invalidateSelf(); 336a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 337a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 338a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 339a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void start() { 340a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (!isRunning()) { 341a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 3424eb541aff092a057b27b917f09d33aba226dffedChris Craik checkDestroyedLocked(); 343a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (mState == STATE_SCHEDULED) return; // already scheduled 344e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik mCurrentLoop = 0; 345a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik scheduleDecodeLocked(); 346a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 347a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 348a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 349a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 350a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 351a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void stop() { 352a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (isRunning()) { 353a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik unscheduleSelf(this); 354a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 355a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 356a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 357a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 358a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public boolean isRunning() { 359a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 3604eb541aff092a057b27b917f09d33aba226dffedChris Craik return mNextFrameToDecode > -1 && !mDestroyed; 361a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 362a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 363a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 364a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 365a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void unscheduleSelf(Runnable what) { 366a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 367a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mNextFrameToDecode = -1; 368a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 369a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik super.unscheduleSelf(what); 370a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 371a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 372a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 373a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public boolean setVisible(boolean visible, boolean restart) { 374a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik boolean changed = super.setVisible(visible, restart); 375a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 376a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (!visible) { 377a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik stop(); 378a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } else if (restart || changed) { 379a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik stop(); 380a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik start(); 381a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 382a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 383a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return changed; 384a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 385a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 386a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik // drawing properties 387a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 388a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 389a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void setFilterBitmap(boolean filter) { 390a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mPaint.setFilterBitmap(filter); 391a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 392a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 393a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 394a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void setAlpha(int alpha) { 395a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mPaint.setAlpha(alpha); 396a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 397a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 398a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 399a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void setColorFilter(ColorFilter colorFilter) { 400a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mPaint.setColorFilter(colorFilter); 401a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 402a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 403a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 404a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public int getIntrinsicWidth() { 405a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return mFrameSequence.getWidth(); 406a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 407a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 408a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 409a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public int getIntrinsicHeight() { 410a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return mFrameSequence.getHeight(); 411a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 412a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 413a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 414a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public int getOpacity() { 415a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return mFrameSequence.isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSPARENT; 416a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 417a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik} 418