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.util.SparseArray; 21 22 import android.util.Pools.Pool; 23 24 public class SparseArrayBitmapPool { 25 26 private static final int BITMAPS_TO_KEEP_AFTER_UNNEEDED_HINT = 4; 27 private int mCapacityBytes; 28 private SparseArray<Node> mStore = new SparseArray<Node>(); 29 private int mSizeBytes = 0; 30 31 private Pool<Node> mNodePool; 32 private Node mPoolNodesHead = null; 33 private Node mPoolNodesTail = null; 34 35 protected static class Node { 36 Bitmap bitmap; 37 Node prevInBucket; 38 Node nextInBucket; 39 Node nextInPool; 40 Node prevInPool; 41 } 42 43 public SparseArrayBitmapPool(int capacityBytes, Pool<Node> nodePool) { 44 mCapacityBytes = capacityBytes; 45 mNodePool = nodePool; 46 } 47 48 public synchronized void setCapacity(int capacityBytes) { 49 mCapacityBytes = capacityBytes; 50 freeUpCapacity(0); 51 } 52 53 private void freeUpCapacity(int bytesNeeded) { 54 int targetSize = mCapacityBytes - bytesNeeded; 55 while (mPoolNodesTail != null && mSizeBytes > targetSize) { 56 unlinkAndRecycleNode(mPoolNodesTail, true); 57 } 58 } 59 60 private void unlinkAndRecycleNode(Node n, boolean recycleBitmap) { 61 // Remove the node from its spot in its bucket 62 if (n.prevInBucket != null) { 63 n.prevInBucket.nextInBucket = n.nextInBucket; 64 } else { 65 mStore.put(n.bitmap.getWidth(), n.nextInBucket); 66 } 67 if (n.nextInBucket != null) { 68 n.nextInBucket.prevInBucket = n.prevInBucket; 69 } 70 71 // Remove the node from its spot in the list of pool nodes 72 if (n.prevInPool != null) { 73 n.prevInPool.nextInPool = n.nextInPool; 74 } else { 75 mPoolNodesHead = n.nextInPool; 76 } 77 if (n.nextInPool != null) { 78 n.nextInPool.prevInPool = n.prevInPool; 79 } else { 80 mPoolNodesTail = n.prevInPool; 81 } 82 83 // Recycle the node 84 n.nextInBucket = null; 85 n.nextInPool = null; 86 n.prevInBucket = null; 87 n.prevInPool = null; 88 mSizeBytes -= n.bitmap.getByteCount(); 89 if (recycleBitmap) n.bitmap.recycle(); 90 n.bitmap = null; 91 mNodePool.release(n); 92 } 93 94 public synchronized int getCapacity() { 95 return mCapacityBytes; 96 } 97 98 public synchronized int getSize() { 99 return mSizeBytes; 100 } 101 102 public synchronized Bitmap get(int width, int height) { 103 Node cur = mStore.get(width); 104 while (cur != null) { 105 if (cur.bitmap.getHeight() == height) { 106 Bitmap b = cur.bitmap; 107 unlinkAndRecycleNode(cur, false); 108 return b; 109 } 110 cur = cur.nextInBucket; 111 } 112 return null; 113 } 114 115 public synchronized boolean put(Bitmap b) { 116 if (b == null) { 117 return false; 118 } 119 int bytes = b.getByteCount(); 120 freeUpCapacity(bytes); 121 Node newNode = mNodePool.acquire(); 122 if (newNode == null) { 123 newNode = new Node(); 124 } 125 newNode.bitmap = b; 126 newNode.prevInBucket = null; 127 newNode.prevInPool = null; 128 newNode.nextInPool = mPoolNodesHead; 129 mPoolNodesHead = newNode; 130 int key = b.getWidth(); 131 newNode.nextInBucket = mStore.get(key); 132 if (newNode.nextInBucket != null) { 133 newNode.nextInBucket.prevInBucket = newNode; 134 } 135 mStore.put(key, newNode); 136 if (newNode.nextInPool == null) { 137 mPoolNodesTail = newNode; 138 } else { 139 newNode.nextInPool.prevInPool = newNode; 140 } 141 mSizeBytes += bytes; 142 return true; 143 } 144 145 public synchronized void clear() { 146 freeUpCapacity(mCapacityBytes); 147 } 148 } 149