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; 34a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 35a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikpublic class FrameSequenceDrawable extends Drawable implements Animatable, Runnable { 36e532fb97ba883a488bb751d51732274e03a052afChris Craik /** 37e532fb97ba883a488bb751d51732274e03a052afChris Craik * These constants are chosen to imitate common browser behavior for WebP/GIF. 38e532fb97ba883a488bb751d51732274e03a052afChris Craik * If other decoders are added, this behavior should be moved into the WebP/GIF decoders. 39e532fb97ba883a488bb751d51732274e03a052afChris Craik * 40e532fb97ba883a488bb751d51732274e03a052afChris Craik * Note that 0 delay is undefined behavior in the GIF standard. 41e532fb97ba883a488bb751d51732274e03a052afChris Craik */ 42e532fb97ba883a488bb751d51732274e03a052afChris Craik private static final long MIN_DELAY_MS = 20; 43e532fb97ba883a488bb751d51732274e03a052afChris Craik private static final long DEFAULT_DELAY_MS = 100; 44e532fb97ba883a488bb751d51732274e03a052afChris Craik 45a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static final Object sLock = new Object(); 46a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static HandlerThread sDecodingThread; 47a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static Handler sDecodingThreadHandler; 48a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static void initializeDecodingThread() { 49a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (sLock) { 50a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (sDecodingThread != null) return; 51a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 523105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik sDecodingThread = new HandlerThread("FrameSequence decoding thread", 533105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik Process.THREAD_PRIORITY_BACKGROUND); 54a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik sDecodingThread.start(); 55a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik sDecodingThreadHandler = new Handler(sDecodingThread.getLooper()); 56a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 57a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 58a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 59e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public static interface OnFinishedListener { 60e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 61e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Called when a FrameSequenceDrawable has finished looping. 62e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * 63e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Note that this is will not be called if the drawable is explicitly 64e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * stopped, or marked invisible. 65e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 66e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public abstract void onFinished(FrameSequenceDrawable drawable); 67e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 68e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 693105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik public static interface BitmapProvider { 703105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik /** 713105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik * Called by FrameSequenceDrawable to aquire an 8888 Bitmap with minimum dimensions. 723105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik */ 733105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik public abstract Bitmap acquireBitmap(int minWidth, int minHeight); 743105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 753105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik /** 763105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik * Called by FrameSequenceDrawable to release a Bitmap it no longer needs. The Bitmap 773105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik * will no longer be used at all by the drawable, so it is safe to reuse elsewhere. 784eb541aff092a057b27b917f09d33aba226dffedChris Craik * 794eb541aff092a057b27b917f09d33aba226dffedChris Craik * This method may be called by FrameSequenceDrawable on any thread. 803105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik */ 813105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik public abstract void releaseBitmap(Bitmap bitmap); 823105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 833105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 843105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik private static BitmapProvider sAllocatingBitmapProvider = new BitmapProvider() { 853105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik @Override 863105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik public Bitmap acquireBitmap(int minWidth, int minHeight) { 873105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik return Bitmap.createBitmap(minWidth, minHeight, Bitmap.Config.ARGB_8888); 883105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 893105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 903105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik @Override 91b19ecfd9cfa2c417034403adf7039edf9fe59327Chris Craik public void releaseBitmap(Bitmap bitmap) {} 923105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik }; 933105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 94e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 95e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Register a callback to be invoked when a FrameSequenceDrawable finishes looping. 96e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * 973105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik * @see #setLoopBehavior(int) 98e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 99e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public void setOnFinishedListener(OnFinishedListener onFinishedListener) { 100e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik mOnFinishedListener = onFinishedListener; 101e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 102e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 103e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 104e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Loop only once. 105e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 106e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public static final int LOOP_ONCE = 1; 107e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 108e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 109e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Loop continuously. The OnFinishedListener will never be called. 110e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 111e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public static final int LOOP_INF = 2; 112e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 113e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 114e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Use loop count stored in source data, or LOOP_ONCE if not present. 115e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 116e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public static final int LOOP_DEFAULT = 3; 117e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 118e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 119e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Define looping behavior of frame sequence. 120e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * 121e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Must be one of LOOP_ONCE, LOOP_INF, or LOOP_DEFAULT 122e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 123e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public void setLoopBehavior(int loopBehavior) { 124e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik mLoopBehavior = loopBehavior; 125e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 126e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 127a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private final FrameSequence mFrameSequence; 128a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private final FrameSequence.State mFrameSequenceState; 129a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 130a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private final Paint mPaint; 131537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik private BitmapShader mFrontBitmapShader; 132537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik private BitmapShader mBackBitmapShader; 133537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik private final Rect mSrcRect; 134537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik private boolean mCircleMaskEnabled; 135a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 136a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik //Protects the fields below 137a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private final Object mLock = new Object(); 138a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 1394eb541aff092a057b27b917f09d33aba226dffedChris Craik private final BitmapProvider mBitmapProvider; 1404eb541aff092a057b27b917f09d33aba226dffedChris Craik private boolean mDestroyed = false; 141a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private Bitmap mFrontBitmap; 142a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private Bitmap mBackBitmap; 143a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 144a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static final int STATE_SCHEDULED = 1; 145a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static final int STATE_DECODING = 2; 146a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static final int STATE_WAITING_TO_SWAP = 3; 147a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static final int STATE_READY_TO_SWAP = 4; 148a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 149a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private int mState; 150e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private int mCurrentLoop; 151e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private int mLoopBehavior = LOOP_DEFAULT; 152a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 153a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private long mLastSwap; 1543105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik private long mNextSwap; 155a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private int mNextFrameToDecode; 156e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private OnFinishedListener mOnFinishedListener; 157a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 158a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik /** 159a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * Runs on decoding thread, only modifies mBackBitmap's pixels 160a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik */ 161a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private Runnable mDecodeRunnable = new Runnable() { 162a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 163a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void run() { 164a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik int nextFrame; 165a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik Bitmap bitmap; 166a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 1674eb541aff092a057b27b917f09d33aba226dffedChris Craik if (mDestroyed) return; 1683105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 169a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik nextFrame = mNextFrameToDecode; 170a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (nextFrame < 0) { 171a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return; 172a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 173a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik bitmap = mBackBitmap; 174a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mState = STATE_DECODING; 175a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 176a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik int lastFrame = nextFrame - 2; 177a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik long invalidateTimeMs = mFrameSequenceState.getFrame(nextFrame, bitmap, lastFrame); 178537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik 179e532fb97ba883a488bb751d51732274e03a052afChris Craik if (invalidateTimeMs < MIN_DELAY_MS) { 180e532fb97ba883a488bb751d51732274e03a052afChris Craik invalidateTimeMs = DEFAULT_DELAY_MS; 181e532fb97ba883a488bb751d51732274e03a052afChris Craik } 182a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 18338f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik boolean schedule = false; 18438f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik Bitmap bitmapToRelease = null; 185a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 18638f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik if (mDestroyed) { 18738f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik bitmapToRelease = mBackBitmap; 18838f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik mBackBitmap = null; 18938f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik } else if (mNextFrameToDecode >= 0 && mState == STATE_DECODING) { 19038f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik schedule = true; 19138f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik mNextSwap = invalidateTimeMs + mLastSwap; 19238f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik mState = STATE_WAITING_TO_SWAP; 19338f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik } 19438f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik } 19538f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik if (schedule) { 19638f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik scheduleSelf(FrameSequenceDrawable.this, mNextSwap); 19738f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik } 19838f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik if (bitmapToRelease != null) { 19938f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik // destroy the bitmap here, since there's no safe way to get back to 20038f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik // drawable thread - drawable is likely detached, so schedule is noop. 20138f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik mBitmapProvider.releaseBitmap(bitmapToRelease); 202a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 203a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 204a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik }; 205a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 206e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private Runnable mCallbackRunnable = new Runnable() { 207e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik @Override 208e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public void run() { 209e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik if (mOnFinishedListener != null) { 210e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik mOnFinishedListener.onFinished(FrameSequenceDrawable.this); 211e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 212e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 213e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik }; 214a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 2153105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik private static Bitmap acquireAndValidateBitmap(BitmapProvider bitmapProvider, 2163105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik int minWidth, int minHeight) { 2173105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik Bitmap bitmap = bitmapProvider.acquireBitmap(minWidth, minHeight); 2183105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 2193105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik if (bitmap.getWidth() < minWidth 2203105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik || bitmap.getHeight() < minHeight 2213105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik || bitmap.getConfig() != Bitmap.Config.ARGB_8888) { 2223105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik throw new IllegalArgumentException("Invalid bitmap provided"); 2233105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 2243105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 2253105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik return bitmap; 2263105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 2273105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 228a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public FrameSequenceDrawable(FrameSequence frameSequence) { 2293105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik this(frameSequence, sAllocatingBitmapProvider); 2303105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 2313105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 2323105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik public FrameSequenceDrawable(FrameSequence frameSequence, BitmapProvider bitmapProvider) { 2334eb541aff092a057b27b917f09d33aba226dffedChris Craik if (frameSequence == null || bitmapProvider == null) throw new IllegalArgumentException(); 234a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 235a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mFrameSequence = frameSequence; 236a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mFrameSequenceState = frameSequence.createState(); 237a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik final int width = frameSequence.getWidth(); 238a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik final int height = frameSequence.getHeight(); 239a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 2404eb541aff092a057b27b917f09d33aba226dffedChris Craik mBitmapProvider = bitmapProvider; 2413105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik mFrontBitmap = acquireAndValidateBitmap(bitmapProvider, width, height); 2423105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik mBackBitmap = acquireAndValidateBitmap(bitmapProvider, width, height); 243a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mSrcRect = new Rect(0, 0, width, height); 244a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mPaint = new Paint(); 245a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mPaint.setFilterBitmap(true); 246a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 247537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik mFrontBitmapShader 248537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik = new BitmapShader(mFrontBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); 249537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik mBackBitmapShader 250537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik = new BitmapShader(mBackBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); 251537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik 252a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mLastSwap = 0; 253a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 254a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mNextFrameToDecode = -1; 255a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mFrameSequenceState.getFrame(0, mFrontBitmap, -1); 256a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik initializeDecodingThread(); 257a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 258a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 259537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik /** 260537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik * Pass true to mask the shape of the animated drawing content to a circle. 261537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik * 262537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik * <p> The masking circle will be the largest circle contained in the Drawable's bounds. 263537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik * Masking is done with BitmapShader, incurring minimal additional draw cost. 264537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik */ 265537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik public final void setCircleMaskEnabled(boolean circleMaskEnabled) { 266537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik mCircleMaskEnabled = circleMaskEnabled; 267537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik // Anti alias only necessary when using circular mask 268537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik mPaint.setAntiAlias(circleMaskEnabled); 269537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik } 270537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik 2714eb541aff092a057b27b917f09d33aba226dffedChris Craik private void checkDestroyedLocked() { 2724eb541aff092a057b27b917f09d33aba226dffedChris Craik if (mDestroyed) { 2733105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik throw new IllegalStateException("Cannot perform operation on recycled drawable"); 2743105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 2753105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 2763105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 2774eb541aff092a057b27b917f09d33aba226dffedChris Craik public boolean isDestroyed() { 2783105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik synchronized (mLock) { 2794eb541aff092a057b27b917f09d33aba226dffedChris Craik return mDestroyed; 2803105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 2813105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 2823105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 2833105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik /** 2843105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik * Marks the drawable as permanently recycled (and thus unusable), and releases any owned 2854eb541aff092a057b27b917f09d33aba226dffedChris Craik * Bitmaps drawable to its BitmapProvider, if attached. 2863105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik * 2874eb541aff092a057b27b917f09d33aba226dffedChris Craik * If no BitmapProvider is attached to the drawable, recycle() is called on the Bitmaps. 2883105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik */ 2894eb541aff092a057b27b917f09d33aba226dffedChris Craik public void destroy() { 29038f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik if (mBitmapProvider == null) { 2913105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik throw new IllegalStateException("BitmapProvider must be non-null"); 2923105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 2933105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 2943105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik Bitmap bitmapToReleaseA; 29538f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik Bitmap bitmapToReleaseB = null; 2963105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik synchronized (mLock) { 2974eb541aff092a057b27b917f09d33aba226dffedChris Craik checkDestroyedLocked(); 2983105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 2993105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik bitmapToReleaseA = mFrontBitmap; 3003105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik mFrontBitmap = null; 30138f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik 30238f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik if (mState != STATE_DECODING) { 30338f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik bitmapToReleaseB = mBackBitmap; 30438f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik mBackBitmap = null; 30538f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik } 30638f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik 3074eb541aff092a057b27b917f09d33aba226dffedChris Craik mDestroyed = true; 3083105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 3093105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 3104eb541aff092a057b27b917f09d33aba226dffedChris Craik // For simplicity and safety, we don't destroy the state object here 31138f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik mBitmapProvider.releaseBitmap(bitmapToReleaseA); 31238f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik if (bitmapToReleaseB != null) { 31338f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik mBitmapProvider.releaseBitmap(bitmapToReleaseB); 31438f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik } 3153105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 3163105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 317a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 318a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik protected void finalize() throws Throwable { 319a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik try { 3204eb541aff092a057b27b917f09d33aba226dffedChris Craik mFrameSequenceState.destroy(); 321a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } finally { 322a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik super.finalize(); 323a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 324a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 325a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 326a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 327a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void draw(Canvas canvas) { 328a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 3294eb541aff092a057b27b917f09d33aba226dffedChris Craik checkDestroyedLocked(); 3303105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik if (mState == STATE_WAITING_TO_SWAP) { 3313105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik // may have failed to schedule mark ready runnable, 3323105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik // so go ahead and swap if swapping is due 3333105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik if (mNextSwap - SystemClock.uptimeMillis() <= 0) { 3343105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik mState = STATE_READY_TO_SWAP; 3353105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 3363105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik } 3373105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik 338a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (isRunning() && mState == STATE_READY_TO_SWAP) { 339a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik // Because draw has occurred, the view system is guaranteed to no longer hold a 340a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik // reference to the old mFrontBitmap, so we now use it to produce the next frame 341a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik Bitmap tmp = mBackBitmap; 342a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mBackBitmap = mFrontBitmap; 343a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mFrontBitmap = tmp; 344a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 345537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik BitmapShader tmpShader = mBackBitmapShader; 346537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik mBackBitmapShader = mFrontBitmapShader; 347537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik mFrontBitmapShader = tmpShader; 348537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik 349a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mLastSwap = SystemClock.uptimeMillis(); 350e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 351e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik boolean continueLooping = true; 352e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik if (mNextFrameToDecode == mFrameSequence.getFrameCount() - 1) { 353e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik mCurrentLoop++; 354e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik if ((mLoopBehavior == LOOP_ONCE && mCurrentLoop == 1) || 3553105099a73d4fea3408ea0cf6b358fff77dc8b67Chris Craik (mLoopBehavior == LOOP_DEFAULT && mCurrentLoop == mFrameSequence.getDefaultLoopCount())) { 356e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik continueLooping = false; 357e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 358e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 359e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 360e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik if (continueLooping) { 361e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik scheduleDecodeLocked(); 362e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } else { 363e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik scheduleSelf(mCallbackRunnable, 0); 364e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 365a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 366a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 367a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 368537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik if (mCircleMaskEnabled) { 369537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik Rect bounds = getBounds(); 370537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik mPaint.setShader(mFrontBitmapShader); 371537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik float width = bounds.width(); 372537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik float height = bounds.height(); 373537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik float circleRadius = (Math.min(width, height)) / 2f; 374537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik canvas.drawCircle(width / 2f, height / 2f, circleRadius, mPaint); 375537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik } else { 376537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik mPaint.setShader(null); 377537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik canvas.drawBitmap(mFrontBitmap, mSrcRect, getBounds(), mPaint); 378537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik } 379a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 380a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 381a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private void scheduleDecodeLocked() { 382a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mState = STATE_SCHEDULED; 383a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mNextFrameToDecode = (mNextFrameToDecode + 1) % mFrameSequence.getFrameCount(); 384a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik sDecodingThreadHandler.post(mDecodeRunnable); 385a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 386a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 387a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 388a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void run() { 38938f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik // set ready to swap as necessary 39038f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik boolean invalidate = false; 391a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 39238f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik if (mNextFrameToDecode >= 0 && mState == STATE_WAITING_TO_SWAP) { 39338f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik mState = STATE_READY_TO_SWAP; 39438f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik invalidate = true; 39538f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik } 39638f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik } 39738f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik if (invalidate) { 39838f3678fe9ee5e050af1587a525dadc40f526a51Chris Craik invalidateSelf(); 399a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 400a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 401a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 402a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 403a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void start() { 404a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (!isRunning()) { 405a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 4064eb541aff092a057b27b917f09d33aba226dffedChris Craik checkDestroyedLocked(); 407a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (mState == STATE_SCHEDULED) return; // already scheduled 408e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik mCurrentLoop = 0; 409a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik scheduleDecodeLocked(); 410a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 411a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 412a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 413a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 414a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 415a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void stop() { 416a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (isRunning()) { 417a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik unscheduleSelf(this); 418a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 419a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 420a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 421a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 422a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public boolean isRunning() { 423a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 4244eb541aff092a057b27b917f09d33aba226dffedChris Craik return mNextFrameToDecode > -1 && !mDestroyed; 425a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 426a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 427a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 428a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 429a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void unscheduleSelf(Runnable what) { 430a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 431a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mNextFrameToDecode = -1; 432537754df0d7348949e2324a91cc9e4df7571d7bfChris Craik mState = 0; 433a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 434a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik super.unscheduleSelf(what); 435a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 436a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 437a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 438a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public boolean setVisible(boolean visible, boolean restart) { 439a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik boolean changed = super.setVisible(visible, restart); 440a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 441a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (!visible) { 442a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik stop(); 443a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } else if (restart || changed) { 444a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik stop(); 445a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik start(); 446a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 447a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 448a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return changed; 449a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 450a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 451a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik // drawing properties 452a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 453a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 454a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void setFilterBitmap(boolean filter) { 455a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mPaint.setFilterBitmap(filter); 456a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 457a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 458a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 459a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void setAlpha(int alpha) { 460a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mPaint.setAlpha(alpha); 461a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 462a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 463a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 464a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void setColorFilter(ColorFilter colorFilter) { 465a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mPaint.setColorFilter(colorFilter); 466a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 467a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 468a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 469a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public int getIntrinsicWidth() { 470a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return mFrameSequence.getWidth(); 471a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 472a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 473a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 474a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public int getIntrinsicHeight() { 475a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return mFrameSequence.getHeight(); 476a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 477a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 478a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 479a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public int getOpacity() { 480a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return mFrameSequence.isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSPARENT; 481a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 482a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik} 483