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