2 * Copyright (C) 2012 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.gallery3d.filtershow.pipeline;
19 import android.graphics.Bitmap;
20 import android.graphics.Rect;
21 import android.renderscript.Allocation;
22 import android.util.JsonReader;
23 import android.util.JsonWriter;
24 import android.util.Log;
26 import com.android.gallery3d.R;
27 import com.android.gallery3d.filtershow.cache.BitmapCache;
28 import com.android.gallery3d.filtershow.cache.ImageLoader;
29 import com.android.gallery3d.filtershow.filters.BaseFiltersManager;
30 import com.android.gallery3d.filtershow.filters.FilterCropRepresentation;
31 import com.android.gallery3d.filtershow.filters.FilterFxRepresentation;
32 import com.android.gallery3d.filtershow.filters.FilterImageBorderRepresentation;
33 import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation;
34 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
35 import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation;
36 import com.android.gallery3d.filtershow.filters.FilterStraightenRepresentation;
37 import com.android.gallery3d.filtershow.filters.FilterUserPresetRepresentation;
38 import com.android.gallery3d.filtershow.filters.FiltersManager;
39 import com.android.gallery3d.filtershow.filters.ImageFilter;
40 import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils;
41 import com.android.gallery3d.filtershow.imageshow.MasterImage;
42 import com.android.gallery3d.filtershow.state.State;
43 import com.android.gallery3d.filtershow.state.StateAdapter;
45 import java.io.IOException;
46 import java.io.StringReader;
47 import java.io.StringWriter;
48 import java.util.ArrayList;
49 import java.util.Collection;
50 import java.util.Vector;
52 public class ImagePreset {
54 private static final String LOGTAG = "ImagePreset";
55 public static final String JASON_SAVED = "Saved";
57 private Vector<FilterRepresentation> mFilters = new Vector<FilterRepresentation>();
59 private boolean mDoApplyGeometry = true;
60 private boolean mDoApplyFilters = true;
62 private boolean mPartialRendering = false;
63 private Rect mPartialRenderingBounds;
64 private static final boolean DEBUG = false;
66 public ImagePreset() {
69 public ImagePreset(ImagePreset source) {
70 for (int i = 0; i < source.mFilters.size(); i++) {
71 FilterRepresentation sourceRepresentation = source.mFilters.elementAt(i);
72 mFilters.add(sourceRepresentation.copy());
76 public Vector<FilterRepresentation> getFilters() {
80 public FilterRepresentation getFilterRepresentation(int position) {
81 FilterRepresentation representation = null;
83 representation = mFilters.elementAt(position).copy();
85 return representation;
88 private static boolean sameSerializationName(String a, String b) {
89 if (a != null && b != null) {
92 return a == null && b == null;
96 public static boolean sameSerializationName(FilterRepresentation a, FilterRepresentation b) {
97 if (a == null || b == null) {
100 return sameSerializationName(a.getSerializationName(), b.getSerializationName());
103 public int getPositionForRepresentation(FilterRepresentation representation) {
104 for (int i = 0; i < mFilters.size(); i++) {
105 if (sameSerializationName(mFilters.elementAt(i), representation)) {
112 private FilterRepresentation getFilterRepresentationForType(int type) {
113 for (int i = 0; i < mFilters.size(); i++) {
114 if (mFilters.elementAt(i).getFilterType() == type) {
115 return mFilters.elementAt(i);
121 public int getPositionForType(int type) {
122 for (int i = 0; i < mFilters.size(); i++) {
123 if (mFilters.elementAt(i).getFilterType() == type) {
130 public FilterRepresentation getFilterRepresentationCopyFrom(
131 FilterRepresentation filterRepresentation) {
132 // TODO: add concept of position in the filters (to allow multiple instances)
133 if (filterRepresentation == null) {
136 int position = getPositionForRepresentation(filterRepresentation);
137 if (position == -1) {
140 FilterRepresentation representation = mFilters.elementAt(position);
141 if (representation != null) {
142 representation = representation.copy();
144 return representation;
147 public void updateFilterRepresentations(Collection<FilterRepresentation> reps) {
148 for (FilterRepresentation r : reps) {
149 updateOrAddFilterRepresentation(r);
153 public void updateOrAddFilterRepresentation(FilterRepresentation rep) {
154 int pos = getPositionForRepresentation(rep);
156 mFilters.elementAt(pos).useParametersFrom(rep);
158 addFilter(rep.copy());
162 public void setDoApplyGeometry(boolean value) {
163 mDoApplyGeometry = value;
166 public void setDoApplyFilters(boolean value) {
167 mDoApplyFilters = value;
170 public boolean getDoApplyFilters() {
171 return mDoApplyFilters;
174 public boolean hasModifications() {
175 for (int i = 0; i < mFilters.size(); i++) {
176 FilterRepresentation filter = mFilters.elementAt(i);
177 if (!filter.isNil()) {
184 public boolean contains(byte type) {
185 for (FilterRepresentation representation : mFilters) {
186 if (representation.getFilterType() == type
187 && !representation.isNil()) {
194 public boolean isPanoramaSafe() {
195 for (FilterRepresentation representation : mFilters) {
196 if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY
197 && !representation.isNil()) {
200 if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER
201 && !representation.isNil()) {
204 if (representation.getFilterType() == FilterRepresentation.TYPE_VIGNETTE
205 && !representation.isNil()) {
208 if (representation.getFilterType() == FilterRepresentation.TYPE_TINYPLANET
209 && !representation.isNil()) {
216 public boolean same(ImagePreset preset) {
217 if (preset == null) {
221 if (preset.mFilters.size() != mFilters.size()) {
225 if (mDoApplyGeometry != preset.mDoApplyGeometry) {
229 if (mDoApplyFilters != preset.mDoApplyFilters) {
230 if (mFilters.size() > 0 || preset.mFilters.size() > 0) {
235 if (mDoApplyFilters && preset.mDoApplyFilters) {
236 for (int i = 0; i < preset.mFilters.size(); i++) {
237 FilterRepresentation a = preset.mFilters.elementAt(i);
238 FilterRepresentation b = mFilters.elementAt(i);
249 public boolean equals(ImagePreset preset) {
250 if (preset == null) {
254 if (preset.mFilters.size() != mFilters.size()) {
258 if (mDoApplyGeometry != preset.mDoApplyGeometry) {
262 if (mDoApplyFilters != preset.mDoApplyFilters) {
263 if (mFilters.size() > 0 || preset.mFilters.size() > 0) {
268 for (int i = 0; i < preset.mFilters.size(); i++) {
269 FilterRepresentation a = preset.mFilters.elementAt(i);
270 FilterRepresentation b = mFilters.elementAt(i);
271 boolean isGeometry = false;
272 if (a instanceof FilterRotateRepresentation
273 || a instanceof FilterMirrorRepresentation
274 || a instanceof FilterCropRepresentation
275 || a instanceof FilterStraightenRepresentation) {
278 boolean evaluate = true;
279 if (!isGeometry && mDoApplyGeometry && !mDoApplyFilters) {
281 } else if (isGeometry && !mDoApplyGeometry && mDoApplyFilters) {
284 if (evaluate && !a.equals(b)) {
292 public int similarUpTo(ImagePreset preset) {
293 for (int i = 0; i < preset.mFilters.size(); i++) {
294 FilterRepresentation a = preset.mFilters.elementAt(i);
295 if (i < mFilters.size()) {
296 FilterRepresentation b = mFilters.elementAt(i);
307 return preset.mFilters.size();
310 public void showFilters() {
311 Log.v(LOGTAG, "\\\\\\ showFilters -- " + mFilters.size() + " filters");
313 for (FilterRepresentation representation : mFilters) {
314 Log.v(LOGTAG, " filter " + n + " : " + representation.toString());
317 Log.v(LOGTAG, "/// showFilters -- " + mFilters.size() + " filters");
320 public FilterRepresentation getLastRepresentation() {
321 if (mFilters.size() > 0) {
322 return mFilters.lastElement();
327 public void removeFilter(FilterRepresentation filterRepresentation) {
328 if (filterRepresentation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
329 for (int i = 0; i < mFilters.size(); i++) {
330 if (mFilters.elementAt(i).getFilterType()
331 == filterRepresentation.getFilterType()) {
337 for (int i = 0; i < mFilters.size(); i++) {
338 if (sameSerializationName(mFilters.elementAt(i), filterRepresentation)) {
346 // If the filter is an "None" effect or border, then just don't add this filter.
347 public void addFilter(FilterRepresentation representation) {
348 if (representation instanceof FilterUserPresetRepresentation) {
349 ImagePreset preset = ((FilterUserPresetRepresentation) representation).getImagePreset();
350 if (preset.nbFilters() == 1
351 && preset.contains(FilterRepresentation.TYPE_FX)) {
352 FilterRepresentation rep = preset.getFilterRepresentationForType(
353 FilterRepresentation.TYPE_FX);
356 // user preset replaces everything
358 for (int i = 0; i < preset.nbFilters(); i++) {
359 addFilter(preset.getFilterRepresentation(i));
362 } else if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
363 // Add geometry filter, removing duplicates and do-nothing operations.
364 for (int i = 0; i < mFilters.size(); i++) {
365 if (sameSerializationName(representation, mFilters.elementAt(i))) {
370 for (; index < mFilters.size(); index++) {
371 FilterRepresentation rep = mFilters.elementAt(index);
372 if (rep.getFilterType() != FilterRepresentation.TYPE_GEOMETRY) {
376 if (!representation.isNil()) {
377 mFilters.insertElementAt(representation, index);
379 } else if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
380 removeFilter(representation);
381 if (!isNoneBorderFilter(representation)) {
382 mFilters.add(representation);
384 } else if (representation.getFilterType() == FilterRepresentation.TYPE_FX) {
385 boolean replaced = false;
386 for (int i = 0; i < mFilters.size(); i++) {
387 FilterRepresentation current = mFilters.elementAt(i);
388 if (current.getFilterType() == FilterRepresentation.TYPE_FX) {
391 if (!isNoneFxFilter(representation)) {
392 mFilters.add(i, representation);
397 if (!replaced && !isNoneFxFilter(representation)) {
398 mFilters.add(0, representation);
401 mFilters.add(representation);
403 // Enforces Filter type ordering for borders
404 FilterRepresentation border = null;
405 for (int i = 0; i < mFilters.size();) {
406 FilterRepresentation rep = mFilters.elementAt(i);
407 if (rep.getFilterType() == FilterRepresentation.TYPE_BORDER) {
414 if (border != null) {
415 mFilters.add(border);
419 private boolean isNoneBorderFilter(FilterRepresentation representation) {
420 return representation instanceof FilterImageBorderRepresentation &&
421 ((FilterImageBorderRepresentation) representation).getDrawableResource() == 0;
424 private boolean isNoneFxFilter(FilterRepresentation representation) {
425 return representation instanceof FilterFxRepresentation &&
426 ((FilterFxRepresentation) representation).getNameResource() == R.string.none;
429 public FilterRepresentation getRepresentation(FilterRepresentation filterRepresentation) {
430 for (int i = 0; i < mFilters.size(); i++) {
431 FilterRepresentation representation = mFilters.elementAt(i);
432 if (sameSerializationName(representation, filterRepresentation)) {
433 return representation;
439 public Bitmap apply(Bitmap original, FilterEnvironment environment) {
440 Bitmap bitmap = original;
441 bitmap = applyFilters(bitmap, -1, -1, environment);
442 return applyBorder(bitmap, environment);
445 public Collection<FilterRepresentation> getGeometryFilters() {
446 ArrayList<FilterRepresentation> geometry = new ArrayList<FilterRepresentation>();
447 for (FilterRepresentation r : mFilters) {
448 if (r.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
455 public FilterRepresentation getFilterWithSerializationName(String serializationName) {
456 for (FilterRepresentation r : mFilters) {
458 if (sameSerializationName(r.getSerializationName(), serializationName)) {
466 public Rect finalGeometryRect(int width, int height) {
467 return GeometryMathUtils.finalGeometryRect(width, height, getGeometryFilters());
470 public Bitmap applyGeometry(Bitmap bitmap, FilterEnvironment environment) {
471 // Apply any transform -- 90 rotate, flip, straighten, crop
472 // Returns a new bitmap.
473 if (mDoApplyGeometry) {
474 Bitmap bmp = GeometryMathUtils.applyGeometryRepresentations(
475 getGeometryFilters(), bitmap);
477 environment.cache(bitmap);
484 public Bitmap applyBorder(Bitmap bitmap, FilterEnvironment environment) {
485 // get the border from the list of filters.
486 FilterRepresentation border = getFilterRepresentationForType(
487 FilterRepresentation.TYPE_BORDER);
488 if (border != null && mDoApplyGeometry) {
489 bitmap = environment.applyRepresentation(border, bitmap);
490 if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) {
496 public int nbFilters() {
497 return mFilters.size();
500 public Bitmap applyFilters(Bitmap bitmap, int from, int to, FilterEnvironment environment) {
501 if (mDoApplyFilters) {
506 to = mFilters.size();
508 for (int i = from; i < to; i++) {
509 FilterRepresentation representation = mFilters.elementAt(i);
510 if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
511 // skip the geometry as it's already applied.
514 if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
515 // for now, let's skip the border as it will be applied in
517 // TODO: might be worth getting rid of applyBorder.
521 bitmap = environment.applyRepresentation(representation, bitmap);
523 environment.cache(tmp);
525 if (environment.needsStop()) {
534 public void applyBorder(Allocation in, Allocation out,
535 boolean copyOut, FilterEnvironment environment) {
536 FilterRepresentation border = getFilterRepresentationForType(
537 FilterRepresentation.TYPE_BORDER);
538 if (border != null && mDoApplyGeometry) {
539 // TODO: should keep the bitmap around
540 Allocation bitmapIn = in;
542 bitmapIn = Allocation.createTyped(
543 CachingPipeline.getRenderScriptContext(), in.getType());
544 bitmapIn.copyFrom(out);
546 environment.applyRepresentation(border, bitmapIn, out);
550 public void applyFilters(int from, int to, Allocation in, Allocation out,
551 FilterEnvironment environment) {
552 if (mDoApplyFilters) {
557 to = mFilters.size();
559 for (int i = from; i < to; i++) {
560 FilterRepresentation representation = mFilters.elementAt(i);
561 if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY
562 || representation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
568 environment.applyRepresentation(representation, in, out);
573 public boolean canDoPartialRendering() {
574 if (MasterImage.getImage().getZoomOrientation() != ImageLoader.ORI_NORMAL) {
577 for (int i = 0; i < mFilters.size(); i++) {
578 FilterRepresentation representation = mFilters.elementAt(i);
579 if (!representation.supportsPartialRendering()) {
586 public void fillImageStateAdapter(StateAdapter imageStateAdapter) {
587 if (imageStateAdapter == null) {
590 Vector<State> states = new Vector<State>();
591 for (FilterRepresentation filter : mFilters) {
592 if (filter instanceof FilterUserPresetRepresentation) {
593 // do not show the user preset itself in the state panel
596 State state = new State(filter.getName());
597 state.setFilterRepresentation(filter);
600 imageStateAdapter.fill(states);
603 public void setPartialRendering(boolean partialRendering, Rect bounds) {
604 mPartialRendering = partialRendering;
605 mPartialRenderingBounds = bounds;
608 public boolean isPartialRendering() {
609 return mPartialRendering;
612 public Rect getPartialRenderingBounds() {
613 return mPartialRenderingBounds;
616 public Vector<ImageFilter> getUsedFilters(BaseFiltersManager filtersManager) {
617 Vector<ImageFilter> usedFilters = new Vector<ImageFilter>();
618 for (int i = 0; i < mFilters.size(); i++) {
619 FilterRepresentation representation = mFilters.elementAt(i);
620 ImageFilter filter = filtersManager.getFilterForRepresentation(representation);
621 usedFilters.add(filter);
626 public String getJsonString(String name) {
627 StringWriter swriter = new StringWriter();
629 JsonWriter writer = new JsonWriter(swriter);
630 writeJson(writer, name);
632 } catch (IOException e) {
635 return swriter.toString();
638 public void writeJson(JsonWriter writer, String name) {
639 int numFilters = mFilters.size();
641 writer.beginObject();
642 for (int i = 0; i < numFilters; i++) {
643 FilterRepresentation filter = mFilters.get(i);
644 if (filter instanceof FilterUserPresetRepresentation) {
647 String sname = filter.getSerializationName();
649 Log.v(LOGTAG, "Serialization: " + sname);
651 Log.v(LOGTAG, "Serialization name null for filter: " + filter);
655 filter.serializeRepresentation(writer);
659 } catch (IOException e) {
660 Log.e(LOGTAG,"Error encoding JASON",e);
665 * populates preset from JSON string
667 * @param filterString a JSON string
668 * @return true on success if false ImagePreset is undefined
670 public boolean readJsonFromString(String filterString) {
672 Log.v(LOGTAG, "reading preset: \"" + filterString + "\"");
674 StringReader sreader = new StringReader(filterString);
676 JsonReader reader = new JsonReader(sreader);
677 boolean ok = readJson(reader);
683 } catch (Exception e) {
684 Log.e(LOGTAG, "\""+filterString+"\"");
685 Log.e(LOGTAG, "parsing the filter parameters:", e);
692 * populates preset from JSON stream
694 * @param sreader a JSON string
695 * @return true on success if false ImagePreset is undefined
697 public boolean readJson(JsonReader sreader) throws IOException {
698 sreader.beginObject();
700 while (sreader.hasNext()) {
701 String name = sreader.nextName();
702 FilterRepresentation filter = creatFilterFromName(name);
703 if (filter == null) {
704 Log.w(LOGTAG, "UNKNOWN FILTER! " + name);
707 filter.deSerializeRepresentation(sreader);
714 FilterRepresentation creatFilterFromName(String name) {
715 if (FilterRotateRepresentation.SERIALIZATION_NAME.equals(name)) {
716 return new FilterRotateRepresentation();
717 } else if (FilterMirrorRepresentation.SERIALIZATION_NAME.equals(name)) {
718 return new FilterMirrorRepresentation();
719 } else if (FilterStraightenRepresentation.SERIALIZATION_NAME.equals(name)) {
720 return new FilterStraightenRepresentation();
721 } else if (FilterCropRepresentation.SERIALIZATION_NAME.equals(name)) {
722 return new FilterCropRepresentation();
724 FiltersManager filtersManager = FiltersManager.getManager();
725 return filtersManager.createFilterFromName(name);
728 public void updateWith(ImagePreset preset) {
729 if (preset.mFilters.size() != mFilters.size()) {
730 Log.e(LOGTAG, "Updating a preset with an incompatible one");
733 for (int i = 0; i < mFilters.size(); i++) {
734 FilterRepresentation destRepresentation = mFilters.elementAt(i);
735 FilterRepresentation sourceRepresentation = preset.mFilters.elementAt(i);
736 destRepresentation.useParametersFrom(sourceRepresentation);