1d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/*
2d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Copyright (C) 2015 The Android Open Source Project
3d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
4d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Licensed under the Apache License, Version 2.0 (the "License");
5d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * you may not use this file except in compliance with the License.
6d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * You may obtain a copy of the License at
7d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
8d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *      http://www.apache.org/licenses/LICENSE-2.0
9d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
10d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Unless required by applicable law or agreed to in writing, software
11d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * distributed under the License is distributed on an "AS IS" BASIS,
12d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * See the License for the specific language governing permissions and
14d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * limitations under the License.
15d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */
16d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
17d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpackage com.android.messaging.datamodel;
18d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
19d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.res.Resources;
20d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.graphics.Bitmap;
21d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.graphics.BitmapFactory;
22d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.support.annotation.NonNull;
23d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.text.TextUtils;
24d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.util.SparseArray;
25d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
26d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.MemoryCacheManager.MemoryCache;
27d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.Assert;
28d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.LogUtil;
29d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
30d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.io.InputStream;
31d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
32d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/**
33d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Class for creating / loading / reusing bitmaps. This class allow the user to create a new bitmap,
34d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * reuse an bitmap from the pool and to return a bitmap for future reuse.  The pool of bitmaps
35d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * allows for faster decode and more efficient memory usage.
36d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Note: consumers should not create BitmapPool directly, but instead get the pool they want from
37d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * the BitmapPoolManager.
38d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */
39d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpublic class BitmapPool implements MemoryCache {
40d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static final int MAX_SUPPORTED_IMAGE_DIMENSION = 0xFFFF;
41d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
42d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected static final boolean VERBOSE = false;
43d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
44d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
45d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Number of reuse failures to skip before reporting.
46d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
47d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final int FAILED_REPORTING_FREQUENCY = 100;
48d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
49d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
50d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Count of reuse failures which have occurred.
51d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
52d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static volatile int sFailedBitmapReuseCount = 0;
53d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
54d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
55d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Overall pool data structure which currently only supports rectangular bitmaps. The size of
56d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * one of the sides is used to index into the SparseArray.
57d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
58d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private final SparseArray<SingleSizePool> mPool;
59d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private final Object mPoolLock = new Object();
60d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private final String mPoolName;
61d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private final int mMaxSize;
62d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
63d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
64d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Inner structure which holds a pool of bitmaps all the same size (i.e. all have the same
65d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * width as each other and height as each other, but not necessarily the same).
66d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
67d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private class SingleSizePool {
68d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        int mNumItems;
69d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Bitmap[] mBitmaps;
70d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
71d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        SingleSizePool(final int maxPoolSize) {
72d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mNumItems = 0;
73d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mBitmaps = new Bitmap[maxPoolSize];
74d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
75d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
76d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
77d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
78d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Creates a pool of reused bitmaps with helper decode methods which will attempt to use the
79d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * reclaimed bitmaps. This will help speed up the creation of bitmaps by using already allocated
80d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * bitmaps.
81d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param maxSize The overall max size of the pool. When the pool exceeds this size, all calls
82d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * to reclaimBitmap(Bitmap) will result in recycling the bitmap.
83d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param name Name of the bitmap pool and only used for logging. Can not be null.
84d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
85d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    BitmapPool(final int maxSize, @NonNull final String name) {
86d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Assert.isTrue(maxSize > 0);
87d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Assert.isTrue(!TextUtils.isEmpty(name));
88d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mPoolName = name;
89d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mMaxSize = maxSize;
90d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mPool = new SparseArray<SingleSizePool>();
91d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
92d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
93d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @Override
94d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public void reclaim() {
95d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        synchronized (mPoolLock) {
96d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            for (int p = 0; p < mPool.size(); p++) {
97d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                final SingleSizePool singleSizePool = mPool.valueAt(p);
98d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                for (int i = 0; i < singleSizePool.mNumItems; i++) {
99d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    singleSizePool.mBitmaps[i].recycle();
100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    singleSizePool.mBitmaps[i] = null;
101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                singleSizePool.mNumItems = 0;
103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mPool.clear();
105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Creates a new BitmapFactory.Options.
110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static BitmapFactory.Options getBitmapOptionsForPool(final boolean scaled,
112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final int inputDensity, final int targetDensity) {
113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final BitmapFactory.Options options = new BitmapFactory.Options();
114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        options.inScaled = scaled;
115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        options.inDensity = inputDensity;
116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        options.inTargetDensity = targetDensity;
117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        options.inSampleSize = 1;
118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        options.inJustDecodeBounds = false;
119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        options.inMutable = true;
120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return options;
121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @return The pool key for the provided image dimensions or 0 if either width or height is
125d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * greater than the max supported image dimension.
126d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
127d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private int getPoolKey(final int width, final int height) {
128d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (width > MAX_SUPPORTED_IMAGE_DIMENSION || height > MAX_SUPPORTED_IMAGE_DIMENSION) {
129d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return 0;
130d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
131d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return (width << 16) | height;
132d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
133d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
134d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
135d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *
136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @return A bitmap in the pool with the specified dimensions or null if no bitmap with the
137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * specified dimension is available.
138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private Bitmap findPoolBitmap(final int width, final int height) {
140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final int poolKey = getPoolKey(width, height);
141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (poolKey != 0) {
142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            synchronized (mPoolLock) {
143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                // Take a bitmap from the pool if one is available
144d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                final SingleSizePool singlePool = mPool.get(poolKey);
145d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                if (singlePool != null && singlePool.mNumItems > 0) {
146d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    singlePool.mNumItems--;
147d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    final Bitmap foundBitmap = singlePool.mBitmaps[singlePool.mNumItems];
148d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    singlePool.mBitmaps[singlePool.mNumItems] = null;
149d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    return foundBitmap;
150d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
151d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
152d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
153d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return null;
154d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
155d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
156d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
157d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Internal function to try and find a bitmap in the pool which matches the desired width and
158d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * height and then set that in the bitmap options properly.
159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *
160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * TODO: Why do we take a width/height? Shouldn't this already be in the
161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * BitmapFactory.Options instance? Can we assert that they match?
162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param optionsTmp The BitmapFactory.Options to update with the bitmap for the system to try
163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * to reuse.
164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param width The width of the reusable bitmap.
165d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param height The height of the reusable bitmap.
166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private void assignPoolBitmap(final BitmapFactory.Options optionsTmp, final int width,
168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final int height) {
169d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (optionsTmp.inJustDecodeBounds) {
170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return;
171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
172d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        optionsTmp.inBitmap = findPoolBitmap(width, height);
173d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
174d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
175d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
176d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Load a resource into a bitmap. Uses a bitmap from the pool if possible to reduce memory
177d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * turnover.
178d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param resourceId Resource id to load.
179d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param resources Application resources. Cannot be null.
180d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param optionsTmp Should be the same options returned from getBitmapOptionsForPool(). Cannot
181d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * be null.
182d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param width The width of the bitmap.
183d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param height The height of the bitmap.
184d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @return The decoded Bitmap with the resource drawn in it.
185d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
186d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public Bitmap decodeSampledBitmapFromResource(final int resourceId,
187d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            @NonNull final Resources resources, @NonNull final BitmapFactory.Options optionsTmp,
188d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final int width, final int height) {
189d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Assert.notNull(resources);
190d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Assert.notNull(optionsTmp);
191d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Assert.isTrue(width > 0);
192d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Assert.isTrue(height > 0);
193d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        assignPoolBitmap(optionsTmp, width, height);
194d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Bitmap b = null;
195d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
196d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            b = BitmapFactory.decodeResource(resources, resourceId, optionsTmp);
197d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (final IllegalArgumentException e) {
198d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // BitmapFactory couldn't decode the file, try again without an inputBufferBitmap.
199d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (optionsTmp.inBitmap != null) {
200d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                optionsTmp.inBitmap = null;
201d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                b = BitmapFactory.decodeResource(resources, resourceId, optionsTmp);
202d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                sFailedBitmapReuseCount++;
203d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                if (sFailedBitmapReuseCount % FAILED_REPORTING_FREQUENCY == 0) {
204d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    LogUtil.w(LogUtil.BUGLE_TAG,
205d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            "Pooled bitmap consistently not being reused count = " +
206d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            sFailedBitmapReuseCount);
207d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
208d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
209d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (final OutOfMemoryError e) {
210d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.w(LogUtil.BUGLE_TAG, "Oom decoding resource " + resourceId);
211d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            reclaim();
212d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
213d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return b;
214d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
215d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
216d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
217d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Load an input stream into a bitmap. Uses a bitmap from the pool if possible to reduce memory
218d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * turnover.
219d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param inputStream InputStream load. Cannot be null.
220d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param optionsTmp Should be the same options returned from getBitmapOptionsForPool(). Cannot
221d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * be null.
222d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param width The width of the bitmap.
223d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param height The height of the bitmap.
224d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @return The decoded Bitmap with the resource drawn in it.
225d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
226d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public Bitmap decodeSampledBitmapFromInputStream(@NonNull final InputStream inputStream,
227d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            @NonNull final BitmapFactory.Options optionsTmp,
228d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final int width, final int height) {
229d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Assert.notNull(inputStream);
230d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Assert.isTrue(width > 0);
231d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Assert.isTrue(height > 0);
232d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        assignPoolBitmap(optionsTmp, width, height);
233d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Bitmap b = null;
234d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
235d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            b = BitmapFactory.decodeStream(inputStream, null, optionsTmp);
236d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (final IllegalArgumentException e) {
237d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // BitmapFactory couldn't decode the file, try again without an inputBufferBitmap.
238d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (optionsTmp.inBitmap != null) {
239d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                optionsTmp.inBitmap = null;
240d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                b = BitmapFactory.decodeStream(inputStream, null, optionsTmp);
241d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                sFailedBitmapReuseCount++;
242d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                if (sFailedBitmapReuseCount % FAILED_REPORTING_FREQUENCY == 0) {
243d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    LogUtil.w(LogUtil.BUGLE_TAG,
244d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            "Pooled bitmap consistently not being reused count = " +
245d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            sFailedBitmapReuseCount);
246d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
247d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
248d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (final OutOfMemoryError e) {
249d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.w(LogUtil.BUGLE_TAG, "Oom decoding inputStream");
250d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            reclaim();
251d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
252d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return b;
253d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
254d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
255d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
256d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Turn encoded bytes into a bitmap. Uses a bitmap from the pool if possible to reduce memory
257d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * turnover.
258d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param bytes Encoded bytes to draw on the bitmap. Cannot be null.
259d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param optionsTmp The bitmap will set here and the input should be generated from
260d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * getBitmapOptionsForPool(). Cannot be null.
261d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param width The width of the bitmap.
262d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param height The height of the bitmap.
263d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @return A Bitmap with the encoded bytes drawn in it.
264d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
265d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public Bitmap decodeByteArray(@NonNull final byte[] bytes,
266d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            @NonNull final BitmapFactory.Options optionsTmp, final int width,
267d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final int height) throws OutOfMemoryError {
268d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Assert.notNull(bytes);
269d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Assert.notNull(optionsTmp);
270d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Assert.isTrue(width > 0);
271d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Assert.isTrue(height > 0);
272d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        assignPoolBitmap(optionsTmp, width, height);
273d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Bitmap b = null;
274d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
275d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            b = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, optionsTmp);
276d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (final IllegalArgumentException e) {
277d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (VERBOSE) {
278d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                LogUtil.v(LogUtil.BUGLE_TAG, "BitmapPool(" + mPoolName +
279d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        ") Unable to use pool bitmap");
280d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
281d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // BitmapFactory couldn't decode the file, try again without an inputBufferBitmap.
282d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // (i.e. without the bitmap from the pool)
283d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (optionsTmp.inBitmap != null) {
284d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                optionsTmp.inBitmap = null;
285d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                b = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, optionsTmp);
286d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                sFailedBitmapReuseCount++;
287d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                if (sFailedBitmapReuseCount % FAILED_REPORTING_FREQUENCY == 0) {
288d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    LogUtil.w(LogUtil.BUGLE_TAG,
289d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            "Pooled bitmap consistently not being reused count = " +
290d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            sFailedBitmapReuseCount);
291d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
292d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
293d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
294d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return b;
295d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
296d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
297d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
298d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Creates a bitmap with the given size, this will reuse a bitmap in the pool, if one is
299d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * available, otherwise this will create a new one.
300d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param width The desired width of the bitmap.
301d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param height The desired height of the bitmap.
302d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @return A bitmap with the desired width and height, this maybe a reused bitmap from the pool.
303d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
304d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public Bitmap createOrReuseBitmap(final int width, final int height) {
305d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Bitmap b = findPoolBitmap(width, height);
306d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (b == null) {
307d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            b = createBitmap(width, height);
308d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
309d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return b;
310d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
311d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
312d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
313d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * This will create a new bitmap regardless of pool state.
314d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param width The desired width of the bitmap.
315d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param height The desired height of the bitmap.
316d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @return A bitmap with the desired width and height.
317d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
318d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private Bitmap createBitmap(final int width, final int height) {
319d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
320d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
321d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
322d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
323d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Called when a bitmap is finished being used so that it can be used for another bitmap in the
324d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * future or recycled. Any bitmaps returned should not be used by the caller again.
325d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param b The bitmap to return to the pool for future usage or recycled. This cannot be null.
326d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
327d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public void reclaimBitmap(@NonNull final Bitmap b) {
328d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Assert.notNull(b);
329d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final int poolKey = getPoolKey(b.getWidth(), b.getHeight());
330d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (poolKey == 0 || !b.isMutable()) {
331d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // Unsupported image dimensions or a immutable bitmap.
332d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            b.recycle();
333d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return;
334d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
335d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        synchronized (mPoolLock) {
336d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            SingleSizePool singleSizePool = mPool.get(poolKey);
337d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (singleSizePool == null) {
338d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                singleSizePool = new SingleSizePool(mMaxSize);
339d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                mPool.append(poolKey, singleSizePool);
340d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
341d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (singleSizePool.mNumItems < singleSizePool.mBitmaps.length) {
342d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                singleSizePool.mBitmaps[singleSizePool.mNumItems] = b;
343d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                singleSizePool.mNumItems++;
344d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            } else {
345d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                b.recycle();
346d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
347d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
348d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
349d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
350d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
351d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @return whether the pool is full for a given width and height.
352d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
353d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public boolean isFull(final int width, final int height) {
354d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final int poolKey = getPoolKey(width, height);
355d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        synchronized (mPoolLock) {
356d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final SingleSizePool singleSizePool = mPool.get(poolKey);
357d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (singleSizePool != null &&
358d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    singleSizePool.mNumItems >= singleSizePool.mBitmaps.length) {
359d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                return true;
360d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
361d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return false;
362d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
363d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
364d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd}
365