blob: c2912ed788498dab6e35316b31aca006dad3e03e [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.gallery3d.filtershow.pipeline;
import android.graphics.Bitmap;
import android.util.Log;
import com.android.gallery3d.filtershow.cache.BitmapCache;
import com.android.gallery3d.filtershow.filters.FilterRepresentation;
import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils;
import java.util.ArrayList;
import java.util.Vector;
public class CacheProcessing {
private static final String LOGTAG = "CacheProcessing";
private static final boolean DEBUG = false;
private static final boolean NO_CACHING = false;
private Vector<CacheStep> mSteps = new Vector<CacheStep>();
static class CacheStep {
ArrayList<FilterRepresentation> representations;
Bitmap cache;
public CacheStep() {
representations = new ArrayList<FilterRepresentation>();
}
public void add(FilterRepresentation representation) {
representations.add(representation);
}
public boolean canMergeWith(FilterRepresentation representation) {
for (FilterRepresentation rep : representations) {
if (!rep.canMergeWith(representation)) {
return false;
}
}
return true;
}
public boolean equals(CacheStep step) {
if (representations.size() != step.representations.size()) {
return false;
}
for (int i = 0; i < representations.size(); i++) {
FilterRepresentation r1 = representations.get(i);
FilterRepresentation r2 = step.representations.get(i);
if (!r1.equals(r2)) {
return false;
}
}
return true;
}
public static Vector<CacheStep> buildSteps(Vector<FilterRepresentation> filters) {
Vector<CacheStep> steps = new Vector<CacheStep>();
CacheStep step = new CacheStep();
for (int i = 0; i < filters.size(); i++) {
FilterRepresentation representation = filters.elementAt(i);
if (step.canMergeWith(representation)) {
step.add(representation.copy());
} else {
steps.add(step);
step = new CacheStep();
step.add(representation.copy());
}
}
steps.add(step);
return steps;
}
public String getName() {
if (representations.size() > 0) {
return representations.get(0).getName();
}
return "EMPTY";
}
public Bitmap apply(FilterEnvironment environment, Bitmap cacheBitmap) {
boolean onlyGeometry = true;
Bitmap source = cacheBitmap;
for (FilterRepresentation representation : representations) {
if (representation.getFilterType() != FilterRepresentation.TYPE_GEOMETRY) {
onlyGeometry = false;
break;
}
}
if (onlyGeometry) {
ArrayList<FilterRepresentation> geometry = new ArrayList<FilterRepresentation>();
for (FilterRepresentation representation : representations) {
geometry.add(representation);
}
if (DEBUG) {
Log.v(LOGTAG, "Apply geometry to bitmap " + cacheBitmap);
}
cacheBitmap = GeometryMathUtils.applyGeometryRepresentations(geometry, cacheBitmap);
} else {
for (FilterRepresentation representation : representations) {
if (DEBUG) {
Log.v(LOGTAG, "Apply " + representation.getSerializationName()
+ " to bitmap " + cacheBitmap);
}
cacheBitmap = environment.applyRepresentation(representation, cacheBitmap);
}
}
if (cacheBitmap != source) {
environment.cache(source);
}
if (DEBUG) {
Log.v(LOGTAG, "Apply returns bitmap " + cacheBitmap);
}
return cacheBitmap;
}
}
public Bitmap process(Bitmap originalBitmap,
Vector<FilterRepresentation> filters,
FilterEnvironment environment) {
if (filters.size() == 0) {
return environment.getBitmapCopy(originalBitmap, BitmapCache.PREVIEW_CACHE_NO_FILTERS);
}
environment.getBimapCache().setCacheProcessing(this);
if (DEBUG) {
displayFilters(filters);
}
Vector<CacheStep> steps = CacheStep.buildSteps(filters);
// New set of filters, let's clear the cache and rebuild it.
if (steps.size() != mSteps.size()) {
mSteps = steps;
}
if (DEBUG) {
displaySteps(mSteps);
}
// First, let's find how similar we are in our cache
// compared to the current list of filters
int similarUpToIndex = -1;
boolean similar = true;
for (int i = 0; i < steps.size(); i++) {
CacheStep newStep = steps.elementAt(i);
CacheStep cacheStep = mSteps.elementAt(i);
if (similar) {
similar = newStep.equals(cacheStep);
}
if (similar) {
similarUpToIndex = i;
} else {
mSteps.remove(i);
mSteps.insertElementAt(newStep, i);
environment.cache(cacheStep.cache);
}
}
if (DEBUG) {
Log.v(LOGTAG, "similar up to index " + similarUpToIndex);
}
// Now, let's get the earliest cached result in our pipeline
Bitmap cacheBitmap = null;
int findBaseImageIndex = similarUpToIndex;
if (findBaseImageIndex > -1) {
while (findBaseImageIndex > 0
&& mSteps.elementAt(findBaseImageIndex).cache == null) {
findBaseImageIndex--;
}
cacheBitmap = mSteps.elementAt(findBaseImageIndex).cache;
}
if (DEBUG) {
Log.v(LOGTAG, "found baseImageIndex: " + findBaseImageIndex + " max is "
+ mSteps.size() + " cacheBitmap: " + cacheBitmap);
}
if (NO_CACHING) {
cacheBitmap = environment.getBitmapCopy(originalBitmap,
BitmapCache.PREVIEW_CACHE_NO_ROOT);
for (int i = 0; i < mSteps.size(); i++) {
CacheStep step = mSteps.elementAt(i);
Bitmap prev = cacheBitmap;
cacheBitmap = step.apply(environment, cacheBitmap);
if (prev != cacheBitmap) {
environment.cache(prev);
}
}
return cacheBitmap;
}
Bitmap originalCopy = null;
int lastPositionCached = -1;
for (int i = findBaseImageIndex; i < mSteps.size(); i++) {
if (i == -1 || cacheBitmap == null) {
cacheBitmap = environment.getBitmapCopy(originalBitmap,
BitmapCache.PREVIEW_CACHE_NO_ROOT);
originalCopy = cacheBitmap;
if (DEBUG) {
Log.v(LOGTAG, "i: " + i + " cacheBitmap: " + cacheBitmap + " w: "
+ cacheBitmap.getWidth() + " h: " + cacheBitmap.getHeight()
+ " got from original Bitmap: " + originalBitmap + " w: "
+ originalBitmap.getWidth() + " h: " + originalBitmap.getHeight());
}
if (i == -1) {
continue;
}
}
CacheStep step = mSteps.elementAt(i);
if (step.cache == null) {
if (DEBUG) {
Log.v(LOGTAG, "i: " + i + " get new copy for cacheBitmap "
+ cacheBitmap + " apply...");
}
cacheBitmap = environment.getBitmapCopy(cacheBitmap, BitmapCache.PREVIEW_CACHE);
cacheBitmap = step.apply(environment, cacheBitmap);
step.cache = cacheBitmap;
lastPositionCached = i;
}
}
environment.cache(originalCopy);
if (DEBUG) {
Log.v(LOGTAG, "now let's cleanup the cache...");
displayNbBitmapsInCache();
}
// Let's see if we can cleanup the cache for unused bitmaps
for (int i = 0; i < similarUpToIndex; i++) {
CacheStep currentStep = mSteps.elementAt(i);
Bitmap bitmap = currentStep.cache;
currentStep.cache = null;
environment.cache(bitmap);
}
if (DEBUG) {
Log.v(LOGTAG, "cleanup done...");
displayNbBitmapsInCache();
}
if (lastPositionCached != -1) {
// The last element will never be reused, remove it from the cache.
mSteps.elementAt(lastPositionCached).cache = null;
}
if (contains(cacheBitmap)) {
return environment.getBitmapCopy(cacheBitmap, BitmapCache.PREVIEW_CACHE_NO_APPLY);
}
return cacheBitmap;
}
public boolean contains(Bitmap bitmap) {
for (int i = 0; i < mSteps.size(); i++) {
if (mSteps.elementAt(i).cache == bitmap) {
return true;
}
}
return false;
}
private void displayFilters(Vector<FilterRepresentation> filters) {
Log.v(LOGTAG, "------>>> Filters received");
for (int i = 0; i < filters.size(); i++) {
FilterRepresentation filter = filters.elementAt(i);
Log.v(LOGTAG, "[" + i + "] - " + filter.getName());
}
Log.v(LOGTAG, "<<<------");
}
private void displaySteps(Vector<CacheStep> filters) {
Log.v(LOGTAG, "------>>>");
for (int i = 0; i < filters.size(); i++) {
CacheStep newStep = filters.elementAt(i);
CacheStep step = mSteps.elementAt(i);
boolean similar = step.equals(newStep);
Log.v(LOGTAG, "[" + i + "] - " + step.getName()
+ " similar rep ? " + (similar ? "YES" : "NO")
+ " -- bitmap: " + step.cache);
}
Log.v(LOGTAG, "<<<------");
}
private void displayNbBitmapsInCache() {
int nbBitmapsCached = 0;
for (int i = 0; i < mSteps.size(); i++) {
CacheStep step = mSteps.elementAt(i);
if (step.cache != null) {
nbBitmapsCached++;
}
}
Log.v(LOGTAG, "nb bitmaps in cache: " + nbBitmapsCached + " / " + mSteps.size());
}
}