1f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu/* 2f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu * Copyright (C) 2013 The Android Open Source Project 3f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu * 4f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu * Licensed under the Apache License, Version 2.0 (the "License"); 5f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu * you may not use this file except in compliance with the License. 6f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu * You may obtain a copy of the License at 7f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu * 8f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu * http://www.apache.org/licenses/LICENSE-2.0 9f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu * 10f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu * Unless required by applicable law or agreed to in writing, software 11f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu * distributed under the License is distributed on an "AS IS" BASIS, 12f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu * See the License for the specific language governing permissions and 14f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu * limitations under the License. 15f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu */ 16f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu 17f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescupackage com.android.photos.data; 18f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu 19f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescuimport android.graphics.Bitmap; 20f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescuimport android.graphics.Point; 21f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescuimport android.util.Pools.Pool; 223e43b59805993151c76fcc3f0069cc99985811d3Bobby Georgescuimport android.util.Pools.SynchronizedPool; 23f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu 24f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescuimport com.android.photos.data.SparseArrayBitmapPool.Node; 25f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu 261c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu/** 271c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu * Pool allowing the efficient reuse of bitmaps in order to avoid long 281c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu * garbage collection pauses. 291c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu */ 30f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescupublic class GalleryBitmapPool { 31f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu 32f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu private static final int CAPACITY_BYTES = 20971520; 331c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu 341c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu // We found that Gallery uses bitmaps that are either square (for example, 351c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu // tiles of large images or square thumbnails), match one of the common 361c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu // photo aspect ratios (4x3, 3x2, or 16x9), or, less commonly, are of some 371c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu // other aspect ratio. Taking advantage of this information, we use 3 381c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu // SparseArrayBitmapPool instances to back the GalleryBitmapPool, which affords 391c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu // O(1) lookups for square bitmaps, and average-case - but *not* asymptotically - 401c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu // O(1) lookups for common photo aspect ratios and other miscellaneous aspect 411c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu // ratios. Beware of the pathological case where there are many bitmaps added 421c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu // to the pool with different non-square aspect ratios but the same width, as 431c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu // performance will degrade and the average case lookup will approach 441c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu // O(# of different aspect ratios). 45f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu private static final int POOL_INDEX_NONE = -1; 46f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu private static final int POOL_INDEX_SQUARE = 0; 47f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu private static final int POOL_INDEX_PHOTO = 1; 48f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu private static final int POOL_INDEX_MISC = 2; 49f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu 50f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu private static final Point[] COMMON_PHOTO_ASPECT_RATIOS = 51f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu { new Point(4, 3), new Point(3, 2), new Point(16, 9) }; 52f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu 53f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu private int mCapacityBytes; 54f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu private SparseArrayBitmapPool [] mPools; 553e43b59805993151c76fcc3f0069cc99985811d3Bobby Georgescu private Pool<Node> mSharedNodePool = new SynchronizedPool<Node>(128); 56f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu 57f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu private GalleryBitmapPool(int capacityBytes) { 58f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu mPools = new SparseArrayBitmapPool[3]; 59f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu mPools[POOL_INDEX_SQUARE] = new SparseArrayBitmapPool(capacityBytes / 3, mSharedNodePool); 60f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu mPools[POOL_INDEX_PHOTO] = new SparseArrayBitmapPool(capacityBytes / 3, mSharedNodePool); 61f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu mPools[POOL_INDEX_MISC] = new SparseArrayBitmapPool(capacityBytes / 3, mSharedNodePool); 62f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu mCapacityBytes = capacityBytes; 63f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } 64f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu 6508c0f25091f20c4d618b5adaa381ed6eedf4695fJohn Reck private static GalleryBitmapPool sInstance = new GalleryBitmapPool(CAPACITY_BYTES); 6608c0f25091f20c4d618b5adaa381ed6eedf4695fJohn Reck 67f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu public static GalleryBitmapPool getInstance() { 68f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu return sInstance; 69f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } 70f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu 71f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu private SparseArrayBitmapPool getPoolForDimensions(int width, int height) { 72f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu int index = getPoolIndexForDimensions(width, height); 73f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu if (index == POOL_INDEX_NONE) { 74f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu return null; 75f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } else { 76f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu return mPools[index]; 77f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } 78f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } 79f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu 80f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu private int getPoolIndexForDimensions(int width, int height) { 81f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu if (width <= 0 || height <= 0) { 82f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu return POOL_INDEX_NONE; 83f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } 84f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu if (width == height) { 85f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu return POOL_INDEX_SQUARE; 86f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } 87f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu int min, max; 88f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu if (width > height) { 89f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu min = height; 90f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu max = width; 91f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } else { 92f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu min = width; 93f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu max = height; 94f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } 95f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu for (Point ar : COMMON_PHOTO_ASPECT_RATIOS) { 96f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu if (min * ar.x == max * ar.y) { 97f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu return POOL_INDEX_PHOTO; 98f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } 99f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } 100f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu return POOL_INDEX_MISC; 101f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } 102f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu 1031c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu /** 1041c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu * @return Capacity of the pool in bytes. 1051c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu */ 106f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu public synchronized int getCapacity() { 107f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu return mCapacityBytes; 108f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } 109f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu 1101c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu /** 1111c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu * @return Approximate total size in bytes of the bitmaps stored in the pool. 1121c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu */ 1131c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu public int getSize() { 1141c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu // Note that this only returns an approximate size, since multiple threads 1151c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu // might be getting and putting Bitmaps from the pool and we lock at the 1161c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu // sub-pool level to avoid unnecessary blocking. 117f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu int total = 0; 118f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu for (SparseArrayBitmapPool p : mPools) { 119f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu total += p.getSize(); 120f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } 121f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu return total; 122f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } 123f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu 1241c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu /** 1251c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu * @return Bitmap from the pool with the desired height/width or null if none available. 1261c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu */ 127f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu public Bitmap get(int width, int height) { 128f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu SparseArrayBitmapPool pool = getPoolForDimensions(width, height); 129f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu if (pool == null) { 130f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu return null; 131f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } else { 132f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu return pool.get(width, height); 133f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } 134f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } 135f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu 1361c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu /** 1371c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu * Adds the given bitmap to the pool. 1381c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu * @return Whether the bitmap was added to the pool. 1391c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu */ 140f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu public boolean put(Bitmap b) { 1416058af443b087ba50741cc3e61ec70e830e498faBobby Georgescu if (b == null || b.getConfig() != Bitmap.Config.ARGB_8888) { 142f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu return false; 143f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } 144f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu SparseArrayBitmapPool pool = getPoolForDimensions(b.getWidth(), b.getHeight()); 145f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu if (pool == null) { 146f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu b.recycle(); 147f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu return false; 148f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } else { 149f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu return pool.put(b); 150f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } 151f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } 152f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu 1531c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu /** 1541c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu * Empty the pool, recycling all the bitmaps currently in it. 1551c89159dc7c9a5cddb69b62144c3333c6b6da55dBobby Georgescu */ 156f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu public void clear() { 157f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu for (SparseArrayBitmapPool p : mPools) { 158f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu p.clear(); 159f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } 160f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu } 161f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu} 162