Home | History | Annotate | Download | only in data
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.photos.data;
     18 
     19 import android.graphics.Bitmap;
     20 import android.graphics.Point;
     21 import android.util.Pools.Pool;
     22 import android.util.Pools.SynchronizedPool;
     23 
     24 import com.android.photos.data.SparseArrayBitmapPool.Node;
     25 
     26 /**
     27  * Pool allowing the efficient reuse of bitmaps in order to avoid long
     28  * garbage collection pauses.
     29  */
     30 public class GalleryBitmapPool {
     31 
     32     private static final int CAPACITY_BYTES = 20971520;
     33 
     34     // We found that Gallery uses bitmaps that are either square (for example,
     35     // tiles of large images or square thumbnails), match one of the common
     36     // photo aspect ratios (4x3, 3x2, or 16x9), or, less commonly, are of some
     37     // other aspect ratio. Taking advantage of this information, we use 3
     38     // SparseArrayBitmapPool instances to back the GalleryBitmapPool, which affords
     39     // O(1) lookups for square bitmaps, and average-case - but *not* asymptotically -
     40     // O(1) lookups for common photo aspect ratios and other miscellaneous aspect
     41     // ratios. Beware of the pathological case where there are many bitmaps added
     42     // to the pool with different non-square aspect ratios but the same width, as
     43     // performance will degrade and the average case lookup will approach
     44     // O(# of different aspect ratios).
     45     private static final int POOL_INDEX_NONE = -1;
     46     private static final int POOL_INDEX_SQUARE = 0;
     47     private static final int POOL_INDEX_PHOTO = 1;
     48     private static final int POOL_INDEX_MISC = 2;
     49 
     50     private static final Point[] COMMON_PHOTO_ASPECT_RATIOS =
     51         { new Point(4, 3), new Point(3, 2), new Point(16, 9) };
     52 
     53     private int mCapacityBytes;
     54     private SparseArrayBitmapPool [] mPools;
     55     private Pool<Node> mSharedNodePool = new SynchronizedPool<Node>(128);
     56 
     57     private GalleryBitmapPool(int capacityBytes) {
     58         mPools = new SparseArrayBitmapPool[3];
     59         mPools[POOL_INDEX_SQUARE] = new SparseArrayBitmapPool(capacityBytes / 3, mSharedNodePool);
     60         mPools[POOL_INDEX_PHOTO] = new SparseArrayBitmapPool(capacityBytes / 3, mSharedNodePool);
     61         mPools[POOL_INDEX_MISC] = new SparseArrayBitmapPool(capacityBytes / 3, mSharedNodePool);
     62         mCapacityBytes = capacityBytes;
     63     }
     64 
     65     private static GalleryBitmapPool sInstance = new GalleryBitmapPool(CAPACITY_BYTES);
     66 
     67     public static GalleryBitmapPool getInstance() {
     68         return sInstance;
     69     }
     70 
     71     private SparseArrayBitmapPool getPoolForDimensions(int width, int height) {
     72         int index = getPoolIndexForDimensions(width, height);
     73         if (index == POOL_INDEX_NONE) {
     74             return null;
     75         } else {
     76             return mPools[index];
     77         }
     78     }
     79 
     80     private int getPoolIndexForDimensions(int width, int height) {
     81         if (width <= 0 || height <= 0) {
     82             return POOL_INDEX_NONE;
     83         }
     84         if (width == height) {
     85             return POOL_INDEX_SQUARE;
     86         }
     87         int min, max;
     88         if (width > height) {
     89             min = height;
     90             max = width;
     91         } else {
     92             min = width;
     93             max = height;
     94         }
     95         for (Point ar : COMMON_PHOTO_ASPECT_RATIOS) {
     96             if (min * ar.x == max * ar.y) {
     97                 return POOL_INDEX_PHOTO;
     98             }
     99         }
    100         return POOL_INDEX_MISC;
    101     }
    102 
    103     /**
    104      * @return Capacity of the pool in bytes.
    105      */
    106     public synchronized int getCapacity() {
    107         return mCapacityBytes;
    108     }
    109 
    110     /**
    111      * @return Approximate total size in bytes of the bitmaps stored in the pool.
    112      */
    113     public int getSize() {
    114         // Note that this only returns an approximate size, since multiple threads
    115         // might be getting and putting Bitmaps from the pool and we lock at the
    116         // sub-pool level to avoid unnecessary blocking.
    117         int total = 0;
    118         for (SparseArrayBitmapPool p : mPools) {
    119             total += p.getSize();
    120         }
    121         return total;
    122     }
    123 
    124     /**
    125      * @return Bitmap from the pool with the desired height/width or null if none available.
    126      */
    127     public Bitmap get(int width, int height) {
    128         SparseArrayBitmapPool pool = getPoolForDimensions(width, height);
    129         if (pool == null) {
    130             return null;
    131         } else {
    132             return pool.get(width, height);
    133         }
    134     }
    135 
    136     /**
    137      * Adds the given bitmap to the pool.
    138      * @return Whether the bitmap was added to the pool.
    139      */
    140     public boolean put(Bitmap b) {
    141         if (b == null || b.getConfig() != Bitmap.Config.ARGB_8888) {
    142             return false;
    143         }
    144         SparseArrayBitmapPool pool = getPoolForDimensions(b.getWidth(), b.getHeight());
    145         if (pool == null) {
    146             b.recycle();
    147             return false;
    148         } else {
    149             return pool.put(b);
    150         }
    151     }
    152 
    153     /**
    154      * Empty the pool, recycling all the bitmaps currently in it.
    155      */
    156     public void clear() {
    157         for (SparseArrayBitmapPool p : mPools) {
    158             p.clear();
    159         }
    160     }
    161 }
    162