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.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