Home | History | Annotate | Download | only in pipeline
      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 
     39         public CacheStep() {
     40             representations = new ArrayList<FilterRepresentation>();
     41         }
     42 
     43         public void add(FilterRepresentation representation) {
     44             representations.add(representation);
     45         }
     46 
     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 
     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 
     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 
     87         public String getName() {
     88             if (representations.size() > 0) {
     89                 return representations.get(0).getName();
     90             }
     91             return "EMPTY";
     92         }
     93 
     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 
    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 
    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 
    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 
    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 
    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