193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein/*
293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * Copyright (C) 2013 The Android Open Source Project
393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *
493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * Licensed under the Apache License, Version 2.0 (the "License");
593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * you may not use this file except in compliance with the License.
693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * You may obtain a copy of the License at
793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *
893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *      http://www.apache.org/licenses/LICENSE-2.0
993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *
1093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * Unless required by applicable law or agreed to in writing, software
1193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * distributed under the License is distributed on an "AS IS" BASIS,
1293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * See the License for the specific language governing permissions and
1493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * limitations under the License.
1593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein */
1693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinpackage com.android.bitmap.drawable;
1793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
1893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.content.res.Resources;
1993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.graphics.Canvas;
2093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.graphics.ColorFilter;
2193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.graphics.Paint;
2293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.graphics.PixelFormat;
2393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.graphics.Rect;
2493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.graphics.drawable.Drawable;
2593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.util.DisplayMetrics;
2693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.util.Log;
2793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
2893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport com.android.bitmap.BitmapCache;
2993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport com.android.bitmap.DecodeTask;
309c6ac19d4a3d39b7c2992060957920118ff56a65Mark Weiimport com.android.bitmap.DecodeTask.DecodeCallback;
31cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Weiimport com.android.bitmap.DecodeTask.DecodeOptions;
32cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Weiimport com.android.bitmap.NamedThreadFactory;
3340662f4b39e795d9c64502b13036e7c37fa2d373Sam Blitzsteinimport com.android.bitmap.RequestKey;
349c6ac19d4a3d39b7c2992060957920118ff56a65Mark Weiimport com.android.bitmap.RequestKey.Cancelable;
359c6ac19d4a3d39b7c2992060957920118ff56a65Mark Weiimport com.android.bitmap.RequestKey.FileDescriptorFactory;
3693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport com.android.bitmap.ReusableBitmap;
3793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport com.android.bitmap.util.BitmapUtils;
3893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport com.android.bitmap.util.RectUtils;
3993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport com.android.bitmap.util.Trace;
4093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
4193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport java.util.concurrent.Executor;
4293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport java.util.concurrent.LinkedBlockingQueue;
4393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport java.util.concurrent.ThreadPoolExecutor;
4493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport java.util.concurrent.TimeUnit;
4593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
4693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein/**
4793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * This class encapsulates the basic functionality needed to display a single image bitmap,
4893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * including request creation/cancelling, and data unbinding and re-binding.
4993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * <p>
5093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * The actual bitmap decode work is handled by {@link DecodeTask}.
51a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei * <p>
52a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei * If being used with a long-lived cache (static cache, attached to the Application instead of the
53a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei * Activity, etc) then make sure to call {@link BasicBitmapDrawable#unbind()} at the appropriate
549c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei * times so the cache has accurate unref counts. The
559c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei * {@link com.android.bitmap.view.BitmapDrawableImageView} class has been created to do the
569c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei * appropriate unbind operation when the view is detached from the window.
5793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein */
589c6ac19d4a3d39b7c2992060957920118ff56a65Mark Weipublic class BasicBitmapDrawable extends Drawable implements DecodeCallback,
599c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei        Drawable.Callback, RequestKey.Callback {
602e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
61528300fdc3caeb96a24873acf2b1cc0914b97593Mark Wei    protected RequestKey mCurrKey;
622e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    protected RequestKey mPrevKey;
6341af50eb1ac488572b066629c3954b23c21dfa76Mark Wei    protected int mDecodeWidth;
6441af50eb1ac488572b066629c3954b23c21dfa76Mark Wei    protected int mDecodeHeight;
65e03daa1db89106c11d8885b94d7ac97c10bea3b3Mark Wei
662e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    protected final Paint mPaint = new Paint();
6793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private final BitmapCache mCache;
68a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev    private final Rect mRect = new Rect();
692e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
70cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei    private final boolean mLimitDensity;
71e03daa1db89106c11d8885b94d7ac97c10bea3b3Mark Wei    private final float mDensity;
72a23358fbfd62c7aa1c84bfa8395b4dc427a71ce6Mark Wei    private ReusableBitmap mBitmap;
7393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private DecodeTask mTask;
749c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei    private Cancelable mCreateFileDescriptorFactoryTask;
7593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
76856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei    private int mLayoutDirection;
77856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei
7893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    // based on framework CL:I015d77
7993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
8093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
8193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
82cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei
8393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private static final Executor SMALL_POOL_EXECUTOR = new ThreadPoolExecutor(
8493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, 1, TimeUnit.SECONDS,
85cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei            new LinkedBlockingQueue<Runnable>(128), new NamedThreadFactory("decode"));
8693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private static final Executor EXECUTOR = SMALL_POOL_EXECUTOR;
8793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
8893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private static final int MAX_BITMAP_DENSITY = DisplayMetrics.DENSITY_HIGH;
89cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei    private static final float VERTICAL_CENTER = 1f / 2;
903a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung    private static final float HORIZONTAL_CENTER = 1f / 2;
912e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    private static final float NO_MULTIPLIER = 1f;
9293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
93e03daa1db89106c11d8885b94d7ac97c10bea3b3Mark Wei    private static final String TAG = BasicBitmapDrawable.class.getSimpleName();
94cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei    private static final boolean DEBUG = DecodeTask.DEBUG;
9593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
96cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei    public BasicBitmapDrawable(final Resources res, final BitmapCache cache,
97cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei            final boolean limitDensity) {
9893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        mDensity = res.getDisplayMetrics().density;
9993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        mCache = cache;
100cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        mLimitDensity = limitDensity;
10193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        mPaint.setFilterBitmap(true);
102e03daa1db89106c11d8885b94d7ac97c10bea3b3Mark Wei        mPaint.setAntiAlias(true);
103e03daa1db89106c11d8885b94d7ac97c10bea3b3Mark Wei        mPaint.setDither(true);
10493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
10593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
10610dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei    public final RequestKey getKey() {
10793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        return mCurrKey;
10893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
10993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
110ad97cd0e0753930ec68f189e6be1023dc0f62f75Mark Wei    public final RequestKey getPreviousKey() {
111ad97cd0e0753930ec68f189e6be1023dc0f62f75Mark Wei        return mPrevKey;
112ad97cd0e0753930ec68f189e6be1023dc0f62f75Mark Wei    }
113ad97cd0e0753930ec68f189e6be1023dc0f62f75Mark Wei
11441af50eb1ac488572b066629c3954b23c21dfa76Mark Wei    protected ReusableBitmap getBitmap() {
1152e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        return mBitmap;
1162e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    }
1172e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
11893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    /**
11910dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei     * Set the dimensions to decode into. These dimensions should never change while the drawable is
12010dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei     * attached to the same cache, because caches can only contain bitmaps of one size for re-use.
12110dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei     *
12210dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei     * All UI operations should be called from the UI thread.
12393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     */
12489e59f00d67791754e44e65413baa95f94056df4Mark Wei    public void setDecodeDimensions(int width, int height) {
12589e59f00d67791754e44e65413baa95f94056df4Mark Wei        if (mDecodeWidth == 0 || mDecodeHeight == 0) {
12689e59f00d67791754e44e65413baa95f94056df4Mark Wei            mDecodeWidth = width;
12789e59f00d67791754e44e65413baa95f94056df4Mark Wei            mDecodeHeight = height;
12889e59f00d67791754e44e65413baa95f94056df4Mark Wei            setImage(mCurrKey);
12989e59f00d67791754e44e65413baa95f94056df4Mark Wei        }
13093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
13193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
13210dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei    /**
133856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei     * Set layout direction.
134856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei     * It ends with Local so as not conflict with hidden Drawable.setLayoutDirection.
135856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei     * @param layoutDirection the resolved layout direction for the drawable,
136856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei     *                        either {@link android.view.View#LAYOUT_DIRECTION_LTR}
137856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei     *                        or {@link android.view.View#LAYOUT_DIRECTION_RTL}
138856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei     */
139856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei    public void setLayoutDirectionLocal(int layoutDirection) {
140856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei        if (mLayoutDirection != layoutDirection) {
141856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei            mLayoutDirection = layoutDirection;
142856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei            onLayoutDirectionChangeLocal(layoutDirection);
143856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei        }
144856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei    }
145856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei
146856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei    /**
147856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei     * Called when the drawable's resolved layout direction changes.
148856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei     * It ends with Local so as not conflict with hidden Drawable.onLayoutDirectionChange.
149856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei     *
150856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei     * @param layoutDirection the new resolved layout direction
151856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei     */
152856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei    public void onLayoutDirectionChangeLocal(int layoutDirection) {}
153856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei
154856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei    /**
155856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei     * Returns the resolved layout direction for this Drawable.
156856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei     * It ends with Local so as not conflict with hidden Drawable.getLayoutDirection.
157856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei     *
158856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei     * @return One of {@link android.view.View#LAYOUT_DIRECTION_LTR},
159856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei     *         {@link android.view.View#LAYOUT_DIRECTION_RTL}
160856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei     * @see #setLayoutDirectionLocal(int)
161856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei     */
162856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei    public int getLayoutDirectionLocal() {
163856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei        return mLayoutDirection;
164856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei    }
165856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei
166856e2a9d72fe3a79cd7bfa5cf443d13a60495c4bMark Wei    /**
16710dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei     * Binds to the given key and start the decode process. This will first look in the cache, then
16810dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei     * decode from the request key if not found.
16910dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei     *
1705b46c679794d2f4362e8064b0291c578835d75e0Mark Wei     * The key being replaced will be kept in {@link #mPrevKey}.
1715b46c679794d2f4362e8064b0291c578835d75e0Mark Wei     *
17210dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei     * All UI operations should be called from the UI thread.
17310dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei     */
17441af50eb1ac488572b066629c3954b23c21dfa76Mark Wei    public void bind(RequestKey key) {
175c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei        Trace.beginSection("bind");
17689e59f00d67791754e44e65413baa95f94056df4Mark Wei        if (mCurrKey != null && mCurrKey.equals(key)) {
1772d10993c4276db5b28ef7cb909362fbbc26c460cRuben            Trace.endSection();
17889e59f00d67791754e44e65413baa95f94056df4Mark Wei            return;
17989e59f00d67791754e44e65413baa95f94056df4Mark Wei        }
18010dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei        setImage(key);
181c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei        Trace.endSection();
18293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
18393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
18410dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei    /**
18510dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei     * Unbinds the current key and bitmap from the drawable. This will cause the bitmap to decrement
18610dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei     * its ref count.
18710dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei     *
1885b46c679794d2f4362e8064b0291c578835d75e0Mark Wei     * This will assume that you do not want to keep the unbound key in {@link #mPrevKey}.
1895b46c679794d2f4362e8064b0291c578835d75e0Mark Wei     *
19010dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei     * All UI operations should be called from the UI thread.
19110dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei     */
19241af50eb1ac488572b066629c3954b23c21dfa76Mark Wei    public void unbind() {
1935b46c679794d2f4362e8064b0291c578835d75e0Mark Wei        unbind(false);
1945b46c679794d2f4362e8064b0291c578835d75e0Mark Wei    }
1955b46c679794d2f4362e8064b0291c578835d75e0Mark Wei
1965b46c679794d2f4362e8064b0291c578835d75e0Mark Wei    /**
1975b46c679794d2f4362e8064b0291c578835d75e0Mark Wei     * Unbinds the current key and bitmap from the drawable. This will cause the bitmap to decrement
1985b46c679794d2f4362e8064b0291c578835d75e0Mark Wei     * its ref count.
1995b46c679794d2f4362e8064b0291c578835d75e0Mark Wei     *
2005b46c679794d2f4362e8064b0291c578835d75e0Mark Wei     * If the temporary parameter is true, we will keep the unbound key in {@link #mPrevKey}.
2015b46c679794d2f4362e8064b0291c578835d75e0Mark Wei     *
2025b46c679794d2f4362e8064b0291c578835d75e0Mark Wei     * All UI operations should be called from the UI thread.
2035b46c679794d2f4362e8064b0291c578835d75e0Mark Wei     */
2045b46c679794d2f4362e8064b0291c578835d75e0Mark Wei    public void unbind(boolean temporary) {
205c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei        Trace.beginSection("unbind");
20610dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei        setImage(null);
2075b46c679794d2f4362e8064b0291c578835d75e0Mark Wei        if (!temporary) {
2085b46c679794d2f4362e8064b0291c578835d75e0Mark Wei            mPrevKey = null;
2095b46c679794d2f4362e8064b0291c578835d75e0Mark Wei        }
210c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei        Trace.endSection();
21193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
21293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
21310dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei    /**
21410dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei     * Should only be overriden, not called.
21510dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei     */
2162e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    protected void setImage(final RequestKey key) {
21793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        Trace.beginSection("set image");
21893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        Trace.beginSection("release reference");
21993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (mBitmap != null) {
22093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            mBitmap.releaseReference();
22193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            mBitmap = null;
22293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
22393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        Trace.endSection();
2242e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
2252e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        mPrevKey = mCurrKey;
22693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        mCurrKey = key;
22793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
22893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (mTask != null) {
22993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            mTask.cancel();
23093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            mTask = null;
23193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
2329c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei        if (mCreateFileDescriptorFactoryTask != null) {
2339c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei            mCreateFileDescriptorFactoryTask.cancel();
2349c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei            mCreateFileDescriptorFactoryTask = null;
2359c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei        }
23693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
23793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (key == null) {
238c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei            onDecodeFailed();
23993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            Trace.endSection();
24093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            return;
24193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
24293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
24393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        // find cached entry here and skip decode if found.
24493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        final ReusableBitmap cached = mCache.get(key, true /* incrementRefCount */);
24593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (cached != null) {
24693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            setBitmap(cached);
24793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (DEBUG) {
24893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                Log.d(TAG, String.format("CACHE HIT key=%s", mCurrKey));
24993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
25093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        } else {
2519c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei            loadFileDescriptorFactory();
25293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (DEBUG) {
25393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                Log.d(TAG, String.format(
25493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        "CACHE MISS key=%s\ncache=%s", mCurrKey, mCache.toDebugString()));
25593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
25693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
25793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        Trace.endSection();
25893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
25993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
26010dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei    /**
26110dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei     * Should only be overriden, not called.
26210dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei     */
2632e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    protected void setBitmap(ReusableBitmap bmp) {
2648aa2d8601f6a97af4a329cfc3bfd5fb5fb5bcbf8Mark Wei        if (hasBitmap()) {
2652e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei            mBitmap.releaseReference();
2662e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        }
2672e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        mBitmap = bmp;
2682e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        invalidateSelf();
2692e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    }
2702e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
27110dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei    /**
27210dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei     * Should only be overriden, not called.
27310dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei     */
2742e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    protected void loadFileDescriptorFactory() {
2752e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        if (mCurrKey == null || mDecodeWidth == 0 || mDecodeHeight == 0) {
276c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei            onDecodeFailed();
2772e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei            return;
2782e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        }
2792e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
2802e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        // Create file descriptor if request supports it.
2812e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        mCreateFileDescriptorFactoryTask = mCurrKey
2822e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei                .createFileDescriptorFactoryAsync(mCurrKey, this);
2832e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        if (mCreateFileDescriptorFactoryTask == null) {
2842e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei            // Use input stream if request does not.
2852e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei            decode(null);
2862e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        }
2872e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    }
2882e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
2892e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    @Override
2902e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    public void fileDescriptorFactoryCreated(final RequestKey key,
2912e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei            final FileDescriptorFactory factory) {
2922e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        if (mCreateFileDescriptorFactoryTask == null) {
2932e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei            // Cancelled.
294c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei            onDecodeFailed();
2952e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei            return;
2962e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        }
2972e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        mCreateFileDescriptorFactoryTask = null;
2982e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
2992e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        if (key.equals(mCurrKey)) {
3002e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei            decode(factory);
3012e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        }
3022e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    }
3032e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
30410dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei    /**
305c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei     * Called when the decode process is cancelled at any time.
306c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei     */
307c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei    protected void onDecodeFailed() {
308c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei        invalidateSelf();
309c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei    }
310c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei
311c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei    /**
31210dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei     * Should only be overriden, not called.
31310dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei     */
3142e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    protected void decode(final FileDescriptorFactory factory) {
3152e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        Trace.beginSection("decode");
3162e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        final int bufferW;
3172e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        final int bufferH;
3182e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        if (mLimitDensity) {
3192e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei            final float scale =
3202e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei                    Math.min(1f, (float) MAX_BITMAP_DENSITY / DisplayMetrics.DENSITY_DEFAULT
3212e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei                            / mDensity);
3222e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei            bufferW = (int) (mDecodeWidth * scale);
3232e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei            bufferH = (int) (mDecodeHeight * scale);
3242e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        } else {
3252e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei            bufferW = mDecodeWidth;
3262e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei            bufferH = mDecodeHeight;
3272e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        }
3282e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
3292e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        if (mTask != null) {
3302e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei            mTask.cancel();
3312e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        }
3323a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung        final DecodeOptions opts = new DecodeOptions(bufferW, bufferH, getDecodeHorizontalCenter(),
3333a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung                getDecodeVerticalCenter(), getDecodeStrategy());
3342e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        mTask = new DecodeTask(mCurrKey, opts, factory, this, mCache);
335aab539ecd75ae365912200eb7f3318a53e9834f4Mark Wei        mTask.executeOnExecutor(getExecutor());
3362e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        Trace.endSection();
3372e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    }
3382e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
339d05e64cf9f9b1542ccdac8675c63b8b185c97a48Sam Blitzstein    /**
340d05e64cf9f9b1542ccdac8675c63b8b185c97a48Sam Blitzstein     * Return one of the STRATEGY constants in {@link DecodeOptions}.
341d05e64cf9f9b1542ccdac8675c63b8b185c97a48Sam Blitzstein     */
342d05e64cf9f9b1542ccdac8675c63b8b185c97a48Sam Blitzstein    protected int getDecodeStrategy() {
343d05e64cf9f9b1542ccdac8675c63b8b185c97a48Sam Blitzstein        return DecodeOptions.STRATEGY_ROUND_NEAREST;
344d05e64cf9f9b1542ccdac8675c63b8b185c97a48Sam Blitzstein    }
345d05e64cf9f9b1542ccdac8675c63b8b185c97a48Sam Blitzstein
346aab539ecd75ae365912200eb7f3318a53e9834f4Mark Wei    protected Executor getExecutor() {
347aab539ecd75ae365912200eb7f3318a53e9834f4Mark Wei        return EXECUTOR;
348aab539ecd75ae365912200eb7f3318a53e9834f4Mark Wei    }
349aab539ecd75ae365912200eb7f3318a53e9834f4Mark Wei
3502e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    protected float getDrawVerticalCenter() {
3512e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        return VERTICAL_CENTER;
3522e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    }
3532e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
3542e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    protected float getDrawVerticalOffsetMultiplier() {
3552e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        return NO_MULTIPLIER;
3562e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    }
3572e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
35889e59f00d67791754e44e65413baa95f94056df4Mark Wei    /**
35989e59f00d67791754e44e65413baa95f94056df4Mark Wei     * Clients can override this to specify which section of the source image to decode from.
36089e59f00d67791754e44e65413baa95f94056df4Mark Wei     * Possible applications include using face detection to always decode around facial features.
36189e59f00d67791754e44e65413baa95f94056df4Mark Wei     */
3623a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung    protected float getDecodeHorizontalCenter() {
3633a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung        return HORIZONTAL_CENTER;
3643a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung    }
3653a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung
3663a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung    /**
3673a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung     * Clients can override this to specify which section of the source image to decode from.
3683a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung     * Possible applications include using face detection to always decode around facial features.
3693a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung     */
3702e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    protected float getDecodeVerticalCenter() {
3712e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        return VERTICAL_CENTER;
3722e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    }
3732e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
37493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
37593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public void draw(final Canvas canvas) {
37693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        final Rect bounds = getBounds();
37793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (bounds.isEmpty()) {
37893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            return;
37993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
38093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
3818aa2d8601f6a97af4a329cfc3bfd5fb5fb5bcbf8Mark Wei        if (hasBitmap()) {
38293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            BitmapUtils.calculateCroppedSrcRect(
38393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    mBitmap.getLogicalWidth(), mBitmap.getLogicalHeight(),
38493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    bounds.width(), bounds.height(),
3853a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung                    bounds.height(), Integer.MAX_VALUE, getDecodeHorizontalCenter(),
3862e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei                    getDrawVerticalCenter(), false /* absoluteFraction */,
387a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev                    getDrawVerticalOffsetMultiplier(), mRect);
38893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
38993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            final int orientation = mBitmap.getOrientation();
39093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // calculateCroppedSrcRect() gave us the source rectangle "as if" the orientation has
39193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // been corrected. We need to decode the uncorrected source rectangle. Calculate true
39293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // coordinates.
39393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            RectUtils.rotateRectForOrientation(orientation,
39493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    new Rect(0, 0, mBitmap.getLogicalWidth(), mBitmap.getLogicalHeight()),
395a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev                    mRect);
39693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
39793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // We may need to rotate the canvas, so we also have to rotate the bounds.
39893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            final Rect rotatedBounds = new Rect(bounds);
39993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            RectUtils.rotateRect(orientation, bounds.centerX(), bounds.centerY(), rotatedBounds);
40093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
40193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // Rotate the canvas.
40293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            canvas.save();
40393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            canvas.rotate(orientation, bounds.centerX(), bounds.centerY());
404a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev            onDrawBitmap(canvas, mRect, rotatedBounds);
40593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            canvas.restore();
40693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
40793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
40893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
4098aa2d8601f6a97af4a329cfc3bfd5fb5fb5bcbf8Mark Wei    protected boolean hasBitmap() {
4108aa2d8601f6a97af4a329cfc3bfd5fb5fb5bcbf8Mark Wei        return mBitmap != null && mBitmap.bmp != null;
4118aa2d8601f6a97af4a329cfc3bfd5fb5fb5bcbf8Mark Wei    }
4128aa2d8601f6a97af4a329cfc3bfd5fb5fb5bcbf8Mark Wei
4130c9aacac20202f4a41e98d8a34a6ee2c152ae2deMark Wei    /**
4140c9aacac20202f4a41e98d8a34a6ee2c152ae2deMark Wei     * Override this method to customize how to draw the bitmap to the canvas for the given bounds.
4150c9aacac20202f4a41e98d8a34a6ee2c152ae2deMark Wei     * The bitmap to be drawn can be found at {@link #getBitmap()}.
4160c9aacac20202f4a41e98d8a34a6ee2c152ae2deMark Wei     */
4177be91f8fe3c6d9af037b36918ea5bd86a10bbb44Mark Wei    protected void onDrawBitmap(final Canvas canvas, final Rect src, final Rect dst) {
4188aa2d8601f6a97af4a329cfc3bfd5fb5fb5bcbf8Mark Wei        if (hasBitmap()) {
4198aa2d8601f6a97af4a329cfc3bfd5fb5fb5bcbf8Mark Wei            canvas.drawBitmap(mBitmap.bmp, src, dst, mPaint);
4208aa2d8601f6a97af4a329cfc3bfd5fb5fb5bcbf8Mark Wei        }
4210c9aacac20202f4a41e98d8a34a6ee2c152ae2deMark Wei    }
4220c9aacac20202f4a41e98d8a34a6ee2c152ae2deMark Wei
42393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
42493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public void setAlpha(int alpha) {
42593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        final int old = mPaint.getAlpha();
42693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        mPaint.setAlpha(alpha);
42793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (alpha != old) {
42893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            invalidateSelf();
42993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
43093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
43193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
43293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
43393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public void setColorFilter(ColorFilter cf) {
43493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        mPaint.setColorFilter(cf);
43593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        invalidateSelf();
43693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
43793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
43893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
43993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public int getOpacity() {
4408aa2d8601f6a97af4a329cfc3bfd5fb5fb5bcbf8Mark Wei        return (hasBitmap() && (mBitmap.bmp.hasAlpha() || mPaint.getAlpha() < 255)) ?
44193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
44293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
44393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
44493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
44540662f4b39e795d9c64502b13036e7c37fa2d373Sam Blitzstein    public void onDecodeBegin(final RequestKey key) { }
44693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
44793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
44840662f4b39e795d9c64502b13036e7c37fa2d373Sam Blitzstein    public void onDecodeComplete(final RequestKey key, final ReusableBitmap result) {
44993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (key.equals(mCurrKey)) {
45093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            setBitmap(result);
45193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        } else {
45293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // if the requests don't match (i.e. this request is stale), decrement the
45393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // ref count to allow the bitmap to be pooled
45493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (result != null) {
45593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                result.releaseReference();
45693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
45793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
45893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
45993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
46093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
46140662f4b39e795d9c64502b13036e7c37fa2d373Sam Blitzstein    public void onDecodeCancel(final RequestKey key) { }
46293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
46393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
46493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public void invalidateDrawable(Drawable who) {
46593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        invalidateSelf();
46693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
46793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
46893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
46993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public void scheduleDrawable(Drawable who, Runnable what, long when) {
47093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        scheduleSelf(what, when);
47193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
47293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
47393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
47493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public void unscheduleDrawable(Drawable who, Runnable what) {
47593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        unscheduleSelf(what);
47693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
47793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein}
478