blob: 4765a59909cc8f9362b04830f17c8f0ee091b34c [file] [log] [blame]
/*
* Copyright (C) 2012 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.graphics.Rect;
import android.support.v8.renderscript.Allocation;
import android.util.JsonReader;
import android.util.JsonWriter;
import android.util.Log;
import com.android.gallery3d.R;
import com.android.gallery3d.filtershow.cache.BitmapCache;
import com.android.gallery3d.filtershow.cache.ImageLoader;
import com.android.gallery3d.filtershow.filters.BaseFiltersManager;
import com.android.gallery3d.filtershow.filters.FilterCropRepresentation;
import com.android.gallery3d.filtershow.filters.FilterFxRepresentation;
import com.android.gallery3d.filtershow.filters.FilterImageBorderRepresentation;
import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation;
import com.android.gallery3d.filtershow.filters.FilterRepresentation;
import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation;
import com.android.gallery3d.filtershow.filters.FilterStraightenRepresentation;
import com.android.gallery3d.filtershow.filters.FilterUserPresetRepresentation;
import com.android.gallery3d.filtershow.filters.FiltersManager;
import com.android.gallery3d.filtershow.filters.ImageFilter;
import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils;
import com.android.gallery3d.filtershow.imageshow.MasterImage;
import com.android.gallery3d.filtershow.state.State;
import com.android.gallery3d.filtershow.state.StateAdapter;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Vector;
public class ImagePreset {
private static final String LOGTAG = "ImagePreset";
public static final String JASON_SAVED = "Saved";
private Vector<FilterRepresentation> mFilters = new Vector<FilterRepresentation>();
private boolean mDoApplyGeometry = true;
private boolean mDoApplyFilters = true;
private boolean mPartialRendering = false;
private Rect mPartialRenderingBounds;
private static final boolean DEBUG = false;
public ImagePreset() {
}
public ImagePreset(ImagePreset source) {
for (int i = 0; i < source.mFilters.size(); i++) {
FilterRepresentation sourceRepresentation = source.mFilters.elementAt(i);
mFilters.add(sourceRepresentation.copy());
}
}
public Vector<FilterRepresentation> getFilters() {
return mFilters;
}
public FilterRepresentation getFilterRepresentation(int position) {
FilterRepresentation representation = null;
representation = mFilters.elementAt(position).copy();
return representation;
}
private static boolean sameSerializationName(String a, String b) {
if (a != null && b != null) {
return a.equals(b);
} else {
return a == null && b == null;
}
}
public static boolean sameSerializationName(FilterRepresentation a, FilterRepresentation b) {
if (a == null || b == null) {
return false;
}
return sameSerializationName(a.getSerializationName(), b.getSerializationName());
}
public int getPositionForRepresentation(FilterRepresentation representation) {
for (int i = 0; i < mFilters.size(); i++) {
if (sameSerializationName(mFilters.elementAt(i), representation)) {
return i;
}
}
return -1;
}
private FilterRepresentation getFilterRepresentationForType(int type) {
for (int i = 0; i < mFilters.size(); i++) {
if (mFilters.elementAt(i).getFilterType() == type) {
return mFilters.elementAt(i);
}
}
return null;
}
public int getPositionForType(int type) {
for (int i = 0; i < mFilters.size(); i++) {
if (mFilters.elementAt(i).getFilterType() == type) {
return i;
}
}
return -1;
}
public FilterRepresentation getFilterRepresentationCopyFrom(
FilterRepresentation filterRepresentation) {
// TODO: add concept of position in the filters (to allow multiple instances)
if (filterRepresentation == null) {
return null;
}
int position = getPositionForRepresentation(filterRepresentation);
if (position == -1) {
return null;
}
FilterRepresentation representation = mFilters.elementAt(position);
if (representation != null) {
representation = representation.copy();
}
return representation;
}
public void updateFilterRepresentations(Collection<FilterRepresentation> reps) {
for (FilterRepresentation r : reps) {
updateOrAddFilterRepresentation(r);
}
}
public void updateOrAddFilterRepresentation(FilterRepresentation rep) {
int pos = getPositionForRepresentation(rep);
if (pos != -1) {
mFilters.elementAt(pos).useParametersFrom(rep);
} else {
addFilter(rep.copy());
}
}
public void setDoApplyGeometry(boolean value) {
mDoApplyGeometry = value;
}
public void setDoApplyFilters(boolean value) {
mDoApplyFilters = value;
}
public boolean getDoApplyFilters() {
return mDoApplyFilters;
}
public boolean hasModifications() {
for (int i = 0; i < mFilters.size(); i++) {
FilterRepresentation filter = mFilters.elementAt(i);
if (!filter.isNil()) {
return true;
}
}
return false;
}
public boolean contains(byte type) {
for (FilterRepresentation representation : mFilters) {
if (representation.getFilterType() == type
&& !representation.isNil()) {
return true;
}
}
return false;
}
public boolean isPanoramaSafe() {
for (FilterRepresentation representation : mFilters) {
if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY
&& !representation.isNil()) {
return false;
}
if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER
&& !representation.isNil()) {
return false;
}
if (representation.getFilterType() == FilterRepresentation.TYPE_VIGNETTE
&& !representation.isNil()) {
return false;
}
if (representation.getFilterType() == FilterRepresentation.TYPE_TINYPLANET
&& !representation.isNil()) {
return false;
}
}
return true;
}
public boolean same(ImagePreset preset) {
if (preset == null) {
return false;
}
if (preset.mFilters.size() != mFilters.size()) {
return false;
}
if (mDoApplyGeometry != preset.mDoApplyGeometry) {
return false;
}
if (mDoApplyFilters != preset.mDoApplyFilters) {
if (mFilters.size() > 0 || preset.mFilters.size() > 0) {
return false;
}
}
if (mDoApplyFilters && preset.mDoApplyFilters) {
for (int i = 0; i < preset.mFilters.size(); i++) {
FilterRepresentation a = preset.mFilters.elementAt(i);
FilterRepresentation b = mFilters.elementAt(i);
if (!a.same(b)) {
return false;
}
}
}
return true;
}
public boolean equals(ImagePreset preset) {
if (preset == null) {
return false;
}
if (preset.mFilters.size() != mFilters.size()) {
return false;
}
if (mDoApplyGeometry != preset.mDoApplyGeometry) {
return false;
}
if (mDoApplyFilters != preset.mDoApplyFilters) {
if (mFilters.size() > 0 || preset.mFilters.size() > 0) {
return false;
}
}
for (int i = 0; i < preset.mFilters.size(); i++) {
FilterRepresentation a = preset.mFilters.elementAt(i);
FilterRepresentation b = mFilters.elementAt(i);
boolean isGeometry = false;
if (a instanceof FilterRotateRepresentation
|| a instanceof FilterMirrorRepresentation
|| a instanceof FilterCropRepresentation
|| a instanceof FilterStraightenRepresentation) {
isGeometry = true;
}
boolean evaluate = true;
if (!isGeometry && mDoApplyGeometry && !mDoApplyFilters) {
evaluate = false;
} else if (isGeometry && !mDoApplyGeometry && mDoApplyFilters) {
evaluate = false;
}
if (evaluate && !a.equals(b)) {
return false;
}
}
return true;
}
public int similarUpTo(ImagePreset preset) {
for (int i = 0; i < preset.mFilters.size(); i++) {
FilterRepresentation a = preset.mFilters.elementAt(i);
if (i < mFilters.size()) {
FilterRepresentation b = mFilters.elementAt(i);
if (!a.same(b)) {
return i;
}
if (!a.equals(b)) {
return i;
}
} else {
return i;
}
}
return preset.mFilters.size();
}
public void showFilters() {
Log.v(LOGTAG, "\\\\\\ showFilters -- " + mFilters.size() + " filters");
int n = 0;
for (FilterRepresentation representation : mFilters) {
Log.v(LOGTAG, " filter " + n + " : " + representation.toString());
n++;
}
Log.v(LOGTAG, "/// showFilters -- " + mFilters.size() + " filters");
}
public FilterRepresentation getLastRepresentation() {
if (mFilters.size() > 0) {
return mFilters.lastElement();
}
return null;
}
public void removeFilter(FilterRepresentation filterRepresentation) {
if (filterRepresentation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
for (int i = 0; i < mFilters.size(); i++) {
if (mFilters.elementAt(i).getFilterType()
== filterRepresentation.getFilterType()) {
mFilters.remove(i);
break;
}
}
} else {
for (int i = 0; i < mFilters.size(); i++) {
if (sameSerializationName(mFilters.elementAt(i), filterRepresentation)) {
mFilters.remove(i);
break;
}
}
}
}
// If the filter is an "None" effect or border, then just don't add this filter.
public void addFilter(FilterRepresentation representation) {
if (representation instanceof FilterUserPresetRepresentation) {
ImagePreset preset = ((FilterUserPresetRepresentation) representation).getImagePreset();
if (preset.nbFilters() == 1
&& preset.contains(FilterRepresentation.TYPE_FX)) {
FilterRepresentation rep = preset.getFilterRepresentationForType(
FilterRepresentation.TYPE_FX);
addFilter(rep);
} else {
// user preset replaces everything
mFilters.clear();
for (int i = 0; i < preset.nbFilters(); i++) {
addFilter(preset.getFilterRepresentation(i));
}
}
} else if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
// Add geometry filter, removing duplicates and do-nothing operations.
for (int i = 0; i < mFilters.size(); i++) {
if (sameSerializationName(representation, mFilters.elementAt(i))) {
mFilters.remove(i);
}
}
int index = 0;
for (; index < mFilters.size(); index++) {
FilterRepresentation rep = mFilters.elementAt(index);
if (rep.getFilterType() != FilterRepresentation.TYPE_GEOMETRY) {
break;
}
}
if (!representation.isNil()) {
mFilters.insertElementAt(representation, index);
}
} else if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
removeFilter(representation);
if (!isNoneBorderFilter(representation)) {
mFilters.add(representation);
}
} else if (representation.getFilterType() == FilterRepresentation.TYPE_FX) {
boolean replaced = false;
for (int i = 0; i < mFilters.size(); i++) {
FilterRepresentation current = mFilters.elementAt(i);
if (current.getFilterType() == FilterRepresentation.TYPE_FX) {
mFilters.remove(i);
replaced = true;
if (!isNoneFxFilter(representation)) {
mFilters.add(i, representation);
}
break;
}
}
if (!replaced && !isNoneFxFilter(representation)) {
mFilters.add(0, representation);
}
} else {
mFilters.add(representation);
}
// Enforces Filter type ordering for borders
FilterRepresentation border = null;
for (int i = 0; i < mFilters.size();) {
FilterRepresentation rep = mFilters.elementAt(i);
if (rep.getFilterType() == FilterRepresentation.TYPE_BORDER) {
border = rep;
mFilters.remove(i);
continue;
}
i++;
}
if (border != null) {
mFilters.add(border);
}
}
private boolean isNoneBorderFilter(FilterRepresentation representation) {
return representation instanceof FilterImageBorderRepresentation &&
((FilterImageBorderRepresentation) representation).getDrawableResource() == 0;
}
private boolean isNoneFxFilter(FilterRepresentation representation) {
return representation instanceof FilterFxRepresentation &&
((FilterFxRepresentation) representation).getNameResource() == R.string.none;
}
public FilterRepresentation getRepresentation(FilterRepresentation filterRepresentation) {
for (int i = 0; i < mFilters.size(); i++) {
FilterRepresentation representation = mFilters.elementAt(i);
if (sameSerializationName(representation, filterRepresentation)) {
return representation;
}
}
return null;
}
public Bitmap apply(Bitmap original, FilterEnvironment environment) {
Bitmap bitmap = original;
bitmap = applyFilters(bitmap, -1, -1, environment);
return applyBorder(bitmap, environment);
}
public Collection<FilterRepresentation> getGeometryFilters() {
ArrayList<FilterRepresentation> geometry = new ArrayList<FilterRepresentation>();
for (FilterRepresentation r : mFilters) {
if (r.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
geometry.add(r);
}
}
return geometry;
}
public FilterRepresentation getFilterWithSerializationName(String serializationName) {
for (FilterRepresentation r : mFilters) {
if (r != null) {
if (sameSerializationName(r.getSerializationName(), serializationName)) {
return r.copy();
}
}
}
return null;
}
public Rect finalGeometryRect(int width, int height) {
return GeometryMathUtils.finalGeometryRect(width, height, getGeometryFilters());
}
public Bitmap applyGeometry(Bitmap bitmap, FilterEnvironment environment) {
// Apply any transform -- 90 rotate, flip, straighten, crop
// Returns a new bitmap.
if (mDoApplyGeometry) {
Bitmap bmp = GeometryMathUtils.applyGeometryRepresentations(
getGeometryFilters(), bitmap);
if (bmp != bitmap) {
environment.cache(bitmap);
}
return bmp;
}
return bitmap;
}
public Bitmap applyBorder(Bitmap bitmap, FilterEnvironment environment) {
// get the border from the list of filters.
FilterRepresentation border = getFilterRepresentationForType(
FilterRepresentation.TYPE_BORDER);
if (border != null && mDoApplyGeometry) {
bitmap = environment.applyRepresentation(border, bitmap);
if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) {
}
}
return bitmap;
}
public int nbFilters() {
return mFilters.size();
}
public Bitmap applyFilters(Bitmap bitmap, int from, int to, FilterEnvironment environment) {
if (mDoApplyFilters) {
if (from < 0) {
from = 0;
}
if (to == -1) {
to = mFilters.size();
}
for (int i = from; i < to; i++) {
FilterRepresentation representation = mFilters.elementAt(i);
if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
// skip the geometry as it's already applied.
continue;
}
if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
// for now, let's skip the border as it will be applied in
// applyBorder()
// TODO: might be worth getting rid of applyBorder.
continue;
}
Bitmap tmp = bitmap;
bitmap = environment.applyRepresentation(representation, bitmap);
if (tmp != bitmap) {
environment.cache(tmp);
}
if (environment.needsStop()) {
return bitmap;
}
}
}
return bitmap;
}
public void applyBorder(Allocation in, Allocation out,
boolean copyOut, FilterEnvironment environment) {
FilterRepresentation border = getFilterRepresentationForType(
FilterRepresentation.TYPE_BORDER);
if (border != null && mDoApplyGeometry) {
// TODO: should keep the bitmap around
Allocation bitmapIn = in;
if (copyOut) {
bitmapIn = Allocation.createTyped(
CachingPipeline.getRenderScriptContext(), in.getType());
bitmapIn.copyFrom(out);
}
environment.applyRepresentation(border, bitmapIn, out);
}
}
public void applyFilters(int from, int to, Allocation in, Allocation out,
FilterEnvironment environment) {
if (mDoApplyFilters) {
if (from < 0) {
from = 0;
}
if (to == -1) {
to = mFilters.size();
}
for (int i = from; i < to; i++) {
FilterRepresentation representation = mFilters.elementAt(i);
if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY
|| representation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
continue;
}
if (i > from) {
in.copyFrom(out);
}
environment.applyRepresentation(representation, in, out);
}
}
}
public boolean canDoPartialRendering() {
if (MasterImage.getImage().getZoomOrientation() != ImageLoader.ORI_NORMAL) {
return false;
}
for (int i = 0; i < mFilters.size(); i++) {
FilterRepresentation representation = mFilters.elementAt(i);
if (!representation.supportsPartialRendering()) {
return false;
}
}
return true;
}
public void fillImageStateAdapter(StateAdapter imageStateAdapter) {
if (imageStateAdapter == null) {
return;
}
Vector<State> states = new Vector<State>();
for (FilterRepresentation filter : mFilters) {
if (filter instanceof FilterUserPresetRepresentation) {
// do not show the user preset itself in the state panel
continue;
}
State state = new State(filter.getName());
state.setFilterRepresentation(filter);
states.add(state);
}
imageStateAdapter.fill(states);
}
public void setPartialRendering(boolean partialRendering, Rect bounds) {
mPartialRendering = partialRendering;
mPartialRenderingBounds = bounds;
}
public boolean isPartialRendering() {
return mPartialRendering;
}
public Rect getPartialRenderingBounds() {
return mPartialRenderingBounds;
}
public Vector<ImageFilter> getUsedFilters(BaseFiltersManager filtersManager) {
Vector<ImageFilter> usedFilters = new Vector<ImageFilter>();
for (int i = 0; i < mFilters.size(); i++) {
FilterRepresentation representation = mFilters.elementAt(i);
ImageFilter filter = filtersManager.getFilterForRepresentation(representation);
usedFilters.add(filter);
}
return usedFilters;
}
public String getJsonString(String name) {
StringWriter swriter = new StringWriter();
try {
JsonWriter writer = new JsonWriter(swriter);
writeJson(writer, name);
writer.close();
} catch (IOException e) {
return null;
}
return swriter.toString();
}
public void writeJson(JsonWriter writer, String name) {
int numFilters = mFilters.size();
try {
writer.beginObject();
for (int i = 0; i < numFilters; i++) {
FilterRepresentation filter = mFilters.get(i);
if (filter instanceof FilterUserPresetRepresentation) {
continue;
}
String sname = filter.getSerializationName();
if (DEBUG) {
Log.v(LOGTAG, "Serialization: " + sname);
if (sname == null) {
Log.v(LOGTAG, "Serialization name null for filter: " + filter);
}
}
writer.name(sname);
filter.serializeRepresentation(writer);
}
writer.endObject();
} catch (IOException e) {
Log.e(LOGTAG,"Error encoding JASON",e);
}
}
/**
* populates preset from JSON string
*
* @param filterString a JSON string
* @return true on success if false ImagePreset is undefined
*/
public boolean readJsonFromString(String filterString) {
if (DEBUG) {
Log.v(LOGTAG, "reading preset: \"" + filterString + "\"");
}
StringReader sreader = new StringReader(filterString);
try {
JsonReader reader = new JsonReader(sreader);
boolean ok = readJson(reader);
if (!ok) {
reader.close();
return false;
}
reader.close();
} catch (Exception e) {
Log.e(LOGTAG, "\""+filterString+"\"");
Log.e(LOGTAG, "parsing the filter parameters:", e);
return false;
}
return true;
}
/**
* populates preset from JSON stream
*
* @param sreader a JSON string
* @return true on success if false ImagePreset is undefined
*/
public boolean readJson(JsonReader sreader) throws IOException {
sreader.beginObject();
while (sreader.hasNext()) {
String name = sreader.nextName();
FilterRepresentation filter = creatFilterFromName(name);
if (filter == null) {
Log.w(LOGTAG, "UNKNOWN FILTER! " + name);
return false;
}
filter.deSerializeRepresentation(sreader);
addFilter(filter);
}
sreader.endObject();
return true;
}
FilterRepresentation creatFilterFromName(String name) {
if (FilterRotateRepresentation.SERIALIZATION_NAME.equals(name)) {
return new FilterRotateRepresentation();
} else if (FilterMirrorRepresentation.SERIALIZATION_NAME.equals(name)) {
return new FilterMirrorRepresentation();
} else if (FilterStraightenRepresentation.SERIALIZATION_NAME.equals(name)) {
return new FilterStraightenRepresentation();
} else if (FilterCropRepresentation.SERIALIZATION_NAME.equals(name)) {
return new FilterCropRepresentation();
}
FiltersManager filtersManager = FiltersManager.getManager();
return filtersManager.createFilterFromName(name);
}
public void updateWith(ImagePreset preset) {
if (preset.mFilters.size() != mFilters.size()) {
Log.e(LOGTAG, "Updating a preset with an incompatible one");
return;
}
for (int i = 0; i < mFilters.size(); i++) {
FilterRepresentation destRepresentation = mFilters.elementAt(i);
FilterRepresentation sourceRepresentation = preset.mFilters.elementAt(i);
destRepresentation.useParametersFrom(sourceRepresentation);
}
}
}