Home | History | Annotate | Download | only in bitmap_recycle
      1 package com.bumptech.glide.load.engine.bitmap_recycle;
      2 
      3 import android.annotation.TargetApi;
      4 import android.graphics.Bitmap;
      5 import android.os.Build;
      6 
      7 import com.bumptech.glide.util.Util;
      8 
      9 import java.util.TreeMap;
     10 
     11 /**
     12  * A strategy for reusing bitmaps that relies on {@link Bitmap#reconfigure(int, int, Bitmap.Config)}.
     13  * Requires {@link Build.VERSION_CODES#KITKAT KitKat} (API {@value Build.VERSION_CODES#KITKAT}) or higher.
     14  */
     15 @TargetApi(Build.VERSION_CODES.KITKAT)
     16 class SizeStrategy implements LruPoolStrategy {
     17     private static final int MAX_SIZE_MULTIPLE = 8;
     18     private final KeyPool keyPool = new KeyPool();
     19     private final GroupedLinkedMap<Key, Bitmap> groupedMap = new GroupedLinkedMap<Key, Bitmap>();
     20     private final TreeMap<Integer, Integer> sortedSizes = new PrettyPrintTreeMap<Integer, Integer>();
     21 
     22     @Override
     23     public void put(Bitmap bitmap) {
     24         int size = Util.getBitmapByteSize(bitmap);
     25         final Key key = keyPool.get(size);
     26 
     27         groupedMap.put(key, bitmap);
     28 
     29         Integer current = sortedSizes.get(key.size);
     30         sortedSizes.put(key.size, current == null ? 1 : current + 1);
     31     }
     32 
     33     @Override
     34     public Bitmap get(int width, int height, Bitmap.Config config) {
     35         final int size = Util.getBitmapByteSize(width, height, config);
     36         Key key = keyPool.get(size);
     37 
     38         Integer possibleSize = sortedSizes.ceilingKey(size);
     39         if (possibleSize != null && possibleSize != size && possibleSize <= size * MAX_SIZE_MULTIPLE) {
     40             keyPool.offer(key);
     41             key = keyPool.get(possibleSize);
     42         }
     43 
     44         // Do a get even if we know we don't have a bitmap so that the key moves to the front in the lru pool
     45         final Bitmap result = groupedMap.get(key);
     46         if (result != null) {
     47             result.reconfigure(width, height, config);
     48             decrementBitmapOfSize(possibleSize);
     49         }
     50 
     51         return result;
     52     }
     53 
     54     @Override
     55     public Bitmap removeLast() {
     56         Bitmap removed = groupedMap.removeLast();
     57         if (removed != null) {
     58             final int removedSize = Util.getBitmapByteSize(removed);
     59             decrementBitmapOfSize(removedSize);
     60         }
     61         return removed;
     62     }
     63 
     64     private void decrementBitmapOfSize(Integer size) {
     65         Integer current = sortedSizes.get(size);
     66         if (current == 1) {
     67             sortedSizes.remove(size);
     68         } else {
     69             sortedSizes.put(size, current - 1);
     70         }
     71     }
     72 
     73     @Override
     74     public String logBitmap(Bitmap bitmap) {
     75         return getBitmapString(bitmap);
     76     }
     77 
     78     @Override
     79     public String logBitmap(int width, int height, Bitmap.Config config) {
     80         int size = Util.getBitmapByteSize(width, height, config);
     81         return getBitmapString(size);
     82     }
     83 
     84     @Override
     85     public int getSize(Bitmap bitmap) {
     86         return Util.getBitmapByteSize(bitmap);
     87     }
     88 
     89     @Override
     90     public String toString() {
     91         return "SizeStrategy:\n  "
     92                 + groupedMap + "\n"
     93                 + "  SortedSizes" + sortedSizes;
     94     }
     95 
     96     private static class PrettyPrintTreeMap<K, V> extends TreeMap<K, V> {
     97         @Override
     98         public String toString() {
     99             StringBuilder sb = new StringBuilder();
    100             sb.append("( ");
    101             for (Entry<K, V> entry : entrySet()) {
    102                 sb.append('{').append(entry.getKey()).append(':').append(entry.getValue()).append("}, ");
    103             }
    104             final String result;
    105             if (!isEmpty()) {
    106                 result = sb.substring(0, sb.length() - 2);
    107             } else {
    108                 result = sb.toString();
    109             }
    110             return result + " )";
    111         }
    112     }
    113 
    114     private static String getBitmapString(Bitmap bitmap) {
    115         int size = Util.getBitmapByteSize(bitmap);
    116         return getBitmapString(size);
    117     }
    118 
    119     private static String getBitmapString(int size) {
    120         return "[" + size + "]";
    121     }
    122 
    123     // Visible for testing.
    124     static class KeyPool extends BaseKeyPool<Key> {
    125 
    126         public Key get(int size) {
    127             Key result = get();
    128             result.init(size);
    129             return result;
    130         }
    131 
    132         @Override
    133         protected Key create() {
    134             return new Key(this);
    135         }
    136     }
    137 
    138     // Visible for testing.
    139     static final class Key implements Poolable {
    140         private final KeyPool pool;
    141         private int size;
    142 
    143         Key(KeyPool pool) {
    144             this.pool = pool;
    145         }
    146 
    147         public void init(int size) {
    148             this.size = size;
    149         }
    150 
    151         @Override
    152         public boolean equals(Object o) {
    153             if (o instanceof Key) {
    154                 Key other = (Key) o;
    155                 return size == other.size;
    156             }
    157             return false;
    158         }
    159 
    160         @Override
    161         public int hashCode() {
    162             return size;
    163         }
    164 
    165         @Override
    166         public String toString() {
    167             return getBitmapString(size);
    168         }
    169 
    170         @Override
    171         public void offer() {
    172             pool.offer(this);
    173         }
    174     }
    175 }
    176