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.gallery3d.filtershow.cache;
18 
19 import android.graphics.Bitmap;
20 import android.graphics.Canvas;
21 import android.util.Log;
22 import com.android.gallery3d.filtershow.pipeline.Buffer;
23 import com.android.gallery3d.filtershow.pipeline.CacheProcessing;
24 
25 import java.lang.ref.WeakReference;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 
29 public class BitmapCache {
30     private static final String LOGTAG = "BitmapCache";
31     private HashMap<Long, ArrayList<WeakReference<Bitmap>>>
32             mBitmapCache = new HashMap<Long, ArrayList<WeakReference<Bitmap>>>();
33     private final int mMaxItemsPerKey = 4;
34 
35     private static final boolean DEBUG = false;
36     private CacheProcessing mCacheProcessing;
37 
38     public final static int PREVIEW_CACHE = 1;
39     public final static int NEW_LOOK = 2;
40     public final static int ICON = 3;
41     public final static int FILTERS = 4;
42     public final static int GEOMETRY = 5;
43     public final static int HIGHRES = 6;
44     public final static int UTIL_GEOMETRY = 7;
45     public final static int RENDERING_REQUEST = 8;
46     public final static int REGION = 9;
47     public final static int TINY_PLANET = 10;
48     public final static int PREVIEW_CACHE_NO_FILTERS = 11;
49     public final static int PREVIEW_CACHE_NO_ROOT = 12;
50     public static final int PREVIEW_CACHE_NO_APPLY = 13;
51     public final static int TRACKING_COUNT = 14;
52     private int[] mTracking = new int[TRACKING_COUNT];
53 
54     class BitmapTracking {
55         Bitmap bitmap;
56         int type;
57     }
58 
59     private ArrayList<BitmapTracking> mBitmapTracking = new ArrayList<BitmapTracking>();
60 
track(Bitmap bitmap, int type)61     private void track(Bitmap bitmap, int type) {
62         for (int i = 0; i < mBitmapTracking.size(); i++) {
63             BitmapTracking tracking = mBitmapTracking.get(i);
64             if (tracking.bitmap == bitmap) {
65                 Log.e(LOGTAG, "giving a bitmap already given!!!");
66             }
67         }
68         BitmapTracking tracking = new BitmapTracking();
69         tracking.bitmap = bitmap;
70         tracking.type = type;
71         mBitmapTracking.add(tracking);
72         mTracking[tracking.type] ++;
73     }
74 
untrack(Bitmap bitmap)75     private void untrack(Bitmap bitmap) {
76         for (int i = 0; i < mBitmapTracking.size(); i++) {
77             BitmapTracking tracking = mBitmapTracking.get(i);
78             if (tracking.bitmap == bitmap) {
79                 mTracking[tracking.type] --;
80                 mBitmapTracking.remove(i);
81                 return;
82             }
83         }
84     }
85 
getTrackingName(int i)86     public String getTrackingName(int i) {
87         switch (i) {
88             case PREVIEW_CACHE: return "PREVIEW_CACHE";
89             case PREVIEW_CACHE_NO_FILTERS: return "PREVIEW_CACHE_NO_FILTERS";
90             case PREVIEW_CACHE_NO_ROOT: return "PREVIEW_CACHE_NO_ROOT";
91             case PREVIEW_CACHE_NO_APPLY: return "PREVIEW_CACHE_NO_APPLY";
92             case NEW_LOOK: return "NEW_LOOK";
93             case ICON: return "ICON";
94             case FILTERS: return "FILTERS";
95             case GEOMETRY: return "GEOMETRY";
96             case HIGHRES: return "HIGHRES";
97             case UTIL_GEOMETRY: return "UTIL_GEOMETRY";
98             case RENDERING_REQUEST: return "RENDERING_REQUEST";
99             case REGION: return "REGION";
100             case TINY_PLANET: return "TINY_PLANET";
101         }
102         return "UNKNOWN";
103     }
104 
showBitmapCounts()105     public void showBitmapCounts() {
106         if (!DEBUG) {
107             return;
108         }
109         Log.v(LOGTAG, "\n--- showBitmap --- ");
110         for (int i = 0; i < TRACKING_COUNT; i++) {
111             if (mTracking[i] != 0) {
112                 Log.v(LOGTAG, getTrackingName(i) + " => " + mTracking[i]);
113             }
114         }
115     }
116 
setCacheProcessing(CacheProcessing cache)117     public void setCacheProcessing(CacheProcessing cache) {
118         mCacheProcessing = cache;
119     }
120 
cache(Buffer buffer)121     public void cache(Buffer buffer) {
122         if (buffer == null) {
123             return;
124         }
125         Bitmap bitmap = buffer.getBitmap();
126         cache(bitmap);
127     }
128 
cache(Bitmap bitmap)129     public synchronized boolean cache(Bitmap bitmap) {
130         if (bitmap == null) {
131             return true;
132         }
133         if (mCacheProcessing != null && mCacheProcessing.contains(bitmap)) {
134             Log.e(LOGTAG, "Trying to cache a bitmap still used in the pipeline");
135             return false;
136         }
137         if (DEBUG) {
138             untrack(bitmap);
139         }
140         if (!bitmap.isMutable()) {
141             Log.e(LOGTAG, "Trying to cache a non mutable bitmap");
142             return true;
143         }
144         Long key = calcKey(bitmap.getWidth(), bitmap.getHeight());
145         ArrayList<WeakReference<Bitmap>> list = mBitmapCache.get(key);
146         if (list == null) {
147             list = new ArrayList<WeakReference<Bitmap>>();
148             mBitmapCache.put(key, list);
149         }
150         int i = 0;
151         while (i < list.size()) {
152             if (list.get(i).get() == null) {
153                 list.remove(i);
154             } else {
155                 i++;
156             }
157         }
158         for (i = 0; i < list.size(); i++) {
159             if (list.get(i).get() == null) {
160                 list.remove(i);
161             }
162         }
163         if (list.size() < mMaxItemsPerKey) {
164             for (i = 0; i < list.size(); i++) {
165                 WeakReference<Bitmap> ref = list.get(i);
166                 if (ref.get() == bitmap) {
167                     return true; // bitmap already in the cache
168                 }
169             }
170             list.add(new WeakReference<Bitmap>(bitmap));
171         }
172         return true;
173     }
174 
getBitmap(int w, int h, int type)175     public synchronized Bitmap getBitmap(int w, int h, int type) {
176         Long key = calcKey(w, h);
177         WeakReference<Bitmap> ref = null;
178         ArrayList<WeakReference<Bitmap>> list = mBitmapCache.get(key);
179         if (list != null && list.size() > 0) {
180             ref = list.remove(0);
181             if (list.size() == 0) {
182                 mBitmapCache.remove(key);
183             }
184         }
185         Bitmap bitmap = null;
186         if (ref != null) {
187             bitmap = ref.get();
188         }
189         if (bitmap == null
190                 || bitmap.getWidth() != w
191                 || bitmap.getHeight() != h) {
192             bitmap = Bitmap.createBitmap(
193                     w, h, Bitmap.Config.ARGB_8888);
194             showBitmapCounts();
195         }
196 
197         if (DEBUG) {
198             track(bitmap, type);
199             if (mCacheProcessing != null && mCacheProcessing.contains(bitmap)) {
200                 Log.e(LOGTAG, "Trying to give a bitmap used in the pipeline");
201             }
202         }
203         return bitmap;
204     }
205 
getBitmapCopy(Bitmap source, int type)206     public synchronized Bitmap getBitmapCopy(Bitmap source, int type) {
207         Bitmap bitmap = getBitmap(source.getWidth(), source.getHeight(), type);
208         Canvas canvas = new Canvas(bitmap);
209         canvas.drawBitmap(source, 0, 0, null);
210         return bitmap;
211     }
212 
calcKey(long w, long h)213     private Long calcKey(long w, long h) {
214         return (w << 32) | h;
215     }
216 }
217