• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.bumptech.glide.load.engine.bitmap_recycle;
2 
3 import android.annotation.TargetApi;
4 import android.graphics.Bitmap;
5 
6 import java.util.TreeMap;
7 
8 /**
9  * A strategy for reusing bitmaps that relies on {@link Bitmap#reconfigure(int, int, Bitmap.Config)}. Requires KitKat
10  * (API 19) or higher.
11  */
12 @TargetApi(19)
13 class SizeStrategy implements LruPoolStrategy {
14     private static final int MAX_SIZE_MULTIPLE = 4;
15     private final KeyPool keyPool = new KeyPool();
16     private final GroupedLinkedMap<Key, Bitmap> groupedMap = new GroupedLinkedMap<Key, Bitmap>();
17     private final TreeMap<Integer, Integer> sortedSizes = new TreeMap<Integer, Integer>();
18 
19     @Override
put(Bitmap bitmap)20     public void put(Bitmap bitmap) {
21         final Key key = keyPool.get(bitmap.getAllocationByteCount());
22 
23         groupedMap.put(key, bitmap);
24 
25         Integer current = sortedSizes.get(key.size);
26         sortedSizes.put(key.size, current == null ? 1 : current + 1);
27     }
28 
29     @Override
get(int width, int height, Bitmap.Config config)30     public Bitmap get(int width, int height, Bitmap.Config config) {
31         final int size = getSize(width, height, config);
32         Key key = keyPool.get(size);
33 
34         Integer possibleSize = sortedSizes.ceilingKey(size);
35         if (possibleSize != null && possibleSize != size && possibleSize <= size * MAX_SIZE_MULTIPLE) {
36             keyPool.offer(key);
37             key = keyPool.get(possibleSize);
38         }
39 
40         // 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
41         final Bitmap result = groupedMap.get(key);
42         if (result != null) {
43             result.reconfigure(width, height, config);
44             decrementBitmapOfSize(possibleSize);
45         }
46 
47         return result;
48     }
49 
50     @Override
removeLast()51     public Bitmap removeLast() {
52         Bitmap removed = groupedMap.removeLast();
53         if (removed != null) {
54             final int removedSize = removed.getAllocationByteCount();
55             decrementBitmapOfSize(removedSize);
56         }
57         return removed;
58     }
59 
decrementBitmapOfSize(Integer size)60     private void decrementBitmapOfSize(Integer size) {
61         Integer current = sortedSizes.get(size);
62         if (current == 1) {
63             sortedSizes.remove(size);
64         } else {
65             sortedSizes.put(size, current - 1);
66         }
67     }
68 
69     @Override
logBitmap(Bitmap bitmap)70     public String logBitmap(Bitmap bitmap) {
71         return getBitmapString(bitmap);
72     }
73 
74     @Override
logBitmap(int width, int height, Bitmap.Config config)75     public String logBitmap(int width, int height, Bitmap.Config config) {
76         return getBitmapString(getSize(width, height, config));
77     }
78 
79     @Override
getSize(Bitmap bitmap)80     public int getSize(Bitmap bitmap) {
81         return bitmap.getAllocationByteCount();
82     }
83 
84     @Override
toString()85     public String toString() {
86         String result = "SizeStrategy:\n  " + groupedMap + "\n  SortedSizes( ";
87         boolean hadAtLeastOneKey = false;
88         for (Integer size : sortedSizes.keySet()) {
89             hadAtLeastOneKey = true;
90             result += "{" + getBitmapString(size) + ":" + sortedSizes.get(size) + "}, ";
91         }
92         if (hadAtLeastOneKey) {
93             result = result.substring(0, result.length() - 2);
94         }
95         return result + " )";
96     }
97 
getBitmapString(Bitmap bitmap)98     private static String getBitmapString(Bitmap bitmap) {
99         return getBitmapString(bitmap.getAllocationByteCount());
100     }
101 
getBitmapString(int size)102     private static String getBitmapString(int size) {
103         return "[" + size + "]";
104     }
105 
getSize(int width, int height, Bitmap.Config config)106     private static int getSize(int width, int height, Bitmap.Config config) {
107         return width * height * getBytesPerPixel(config);
108     }
109 
getBytesPerPixel(Bitmap.Config config)110     private static int getBytesPerPixel(Bitmap.Config config) {
111         switch (config) {
112             case ARGB_8888:
113                 return 4;
114             case RGB_565:
115                 return 2;
116             case ARGB_4444:
117                 return 2;
118             case ALPHA_8:
119                 return 1;
120             default:
121                 // We only use this to calculate sizes to get, so choosing 4 bytes per pixel is conservative and
122                 // probably forces us to get a larger bitmap than we really need. Since we can't tell for sure, probably
123                 // better safe than sorry.
124                 return 4;
125         }
126     }
127 
128     private static class KeyPool extends BaseKeyPool<Key> {
129 
get(int size)130         public Key get(int size) {
131             Key result = get();
132             result.init(size);
133             return result;
134         }
135 
136         @Override
create()137         protected Key create() {
138             return new Key(this);
139         }
140     }
141 
142     private static class Key implements Poolable {
143         private final KeyPool pool;
144         private int size;
145 
Key(KeyPool pool)146         private Key(KeyPool pool) {
147             this.pool = pool;
148         }
149 
init(int size)150         public void init(int size) {
151             this.size = size;
152         }
153 
154         @Override
equals(Object o)155         public boolean equals(Object o) {
156             if (this == o) return true;
157             if (o == null || getClass() != o.getClass()) return false;
158 
159             Key key = (Key) o;
160 
161             return size == key.size;
162         }
163 
164         @Override
hashCode()165         public int hashCode() {
166             return size;
167         }
168 
169         @Override
toString()170         public String toString() {
171             return getBitmapString(size);
172         }
173 
174         @Override
offer()175         public void offer() {
176             pool.offer(this);
177         }
178     }
179 }
180