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