FrameSequenceDrawable.java revision e36c5d675c8c2f900ef186a55edf71ce36ca9fa0
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; 29a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport android.os.SystemClock; 30a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 31a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikpublic class FrameSequenceDrawable extends Drawable implements Animatable, Runnable { 32a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static final Object sLock = new Object(); 33a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static HandlerThread sDecodingThread; 34a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static Handler sDecodingThreadHandler; 35a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static void initializeDecodingThread() { 36a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (sLock) { 37a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (sDecodingThread != null) return; 38a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 39a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik sDecodingThread = new HandlerThread("FrameSequence decoding thread"); 40a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik sDecodingThread.start(); 41a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik sDecodingThreadHandler = new Handler(sDecodingThread.getLooper()); 42a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 43a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 44a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 45e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public static interface OnFinishedListener { 46e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 47e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Called when a FrameSequenceDrawable has finished looping. 48e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * 49e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Note that this is will not be called if the drawable is explicitly 50e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * stopped, or marked invisible. 51e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 52e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public abstract void onFinished(FrameSequenceDrawable drawable); 53e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 54e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 55e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 56e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Register a callback to be invoked when a FrameSequenceDrawable finishes looping. 57e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * 58e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * @see setLoopBehavior() 59e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 60e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public void setOnFinishedListener(OnFinishedListener onFinishedListener) { 61e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik mOnFinishedListener = onFinishedListener; 62e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 63e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 64e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 65e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Loop only once. 66e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 67e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public static final int LOOP_ONCE = 1; 68e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 69e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 70e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Loop continuously. The OnFinishedListener will never be called. 71e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 72e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public static final int LOOP_INF = 2; 73e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 74e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 75e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Use loop count stored in source data, or LOOP_ONCE if not present. 76e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 77e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public static final int LOOP_DEFAULT = 3; 78e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 79e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik /** 80e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Define looping behavior of frame sequence. 81e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * 82e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik * Must be one of LOOP_ONCE, LOOP_INF, or LOOP_DEFAULT 83e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik */ 84e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public void setLoopBehavior(int loopBehavior) { 85e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik mLoopBehavior = loopBehavior; 86e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 87e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 88a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private final FrameSequence mFrameSequence; 89a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private final FrameSequence.State mFrameSequenceState; 90a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 91a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private final Paint mPaint; 92a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private final Rect mSrcRect; 93a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 94a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik //Protects the fields below 95a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private final Object mLock = new Object(); 96a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 97a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private Bitmap mFrontBitmap; 98a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private Bitmap mBackBitmap; 99a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 100a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static final int STATE_SCHEDULED = 1; 101a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static final int STATE_DECODING = 2; 102a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static final int STATE_WAITING_TO_SWAP = 3; 103a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static final int STATE_READY_TO_SWAP = 4; 104a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 105a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private int mState; 106e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private int mCurrentLoop; 107e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private int mLoopBehavior = LOOP_DEFAULT; 108a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 109a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private long mLastSwap; 110a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private int mNextFrameToDecode; 111e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private OnFinishedListener mOnFinishedListener; 112a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 113a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik /** 114a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * Runs on decoding thread, only modifies mBackBitmap's pixels 115a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik */ 116a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private Runnable mDecodeRunnable = new Runnable() { 117a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 118a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void run() { 119a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik int nextFrame; 120a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik Bitmap bitmap; 121a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 122a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik nextFrame = mNextFrameToDecode; 123a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (nextFrame < 0) { 124a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return; 125a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 126a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik bitmap = mBackBitmap; 127a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mState = STATE_DECODING; 128a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 129a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik int lastFrame = nextFrame - 2; 130a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik long invalidateTimeMs = mFrameSequenceState.getFrame(nextFrame, bitmap, lastFrame); 131a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 132a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 133a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (mNextFrameToDecode < 0 || mState != STATE_DECODING) return; 134a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik invalidateTimeMs += mLastSwap; 135a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 136a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mState = STATE_WAITING_TO_SWAP; 137a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 138a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik scheduleSelf(FrameSequenceDrawable.this, invalidateTimeMs); 139a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 140a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik }; 141a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 142e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private Runnable mCallbackRunnable = new Runnable() { 143e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik @Override 144e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public void run() { 145e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik if (mOnFinishedListener != null) { 146e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik mOnFinishedListener.onFinished(FrameSequenceDrawable.this); 147e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 148e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 149e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik }; 150a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 151a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public FrameSequenceDrawable(FrameSequence frameSequence) { 152a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (frameSequence == null) throw new IllegalArgumentException(); 153a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 154a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mFrameSequence = frameSequence; 155a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mFrameSequenceState = frameSequence.createState(); 156a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik // TODO: add callback for requesting bitmaps, to allow for reuse 157a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik final int width = frameSequence.getWidth(); 158a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik final int height = frameSequence.getHeight(); 159a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 160a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mFrontBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 161a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mBackBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 162a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mSrcRect = new Rect(0, 0, width, height); 163a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mPaint = new Paint(); 164a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mPaint.setFilterBitmap(true); 165a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 166a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mLastSwap = 0; 167a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 168a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mNextFrameToDecode = -1; 169a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mFrameSequenceState.getFrame(0, mFrontBitmap, -1); 170a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik initializeDecodingThread(); 171a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 172a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 173a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 174a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik protected void finalize() throws Throwable { 175a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik try { 176a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mFrontBitmap.recycle(); 177a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mBackBitmap.recycle(); 178a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mFrameSequenceState.recycle(); 179a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } finally { 180a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik super.finalize(); 181a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 182a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 183a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 184a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 185a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void draw(Canvas canvas) { 186a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 187a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (isRunning() && mState == STATE_READY_TO_SWAP) { 188a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik // Because draw has occurred, the view system is guaranteed to no longer hold a 189a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik // reference to the old mFrontBitmap, so we now use it to produce the next frame 190a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik Bitmap tmp = mBackBitmap; 191a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mBackBitmap = mFrontBitmap; 192a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mFrontBitmap = tmp; 193a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 194a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mLastSwap = SystemClock.uptimeMillis(); 195e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 196e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik boolean continueLooping = true; 197e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik if (mNextFrameToDecode == mFrameSequence.getFrameCount() - 1) { 198e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik mCurrentLoop++; 199e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik if ((mLoopBehavior == LOOP_ONCE && mCurrentLoop == 1) || 200e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik (mLoopBehavior == LOOP_DEFAULT && mCurrentLoop == mFrameSequence.getDefaultLoopCount())) { 201e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik continueLooping = false; 202e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 203e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 204e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik 205e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik if (continueLooping) { 206e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik scheduleDecodeLocked(); 207e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } else { 208e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik scheduleSelf(mCallbackRunnable, 0); 209e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik } 210a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 211a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 212a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 213a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik canvas.drawBitmap(mFrontBitmap, mSrcRect, getBounds(), mPaint); 214a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 215a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 216a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private void scheduleDecodeLocked() { 217a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mState = STATE_SCHEDULED; 218a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mNextFrameToDecode = (mNextFrameToDecode + 1) % mFrameSequence.getFrameCount(); 219a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik sDecodingThreadHandler.post(mDecodeRunnable); 220a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 221a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 222a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 223a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void run() { 224a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik // set ready to swap 225a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 226a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (mState != STATE_WAITING_TO_SWAP || mNextFrameToDecode < 0) return; 227a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mState = STATE_READY_TO_SWAP; 228a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 229a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik invalidateSelf(); 230a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 231a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 232a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 233a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void start() { 234a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (!isRunning()) { 235a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 236a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (mState == STATE_SCHEDULED) return; // already scheduled 237e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik mCurrentLoop = 0; 238a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik scheduleDecodeLocked(); 239a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 240a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 241a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 242a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 243a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 244a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void stop() { 245a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (isRunning()) { 246a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik unscheduleSelf(this); 247a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 248a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 249a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 250a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 251a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public boolean isRunning() { 252a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 253a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return mNextFrameToDecode > -1; 254a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 255a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 256a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 257a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 258a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void scheduleSelf(Runnable what, long when) { 259a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik super.scheduleSelf(what, when); 260a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 261a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 262a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 263a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void unscheduleSelf(Runnable what) { 264a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik synchronized (mLock) { 265a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mNextFrameToDecode = -1; 266a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 267a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik super.unscheduleSelf(what); 268a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 269a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 270a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 271a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public boolean setVisible(boolean visible, boolean restart) { 272a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik boolean changed = super.setVisible(visible, restart); 273a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 274a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (!visible) { 275a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik stop(); 276a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } else if (restart || changed) { 277a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik stop(); 278a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik start(); 279a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 280a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 281a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return changed; 282a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 283a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 284a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik // drawing properties 285a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 286a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 287a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void setFilterBitmap(boolean filter) { 288a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mPaint.setFilterBitmap(filter); 289a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 290a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 291a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 292a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void setAlpha(int alpha) { 293a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mPaint.setAlpha(alpha); 294a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 295a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 296a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 297a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public void setColorFilter(ColorFilter colorFilter) { 298a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mPaint.setColorFilter(colorFilter); 299a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 300a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 301a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 302a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public int getIntrinsicWidth() { 303a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return mFrameSequence.getWidth(); 304a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 305a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 306a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 307a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public int getIntrinsicHeight() { 308a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return mFrameSequence.getHeight(); 309a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 310a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 311a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 312a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public int getOpacity() { 313a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return mFrameSequence.isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSPARENT; 314a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 315a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik} 316