/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.photos.data;
import android.graphics.Bitmap;
import android.util.SparseArray;
import android.util.Pools.Pool;
public class SparseArrayBitmapPool {
private static final int BITMAPS_TO_KEEP_AFTER_UNNEEDED_HINT = 4;
private int mCapacityBytes;
private SparseArray<Node> mStore = new SparseArray<Node>();
private int mSizeBytes = 0;
private Pool<Node> mNodePool;
private Node mPoolNodesHead = null;
private Node mPoolNodesTail = null;
protected static class Node {
Bitmap bitmap;
Node prevInBucket;
Node nextInBucket;
Node nextInPool;
Node prevInPool;
}
public SparseArrayBitmapPool(int capacityBytes, Pool<Node> nodePool) {
mCapacityBytes = capacityBytes;
mNodePool = nodePool;
}
public synchronized void setCapacity(int capacityBytes) {
mCapacityBytes = capacityBytes;
freeUpCapacity(0);
}
private void freeUpCapacity(int bytesNeeded) {
int targetSize = mCapacityBytes - bytesNeeded;
while (mPoolNodesTail != null && mSizeBytes > targetSize) {
unlinkAndRecycleNode(mPoolNodesTail, true);
}
}
private void unlinkAndRecycleNode(Node n, boolean recycleBitmap) {
// Remove the node from its spot in its bucket
if (n.prevInBucket != null) {
n.prevInBucket.nextInBucket = n.nextInBucket;
} else {
mStore.put(n.bitmap.getWidth(), n.nextInBucket);
}
if (n.nextInBucket != null) {
n.nextInBucket.prevInBucket = n.prevInBucket;
}
// Remove the node from its spot in the list of pool nodes
if (n.prevInPool != null) {
n.prevInPool.nextInPool = n.nextInPool;
} else {
mPoolNodesHead = n.nextInPool;
}
if (n.nextInPool != null) {
n.nextInPool.prevInPool = n.prevInPool;
} else {
mPoolNodesTail = n.prevInPool;
}
// Recycle the node
n.nextInBucket = null;
n.nextInPool = null;
n.prevInBucket = null;
n.prevInPool = null;
mSizeBytes -= n.bitmap.getByteCount();
if (recycleBitmap) n.bitmap.recycle();
n.bitmap = null;
mNodePool.release(n);
}
public synchronized int getCapacity() {
return mCapacityBytes;
}
public synchronized int getSize() {
return mSizeBytes;
}
public synchronized Bitmap get(int width, int height) {
Node cur = mStore.get(width);
while (cur != null) {
if (cur.bitmap.getHeight() == height) {
Bitmap b = cur.bitmap;
unlinkAndRecycleNode(cur, false);
return b;
}
cur = cur.nextInBucket;
}
return null;
}
public synchronized boolean put(Bitmap b) {
if (b == null) {
return false;
}
int bytes = b.getByteCount();
freeUpCapacity(bytes);
Node newNode = mNodePool.acquire();
if (newNode == null) {
newNode = new Node();
}
newNode.bitmap = b;
newNode.prevInBucket = null;
newNode.prevInPool = null;
newNode.nextInPool = mPoolNodesHead;
mPoolNodesHead = newNode;
int key = b.getWidth();
newNode.nextInBucket = mStore.get(key);
if (newNode.nextInBucket != null) {
newNode.nextInBucket.prevInBucket = newNode;
}
mStore.put(key, newNode);
if (newNode.nextInPool == null) {
mPoolNodesTail = newNode;
} else {
newNode.nextInPool.prevInPool = newNode;
}
mSizeBytes += bytes;
return true;
}
public synchronized void clear() {
freeUpCapacity(mCapacityBytes);
}
}