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