• 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 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
put(Bitmap bitmap)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
get(int width, int height, Bitmap.Config config)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
removeLast()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 
decrementBitmapOfSize(Integer size)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
logBitmap(Bitmap bitmap)74     public String logBitmap(Bitmap bitmap) {
75         return getBitmapString(bitmap);
76     }
77 
78     @Override
logBitmap(int width, int height, Bitmap.Config config)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
getSize(Bitmap bitmap)85     public int getSize(Bitmap bitmap) {
86         return Util.getBitmapByteSize(bitmap);
87     }
88 
89     @Override
toString()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
toString()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 
getBitmapString(Bitmap bitmap)114     private static String getBitmapString(Bitmap bitmap) {
115         int size = Util.getBitmapByteSize(bitmap);
116         return getBitmapString(size);
117     }
118 
getBitmapString(int size)119     private static String getBitmapString(int size) {
120         return "[" + size + "]";
121     }
122 
123     // Visible for testing.
124     static class KeyPool extends BaseKeyPool<Key> {
125 
get(int size)126         public Key get(int size) {
127             Key result = get();
128             result.init(size);
129             return result;
130         }
131 
132         @Override
create()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 
Key(KeyPool pool)143         Key(KeyPool pool) {
144             this.pool = pool;
145         }
146 
init(int size)147         public void init(int size) {
148             this.size = size;
149         }
150 
151         @Override
equals(Object o)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
hashCode()161         public int hashCode() {
162             return size;
163         }
164 
165         @Override
toString()166         public String toString() {
167             return getBitmapString(size);
168         }
169 
170         @Override
offer()171         public void offer() {
172             pool.offer(this);
173         }
174     }
175 }
176