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