BasicBitmapDrawable.java revision 10dddd8a24a80d1d539997d8eaa9763c62bd02ad
16bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines/*
26bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * Copyright (C) 2013 The Android Open Source Project
36bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines *
46bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * Licensed under the Apache License, Version 2.0 (the "License");
56bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * you may not use this file except in compliance with the License.
66bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * You may obtain a copy of the License at
76bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines *
86bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines *      http://www.apache.org/licenses/LICENSE-2.0
96bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines *
106bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * Unless required by applicable law or agreed to in writing, software
116bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * distributed under the License is distributed on an "AS IS" BASIS,
126bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * See the License for the specific language governing permissions and
146bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * limitations under the License.
156bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines */
166bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinespackage com.android.bitmap.drawable;
176bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
186bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport android.content.res.Resources;
196bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport android.graphics.Canvas;
206bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport android.graphics.ColorFilter;
216bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport android.graphics.Paint;
226bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport android.graphics.PixelFormat;
236bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport android.graphics.Rect;
246bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport android.graphics.drawable.Drawable;
256bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport android.util.DisplayMetrics;
266bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport android.util.Log;
276bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
286bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport com.android.bitmap.BitmapCache;
296bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport com.android.bitmap.DecodeTask;
306bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport com.android.bitmap.DecodeTask.DecodeCallback;
316bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport com.android.bitmap.DecodeTask.DecodeOptions;
326bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport com.android.bitmap.NamedThreadFactory;
336bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport com.android.bitmap.RequestKey;
346bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport com.android.bitmap.RequestKey.Cancelable;
356bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport com.android.bitmap.RequestKey.FileDescriptorFactory;
366bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport com.android.bitmap.ReusableBitmap;
376bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport com.android.bitmap.util.BitmapUtils;
386bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport com.android.bitmap.util.RectUtils;
396bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport com.android.bitmap.util.Trace;
406bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
416bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport java.util.concurrent.Executor;
426bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport java.util.concurrent.LinkedBlockingQueue;
436bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport java.util.concurrent.ThreadPoolExecutor;
446bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport java.util.concurrent.TimeUnit;
456bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
466bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines/**
476bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * This class encapsulates the basic functionality needed to display a single image bitmap,
486bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * including request creation/cancelling, and data unbinding and re-binding.
496bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * <p>
506bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * The actual bitmap decode work is handled by {@link DecodeTask}.
516bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * <p>
526bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * If being used with a long-lived cache (static cache, attached to the Application instead of the
536bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * Activity, etc) then make sure to call {@link BasicBitmapDrawable#unbind()} at the appropriate
546bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * times so the cache has accurate unref counts. The
556bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * {@link com.android.bitmap.view.BitmapDrawableImageView} class has been created to do the
566bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * appropriate unbind operation when the view is detached from the window.
576bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines */
586bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinespublic class BasicBitmapDrawable extends Drawable implements DecodeCallback,
596bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        Drawable.Callback, RequestKey.Callback {
606bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
616bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    protected static Rect sRect;
626bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
636bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    protected RequestKey mCurrKey;
646bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    protected RequestKey mPrevKey;
656bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
666bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    protected final Paint mPaint = new Paint();
676bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    private final BitmapCache mCache;
686bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
696bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    private final boolean mLimitDensity;
706bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    private final float mDensity;
716bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    private ReusableBitmap mBitmap;
726bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    private DecodeTask mTask;
736bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    private Cancelable mCreateFileDescriptorFactoryTask;
746bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    private int mDecodeWidth;
756bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    private int mDecodeHeight;
766bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
776bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    // based on framework CL:I015d77
786bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
796bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
806bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
816bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
826bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    private static final Executor SMALL_POOL_EXECUTOR = new ThreadPoolExecutor(
836bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, 1, TimeUnit.SECONDS,
846bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            new LinkedBlockingQueue<Runnable>(128), new NamedThreadFactory("decode"));
856bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    private static final Executor EXECUTOR = SMALL_POOL_EXECUTOR;
866bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
876bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    private static final int MAX_BITMAP_DENSITY = DisplayMetrics.DENSITY_HIGH;
886bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    private static final float VERTICAL_CENTER = 1f / 2;
896bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    private static final float NO_MULTIPLIER = 1f;
906bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
916bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    private static final String TAG = BasicBitmapDrawable.class.getSimpleName();
926bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    private static final boolean DEBUG = DecodeTask.DEBUG;
936bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
946bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    public BasicBitmapDrawable(final Resources res, final BitmapCache cache,
956bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            final boolean limitDensity) {
966bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        mDensity = res.getDisplayMetrics().density;
976bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        mCache = cache;
986bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        mLimitDensity = limitDensity;
996bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        mPaint.setFilterBitmap(true);
1006bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        mPaint.setAntiAlias(true);
1016bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        mPaint.setDither(true);
1026bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
1036bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        if (sRect == null) {
1046bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            sRect = new Rect();
1056bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        }
1066bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    }
1076bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
1086bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    public final RequestKey getKey() {
1096bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        return mCurrKey;
1106bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    }
1116bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
112ef8225444452a1486bd721f3285301fe84643b00Stephen Hines    protected final ReusableBitmap getBitmap() {
1136bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        return mBitmap;
114ef8225444452a1486bd721f3285301fe84643b00Stephen Hines    }
1156bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
1166bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    /**
1176bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines     * Set the dimensions to decode into. These dimensions should never change while the drawable is
1186bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines     * attached to the same cache, because caches can only contain bitmaps of one size for re-use.
1196bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines     *
1206bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines     * All UI operations should be called from the UI thread.
1216bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines     */
1226bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    public final void setDecodeDimensions(int w, int h) {
123ef8225444452a1486bd721f3285301fe84643b00Stephen Hines        mDecodeWidth = w;
124ef8225444452a1486bd721f3285301fe84643b00Stephen Hines        mDecodeHeight = h;
125ef8225444452a1486bd721f3285301fe84643b00Stephen Hines        loadFileDescriptorFactory();
126ef8225444452a1486bd721f3285301fe84643b00Stephen Hines    }
127ef8225444452a1486bd721f3285301fe84643b00Stephen Hines
128ef8225444452a1486bd721f3285301fe84643b00Stephen Hines    /**
129ef8225444452a1486bd721f3285301fe84643b00Stephen Hines     * Binds to the given key and start the decode process. This will first look in the cache, then
130ef8225444452a1486bd721f3285301fe84643b00Stephen Hines     * decode from the request key if not found.
1316bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines     *
1326bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines     * All UI operations should be called from the UI thread.
1336bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines     */
1346bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    public final void bind(RequestKey key) {
1356bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        setImage(key);
1366bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    }
1376bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
1386bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    /**
1396bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines     * Unbinds the current key and bitmap from the drawable. This will cause the bitmap to decrement
1406bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines     * its ref count.
1416bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines     *
1426bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines     * All UI operations should be called from the UI thread.
1436bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines     */
1446bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    public final void unbind() {
1456bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        setImage(null);
1466bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    }
1476bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
1486bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    /**
1496bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines     * Should only be overriden, not called.
1506bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines     */
1516bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    protected void setImage(final RequestKey key) {
1526bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        if (mCurrKey != null && mCurrKey.equals(key)) {
1536bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            return;
1546bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        }
1556bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
1566bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        Trace.beginSection("set image");
1576bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        Trace.beginSection("release reference");
1586bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        if (mBitmap != null) {
1596bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            mBitmap.releaseReference();
1606bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            mBitmap = null;
1616bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        }
1626bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        Trace.endSection();
1636bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
1646bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        mPrevKey = mCurrKey;
1656bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        mCurrKey = key;
1666bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
1676bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        if (mTask != null) {
1686bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            mTask.cancel();
1696bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            mTask = null;
1706bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        }
1716bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        if (mCreateFileDescriptorFactoryTask != null) {
1726bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            mCreateFileDescriptorFactoryTask.cancel();
1736bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            mCreateFileDescriptorFactoryTask = null;
1746bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        }
175ef8225444452a1486bd721f3285301fe84643b00Stephen Hines
176ef8225444452a1486bd721f3285301fe84643b00Stephen Hines        if (key == null) {
177ef8225444452a1486bd721f3285301fe84643b00Stephen Hines            invalidateSelf();
178ef8225444452a1486bd721f3285301fe84643b00Stephen Hines            Trace.endSection();
179ef8225444452a1486bd721f3285301fe84643b00Stephen Hines            return;
180ef8225444452a1486bd721f3285301fe84643b00Stephen Hines        }
1816bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
1826bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        // find cached entry here and skip decode if found.
1836bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        final ReusableBitmap cached = mCache.get(key, true /* incrementRefCount */);
1846bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        if (cached != null) {
1856bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            setBitmap(cached);
1866bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            if (DEBUG) {
1876bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines                Log.d(TAG, String.format("CACHE HIT key=%s", mCurrKey));
1886bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            }
1896bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        } else {
1906bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            loadFileDescriptorFactory();
1916bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            if (DEBUG) {
1926bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines                Log.d(TAG, String.format(
1936bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines                        "CACHE MISS key=%s\ncache=%s", mCurrKey, mCache.toDebugString()));
1946bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            }
1956bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        }
1966bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        Trace.endSection();
1976bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    }
1986bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
1996bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    /**
2006bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines     * Should only be overriden, not called.
2016bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines     */
2026bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    protected void setBitmap(ReusableBitmap bmp) {
2036bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        if (mBitmap != null && mBitmap != bmp) {
2046bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            mBitmap.releaseReference();
2056bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        }
2066bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        mBitmap = bmp;
2076bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        invalidateSelf();
2086bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    }
2096bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
2106bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    /**
2116bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines     * Should only be overriden, not called.
2126bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines     */
2136bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    protected void loadFileDescriptorFactory() {
2146bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        if (mCurrKey == null || mDecodeWidth == 0 || mDecodeHeight == 0) {
2156bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            return;
2166bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        }
2176bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
2186bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        // Create file descriptor if request supports it.
2196bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        mCreateFileDescriptorFactoryTask = mCurrKey
2206bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines                .createFileDescriptorFactoryAsync(mCurrKey, this);
2216bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        if (mCreateFileDescriptorFactoryTask == null) {
2226bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            // Use input stream if request does not.
2236bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            decode(null);
2246bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        }
2256bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    }
2266bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
2276bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    @Override
2286bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    public void fileDescriptorFactoryCreated(final RequestKey key,
2296bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            final FileDescriptorFactory factory) {
2306bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        if (mCreateFileDescriptorFactoryTask == null) {
2316bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            // Cancelled.
2326bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            return;
2336bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        }
2346bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        mCreateFileDescriptorFactoryTask = null;
2356bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
2366bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        if (key.equals(mCurrKey)) {
2376bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            decode(factory);
2386bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        }
2396bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    }
2406bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
2416bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    /**
2426bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines     * Should only be overriden, not called.
2436bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines     */
2446bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    protected void decode(final FileDescriptorFactory factory) {
2456bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        Trace.beginSection("decode");
2466bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        final int bufferW;
2476bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        final int bufferH;
2486bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        if (mLimitDensity) {
2496bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            final float scale =
2506bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines                    Math.min(1f, (float) MAX_BITMAP_DENSITY / DisplayMetrics.DENSITY_DEFAULT
2516bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines                            / mDensity);
2526bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            bufferW = (int) (mDecodeWidth * scale);
2536bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            bufferH = (int) (mDecodeHeight * scale);
2546bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        } else {
2556bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            bufferW = mDecodeWidth;
2566bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            bufferH = mDecodeHeight;
2576bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        }
2586bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
2596bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        if (mTask != null) {
2606bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            mTask.cancel();
2616bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        }
2626bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        final DecodeOptions opts = new DecodeOptions(bufferW, bufferH, getDecodeVerticalCenter(),
2636bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines                DecodeOptions.STRATEGY_ROUND_NEAREST);
2646bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        mTask = new DecodeTask(mCurrKey, opts, factory, this, mCache);
2656bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        mTask.executeOnExecutor(getExecutor());
2666bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        Trace.endSection();
2676bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    }
2686bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
2696bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    protected Executor getExecutor() {
2706bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        return EXECUTOR;
2716bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    }
2726bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
2736bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    protected float getDrawVerticalCenter() {
2746bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        return VERTICAL_CENTER;
2756bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    }
2766bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
2776bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    protected float getDrawVerticalOffsetMultiplier() {
2786bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        return NO_MULTIPLIER;
2796bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    }
2806bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
2816bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    protected float getDecodeVerticalCenter() {
2826bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        return VERTICAL_CENTER;
2836bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    }
2846bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
2856bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    @Override
2866bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    public void draw(final Canvas canvas) {
2876bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        final Rect bounds = getBounds();
2886bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        if (bounds.isEmpty()) {
2896bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            return;
2906bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        }
2916bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
2926bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        if (mBitmap != null && mBitmap.bmp != null) {
2936bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            BitmapUtils.calculateCroppedSrcRect(
2946bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines                    mBitmap.getLogicalWidth(), mBitmap.getLogicalHeight(),
2956bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines                    bounds.width(), bounds.height(),
2966bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines                    bounds.height(), Integer.MAX_VALUE,
2976bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines                    getDrawVerticalCenter(), false /* absoluteFraction */,
2986bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines                    getDrawVerticalOffsetMultiplier(), sRect);
2996bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
3006bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            final int orientation = mBitmap.getOrientation();
3016bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            // calculateCroppedSrcRect() gave us the source rectangle "as if" the orientation has
3026bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            // been corrected. We need to decode the uncorrected source rectangle. Calculate true
3036bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            // coordinates.
3046bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            RectUtils.rotateRectForOrientation(orientation,
3056bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines                    new Rect(0, 0, mBitmap.getLogicalWidth(), mBitmap.getLogicalHeight()),
3066bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines                    sRect);
3076bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
3086bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            // We may need to rotate the canvas, so we also have to rotate the bounds.
3096bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            final Rect rotatedBounds = new Rect(bounds);
3106bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            RectUtils.rotateRect(orientation, bounds.centerX(), bounds.centerY(), rotatedBounds);
3116bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines
3126bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            // Rotate the canvas.
3136bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            canvas.save();
3146bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            canvas.rotate(orientation, bounds.centerX(), bounds.centerY());
3156bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            onDrawBitmap(canvas, rotatedBounds);
3166bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines            canvas.restore();
317        }
318    }
319
320    /**
321     * Override this method to customize how to draw the bitmap to the canvas for the given bounds.
322     * The bitmap to be drawn can be found at {@link #getBitmap()}.
323     */
324    protected void onDrawBitmap(final Canvas canvas, final Rect bounds) {
325        canvas.drawBitmap(mBitmap.bmp, sRect, bounds, mPaint);
326    }
327
328    @Override
329    public void setAlpha(int alpha) {
330        final int old = mPaint.getAlpha();
331        mPaint.setAlpha(alpha);
332        if (alpha != old) {
333            invalidateSelf();
334        }
335    }
336
337    @Override
338    public void setColorFilter(ColorFilter cf) {
339        mPaint.setColorFilter(cf);
340        invalidateSelf();
341    }
342
343    @Override
344    public int getOpacity() {
345        return (mBitmap != null && (mBitmap.bmp.hasAlpha() || mPaint.getAlpha() < 255)) ?
346                PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
347    }
348
349    @Override
350    public void onDecodeBegin(final RequestKey key) { }
351
352    @Override
353    public void onDecodeComplete(final RequestKey key, final ReusableBitmap result) {
354        if (key.equals(mCurrKey)) {
355            setBitmap(result);
356        } else {
357            // if the requests don't match (i.e. this request is stale), decrement the
358            // ref count to allow the bitmap to be pooled
359            if (result != null) {
360                result.releaseReference();
361            }
362        }
363    }
364
365    @Override
366    public void onDecodeCancel(final RequestKey key) { }
367
368    @Override
369    public void invalidateDrawable(Drawable who) {
370        invalidateSelf();
371    }
372
373    @Override
374    public void scheduleDrawable(Drawable who, Runnable what, long when) {
375        scheduleSelf(what, when);
376    }
377
378    @Override
379    public void unscheduleDrawable(Drawable who, Runnable what) {
380        unscheduleSelf(what);
381    }
382}
383