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.pipeline;
18 
19 import android.graphics.Bitmap;
20 import android.util.Log;
21 
22 import com.android.gallery3d.filtershow.cache.BitmapCache;
23 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
24 import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils;
25 
26 import java.util.ArrayList;
27 import java.util.Vector;
28 
29 public class CacheProcessing {
30     private static final String LOGTAG = "CacheProcessing";
31     private static final boolean DEBUG = false;
32     private static final boolean NO_CACHING = false;
33     private Vector<CacheStep> mSteps = new Vector<CacheStep>();
34 
35     static class CacheStep {
36         ArrayList<FilterRepresentation> representations;
37         Bitmap cache;
38 
CacheStep()39         public CacheStep() {
40             representations = new ArrayList<FilterRepresentation>();
41         }
42 
add(FilterRepresentation representation)43         public void add(FilterRepresentation representation) {
44             representations.add(representation);
45         }
46 
canMergeWith(FilterRepresentation representation)47         public boolean canMergeWith(FilterRepresentation representation) {
48             for (FilterRepresentation rep : representations) {
49                 if (!rep.canMergeWith(representation)) {
50                     return false;
51                 }
52             }
53             return true;
54         }
55 
equals(CacheStep step)56         public boolean equals(CacheStep step) {
57             if (representations.size() != step.representations.size()) {
58                 return false;
59             }
60             for (int i = 0; i < representations.size(); i++) {
61                 FilterRepresentation r1 = representations.get(i);
62                 FilterRepresentation r2 = step.representations.get(i);
63                 if (!r1.equals(r2)) {
64                     return false;
65                 }
66             }
67             return true;
68         }
69 
buildSteps(Vector<FilterRepresentation> filters)70         public static Vector<CacheStep> buildSteps(Vector<FilterRepresentation> filters) {
71             Vector<CacheStep> steps = new Vector<CacheStep>();
72             CacheStep step = new CacheStep();
73             for (int i = 0; i < filters.size(); i++) {
74                 FilterRepresentation representation = filters.elementAt(i);
75                 if (step.canMergeWith(representation)) {
76                     step.add(representation.copy());
77                 } else {
78                     steps.add(step);
79                     step = new CacheStep();
80                     step.add(representation.copy());
81                 }
82             }
83             steps.add(step);
84             return steps;
85         }
86 
getName()87         public String getName() {
88             if (representations.size() > 0) {
89                 return representations.get(0).getName();
90             }
91             return "EMPTY";
92         }
93 
apply(FilterEnvironment environment, Bitmap cacheBitmap)94         public Bitmap apply(FilterEnvironment environment, Bitmap cacheBitmap) {
95             boolean onlyGeometry = true;
96             Bitmap source = cacheBitmap;
97             for (FilterRepresentation representation : representations) {
98                 if (representation.getFilterType() != FilterRepresentation.TYPE_GEOMETRY) {
99                     onlyGeometry = false;
100                     break;
101                 }
102             }
103             if (onlyGeometry) {
104                 ArrayList<FilterRepresentation> geometry = new ArrayList<FilterRepresentation>();
105                 for (FilterRepresentation representation : representations) {
106                     geometry.add(representation);
107                 }
108                 if (DEBUG) {
109                     Log.v(LOGTAG, "Apply geometry to bitmap " + cacheBitmap);
110                 }
111                 cacheBitmap = GeometryMathUtils.applyGeometryRepresentations(geometry, cacheBitmap);
112             } else {
113                 for (FilterRepresentation representation : representations) {
114                     if (DEBUG) {
115                         Log.v(LOGTAG, "Apply " + representation.getSerializationName()
116                                 + " to bitmap " + cacheBitmap);
117                     }
118                     cacheBitmap = environment.applyRepresentation(representation, cacheBitmap);
119                 }
120             }
121             if (cacheBitmap != source) {
122                 environment.cache(source);
123             }
124             if (DEBUG) {
125                 Log.v(LOGTAG, "Apply returns bitmap " + cacheBitmap);
126             }
127             return cacheBitmap;
128         }
129     }
130 
process(Bitmap originalBitmap, Vector<FilterRepresentation> filters, FilterEnvironment environment)131     public Bitmap process(Bitmap originalBitmap,
132                           Vector<FilterRepresentation> filters,
133                           FilterEnvironment environment) {
134 
135         if (filters.size() == 0) {
136             return environment.getBitmapCopy(originalBitmap, BitmapCache.PREVIEW_CACHE_NO_FILTERS);
137         }
138 
139         environment.getBimapCache().setCacheProcessing(this);
140 
141         if (DEBUG) {
142             displayFilters(filters);
143         }
144         Vector<CacheStep> steps = CacheStep.buildSteps(filters);
145         // New set of filters, let's clear the cache and rebuild it.
146         if (steps.size() != mSteps.size()) {
147             mSteps = steps;
148         }
149 
150         if (DEBUG) {
151             displaySteps(mSteps);
152         }
153 
154         // First, let's find how similar we are in our cache
155         // compared to the current list of filters
156         int similarUpToIndex = -1;
157         boolean similar = true;
158         for (int i = 0; i < steps.size(); i++) {
159             CacheStep newStep = steps.elementAt(i);
160             CacheStep cacheStep = mSteps.elementAt(i);
161             if (similar) {
162                 similar = newStep.equals(cacheStep);
163             }
164             if (similar) {
165                 similarUpToIndex = i;
166             } else {
167                 mSteps.remove(i);
168                 mSteps.insertElementAt(newStep, i);
169                 environment.cache(cacheStep.cache);
170             }
171         }
172         if (DEBUG) {
173             Log.v(LOGTAG, "similar up to index " + similarUpToIndex);
174         }
175 
176         // Now, let's get the earliest cached result in our pipeline
177         Bitmap cacheBitmap = null;
178         int findBaseImageIndex = similarUpToIndex;
179         if (findBaseImageIndex > -1) {
180             while (findBaseImageIndex > 0
181                     && mSteps.elementAt(findBaseImageIndex).cache == null) {
182                 findBaseImageIndex--;
183             }
184             cacheBitmap = mSteps.elementAt(findBaseImageIndex).cache;
185         }
186 
187         if (DEBUG) {
188             Log.v(LOGTAG, "found baseImageIndex: " + findBaseImageIndex + " max is "
189                     + mSteps.size() + " cacheBitmap: " + cacheBitmap);
190         }
191 
192         if (NO_CACHING) {
193             cacheBitmap = environment.getBitmapCopy(originalBitmap,
194                     BitmapCache.PREVIEW_CACHE_NO_ROOT);
195             for (int i = 0; i < mSteps.size(); i++) {
196                 CacheStep step = mSteps.elementAt(i);
197                 Bitmap prev = cacheBitmap;
198                 cacheBitmap = step.apply(environment, cacheBitmap);
199                 if (prev != cacheBitmap) {
200                     environment.cache(prev);
201                 }
202             }
203             return cacheBitmap;
204         }
205 
206         Bitmap originalCopy = null;
207         int lastPositionCached = -1;
208         for (int i = findBaseImageIndex; i < mSteps.size(); i++) {
209             if (i == -1 || cacheBitmap == null) {
210                 cacheBitmap = environment.getBitmapCopy(originalBitmap,
211                         BitmapCache.PREVIEW_CACHE_NO_ROOT);
212                 originalCopy = cacheBitmap;
213                 if (DEBUG) {
214                     Log.v(LOGTAG, "i: " + i + " cacheBitmap: " + cacheBitmap + " w: "
215                             + cacheBitmap.getWidth() + " h: " + cacheBitmap.getHeight()
216                             + " got from original Bitmap: " + originalBitmap + " w: "
217                             + originalBitmap.getWidth() + " h: " + originalBitmap.getHeight());
218                 }
219                 if (i == -1) {
220                     continue;
221                 }
222             }
223             CacheStep step = mSteps.elementAt(i);
224             if (step.cache == null) {
225                 if (DEBUG) {
226                     Log.v(LOGTAG, "i: " + i + " get new copy for cacheBitmap "
227                             + cacheBitmap + " apply...");
228                 }
229                 cacheBitmap = environment.getBitmapCopy(cacheBitmap, BitmapCache.PREVIEW_CACHE);
230                 cacheBitmap = step.apply(environment, cacheBitmap);
231                 step.cache = cacheBitmap;
232                 lastPositionCached = i;
233             }
234         }
235         environment.cache(originalCopy);
236 
237         if (DEBUG) {
238             Log.v(LOGTAG, "now let's cleanup the cache...");
239             displayNbBitmapsInCache();
240         }
241 
242         // Let's see if we can cleanup the cache for unused bitmaps
243         for (int i = 0; i < similarUpToIndex; i++) {
244             CacheStep currentStep = mSteps.elementAt(i);
245             Bitmap bitmap = currentStep.cache;
246             currentStep.cache = null;
247             environment.cache(bitmap);
248         }
249 
250         if (DEBUG) {
251             Log.v(LOGTAG, "cleanup done...");
252             displayNbBitmapsInCache();
253         }
254         if (lastPositionCached != -1) {
255             // The last element will never be reused, remove it from the cache.
256             mSteps.elementAt(lastPositionCached).cache = null;
257         }
258         if (contains(cacheBitmap)) {
259             return environment.getBitmapCopy(cacheBitmap, BitmapCache.PREVIEW_CACHE_NO_APPLY);
260         }
261         return cacheBitmap;
262     }
263 
contains(Bitmap bitmap)264     public boolean contains(Bitmap bitmap) {
265         for (int i = 0; i < mSteps.size(); i++) {
266             if (mSteps.elementAt(i).cache == bitmap) {
267                 return true;
268             }
269         }
270         return false;
271     }
272 
displayFilters(Vector<FilterRepresentation> filters)273     private void displayFilters(Vector<FilterRepresentation> filters) {
274         Log.v(LOGTAG, "------>>> Filters received");
275         for (int i = 0; i < filters.size(); i++) {
276             FilterRepresentation filter = filters.elementAt(i);
277             Log.v(LOGTAG, "[" + i + "] - " + filter.getName());
278         }
279         Log.v(LOGTAG, "<<<------");
280     }
281 
displaySteps(Vector<CacheStep> filters)282     private void displaySteps(Vector<CacheStep> filters) {
283         Log.v(LOGTAG, "------>>>");
284         for (int i = 0; i < filters.size(); i++) {
285             CacheStep newStep = filters.elementAt(i);
286             CacheStep step = mSteps.elementAt(i);
287             boolean similar = step.equals(newStep);
288             Log.v(LOGTAG, "[" + i + "] - " + step.getName()
289                     + " similar rep ? " + (similar ? "YES" : "NO")
290                     + " -- bitmap: " + step.cache);
291         }
292         Log.v(LOGTAG, "<<<------");
293     }
294 
displayNbBitmapsInCache()295     private void displayNbBitmapsInCache() {
296         int nbBitmapsCached = 0;
297         for (int i = 0; i < mSteps.size(); i++) {
298             CacheStep step = mSteps.elementAt(i);
299             if (step.cache != null) {
300                 nbBitmapsCached++;
301             }
302         }
303         Log.v(LOGTAG, "nb bitmaps in cache: " + nbBitmapsCached + " / " + mSteps.size());
304     }
305 
306 }
307