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.support.v8.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;
44 import com.android.gallery3d.util.UsageStatistics;
46 import java.io.IOException;
47 import java.io.StringReader;
48 import java.io.StringWriter;
49 import java.util.ArrayList;
50 import java.util.Collection;
51 import java.util.Vector;
53 public class ImagePreset {
55 private static final String LOGTAG = "ImagePreset";
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 isPanoramaSafe() {
185 for (FilterRepresentation representation : mFilters) {
186 if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY
187 && !representation.isNil()) {
190 if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER
191 && !representation.isNil()) {
194 if (representation.getFilterType() == FilterRepresentation.TYPE_VIGNETTE
195 && !representation.isNil()) {
198 if (representation.getFilterType() == FilterRepresentation.TYPE_TINYPLANET
199 && !representation.isNil()) {
206 public boolean same(ImagePreset preset) {
207 if (preset == null) {
211 if (preset.mFilters.size() != mFilters.size()) {
215 if (mDoApplyGeometry != preset.mDoApplyGeometry) {
219 if (mDoApplyFilters != preset.mDoApplyFilters) {
220 if (mFilters.size() > 0 || preset.mFilters.size() > 0) {
225 if (mDoApplyFilters && preset.mDoApplyFilters) {
226 for (int i = 0; i < preset.mFilters.size(); i++) {
227 FilterRepresentation a = preset.mFilters.elementAt(i);
228 FilterRepresentation b = mFilters.elementAt(i);
239 public boolean equals(ImagePreset preset) {
240 if (preset == null) {
244 if (preset.mFilters.size() != mFilters.size()) {
248 if (mDoApplyGeometry != preset.mDoApplyGeometry) {
252 if (mDoApplyFilters != preset.mDoApplyFilters) {
253 if (mFilters.size() > 0 || preset.mFilters.size() > 0) {
258 for (int i = 0; i < preset.mFilters.size(); i++) {
259 FilterRepresentation a = preset.mFilters.elementAt(i);
260 FilterRepresentation b = mFilters.elementAt(i);
270 public int similarUpTo(ImagePreset preset) {
271 for (int i = 0; i < preset.mFilters.size(); i++) {
272 FilterRepresentation a = preset.mFilters.elementAt(i);
273 if (i < mFilters.size()) {
274 FilterRepresentation b = mFilters.elementAt(i);
285 return preset.mFilters.size();
288 public void showFilters() {
289 Log.v(LOGTAG, "\\\\\\ showFilters -- " + mFilters.size() + " filters");
291 for (FilterRepresentation representation : mFilters) {
292 Log.v(LOGTAG, " filter " + n + " : " + representation.toString());
295 Log.v(LOGTAG, "/// showFilters -- " + mFilters.size() + " filters");
298 public FilterRepresentation getLastRepresentation() {
299 if (mFilters.size() > 0) {
300 return mFilters.lastElement();
305 public void removeFilter(FilterRepresentation filterRepresentation) {
306 if (filterRepresentation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
307 for (int i = 0; i < mFilters.size(); i++) {
308 if (mFilters.elementAt(i).getFilterType()
309 == filterRepresentation.getFilterType()) {
315 for (int i = 0; i < mFilters.size(); i++) {
316 if (sameSerializationName(mFilters.elementAt(i), filterRepresentation)) {
324 // If the filter is an "None" effect or border, then just don't add this filter.
325 public void addFilter(FilterRepresentation representation) {
326 if (representation instanceof FilterUserPresetRepresentation) {
327 ImagePreset preset = ((FilterUserPresetRepresentation) representation).getImagePreset();
328 // user preset replace everything but geometry
330 for (int i = 0; i < preset.nbFilters(); i++) {
331 addFilter(preset.getFilterRepresentation(i));
333 mFilters.add(representation);
334 } else if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
335 // Add geometry filter, removing duplicates and do-nothing operations.
336 for (int i = 0; i < mFilters.size(); i++) {
337 if (sameSerializationName(representation, mFilters.elementAt(i))) {
342 for (; index < mFilters.size(); index++) {
343 FilterRepresentation rep = mFilters.elementAt(index);
344 if (rep.getFilterType() != FilterRepresentation.TYPE_GEOMETRY) {
348 if (!representation.isNil()) {
349 mFilters.insertElementAt(representation, index);
351 } else if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
352 removeFilter(representation);
353 if (!isNoneBorderFilter(representation)) {
354 mFilters.add(representation);
356 } else if (representation.getFilterType() == FilterRepresentation.TYPE_FX) {
357 boolean found = false;
358 for (int i = 0; i < mFilters.size(); i++) {
359 FilterRepresentation current = mFilters.elementAt(i);
360 int type = current.getFilterType();
362 if (type != FilterRepresentation.TYPE_VIGNETTE) {
367 if (type == FilterRepresentation.TYPE_FX) {
368 if (current instanceof FilterUserPresetRepresentation) {
369 ImagePreset preset = ((FilterUserPresetRepresentation) current)
371 // If we had an existing user preset, let's remove all the presets that
373 for (int j = 0; j < preset.nbFilters(); j++) {
374 FilterRepresentation rep = preset.getFilterRepresentation(j);
375 int pos = getPositionForRepresentation(rep);
377 mFilters.remove(pos);
380 int pos = getPositionForRepresentation(current);
382 mFilters.remove(pos);
386 if (!isNoneFxFilter(representation)) {
387 mFilters.add(pos, representation);
392 if (!isNoneFxFilter(representation)) {
393 mFilters.add(i, representation);
400 if (!isNoneFxFilter(representation)) {
401 mFilters.add(representation);
405 mFilters.add(representation);
409 private boolean isNoneBorderFilter(FilterRepresentation representation) {
410 return representation instanceof FilterImageBorderRepresentation &&
411 ((FilterImageBorderRepresentation) representation).getDrawableResource() == 0;
414 private boolean isNoneFxFilter(FilterRepresentation representation) {
415 return representation instanceof FilterFxRepresentation &&
416 ((FilterFxRepresentation) representation).getNameResource() == R.string.none;
419 public FilterRepresentation getRepresentation(FilterRepresentation filterRepresentation) {
420 for (int i = 0; i < mFilters.size(); i++) {
421 FilterRepresentation representation = mFilters.elementAt(i);
422 if (sameSerializationName(representation, filterRepresentation)) {
423 return representation;
429 public Bitmap apply(Bitmap original, FilterEnvironment environment) {
430 Bitmap bitmap = original;
431 bitmap = applyFilters(bitmap, -1, -1, environment);
432 return applyBorder(bitmap, environment);
435 public Collection<FilterRepresentation> getGeometryFilters() {
436 ArrayList<FilterRepresentation> geometry = new ArrayList<FilterRepresentation>();
437 for (FilterRepresentation r : mFilters) {
438 if (r.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
445 public FilterRepresentation getFilterWithSerializationName(String serializationName) {
446 for (FilterRepresentation r : mFilters) {
448 if (sameSerializationName(r.getSerializationName(), serializationName)) {
456 public Rect finalGeometryRect(int width, int height) {
457 return GeometryMathUtils.finalGeometryRect(width, height, getGeometryFilters());
460 public Bitmap applyGeometry(Bitmap bitmap, FilterEnvironment environment) {
461 // Apply any transform -- 90 rotate, flip, straighten, crop
462 // Returns a new bitmap.
463 if (mDoApplyGeometry) {
464 bitmap = GeometryMathUtils.applyGeometryRepresentations(getGeometryFilters(), bitmap);
469 public Bitmap applyBorder(Bitmap bitmap, FilterEnvironment environment) {
470 // get the border from the list of filters.
471 FilterRepresentation border = getFilterRepresentationForType(
472 FilterRepresentation.TYPE_BORDER);
473 if (border != null && mDoApplyGeometry) {
474 bitmap = environment.applyRepresentation(border, bitmap);
475 if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) {
476 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
477 "SaveBorder", border.getSerializationName(), 1);
483 public int nbFilters() {
484 return mFilters.size();
487 public Bitmap applyFilters(Bitmap bitmap, int from, int to, FilterEnvironment environment) {
488 if (mDoApplyFilters) {
493 to = mFilters.size();
495 if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) {
496 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
497 "SaveFilters", "Total", to - from + 1);
499 for (int i = from; i < to; i++) {
500 FilterRepresentation representation = mFilters.elementAt(i);
501 if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
502 // skip the geometry as it's already applied.
505 if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
506 // for now, let's skip the border as it will be applied in
508 // TODO: might be worth getting rid of applyBorder.
512 bitmap = environment.applyRepresentation(representation, bitmap);
514 environment.cache(tmp);
516 if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) {
517 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
518 "SaveFilter", representation.getSerializationName(), 1);
520 if (environment.needsStop()) {
529 public void applyBorder(Allocation in, Allocation out,
530 boolean copyOut, FilterEnvironment environment) {
531 FilterRepresentation border = getFilterRepresentationForType(
532 FilterRepresentation.TYPE_BORDER);
533 if (border != null && mDoApplyGeometry) {
534 // TODO: should keep the bitmap around
535 Allocation bitmapIn = in;
537 bitmapIn = Allocation.createTyped(
538 CachingPipeline.getRenderScriptContext(), in.getType());
539 bitmapIn.copyFrom(out);
541 environment.applyRepresentation(border, bitmapIn, out);
545 public void applyFilters(int from, int to, Allocation in, Allocation out,
546 FilterEnvironment environment) {
547 if (mDoApplyFilters) {
552 to = mFilters.size();
554 for (int i = from; i < to; i++) {
555 FilterRepresentation representation = mFilters.elementAt(i);
556 if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY
557 || representation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
563 environment.applyRepresentation(representation, in, out);
568 public boolean canDoPartialRendering() {
569 if (MasterImage.getImage().getZoomOrientation() != ImageLoader.ORI_NORMAL) {
572 for (int i = 0; i < mFilters.size(); i++) {
573 FilterRepresentation representation = mFilters.elementAt(i);
574 if (!representation.supportsPartialRendering()) {
581 public void fillImageStateAdapter(StateAdapter imageStateAdapter) {
582 if (imageStateAdapter == null) {
585 Vector<State> states = new Vector<State>();
586 for (FilterRepresentation filter : mFilters) {
587 if (filter instanceof FilterUserPresetRepresentation) {
588 // do not show the user preset itself in the state panel
591 State state = new State(filter.getName());
592 state.setFilterRepresentation(filter);
595 imageStateAdapter.fill(states);
598 public void setPartialRendering(boolean partialRendering, Rect bounds) {
599 mPartialRendering = partialRendering;
600 mPartialRenderingBounds = bounds;
603 public boolean isPartialRendering() {
604 return mPartialRendering;
607 public Rect getPartialRenderingBounds() {
608 return mPartialRenderingBounds;
611 public Vector<ImageFilter> getUsedFilters(BaseFiltersManager filtersManager) {
612 Vector<ImageFilter> usedFilters = new Vector<ImageFilter>();
613 for (int i = 0; i < mFilters.size(); i++) {
614 FilterRepresentation representation = mFilters.elementAt(i);
615 ImageFilter filter = filtersManager.getFilterForRepresentation(representation);
616 usedFilters.add(filter);
621 public String getJsonString(String name) {
622 StringWriter swriter = new StringWriter();
624 JsonWriter writer = new JsonWriter(swriter);
625 writeJson(writer, name);
627 } catch (IOException e) {
630 return swriter.toString();
633 public void writeJson(JsonWriter writer, String name) {
634 int numFilters = mFilters.size();
636 writer.beginObject();
637 for (int i = 0; i < numFilters; i++) {
638 FilterRepresentation filter = mFilters.get(i);
639 if (filter instanceof FilterUserPresetRepresentation) {
642 String sname = filter.getSerializationName();
644 Log.v(LOGTAG, "Serialization: " + sname);
646 Log.v(LOGTAG, "Serialization name null for filter: " + filter);
650 filter.serializeRepresentation(writer);
654 } catch (IOException e) {
655 Log.e(LOGTAG,"Error encoding JASON",e);
660 * populates preset from JSON string
662 * @param filterString a JSON string
663 * @return true on success if false ImagePreset is undefined
665 public boolean readJsonFromString(String filterString) {
667 Log.v(LOGTAG, "reading preset: \"" + filterString + "\"");
669 StringReader sreader = new StringReader(filterString);
671 JsonReader reader = new JsonReader(sreader);
672 boolean ok = readJson(reader);
678 } catch (Exception e) {
679 Log.e(LOGTAG, "parsing the filter parameters:", e);
686 * populates preset from JSON stream
688 * @param sreader a JSON string
689 * @return true on success if false ImagePreset is undefined
691 public boolean readJson(JsonReader sreader) throws IOException {
692 sreader.beginObject();
694 while (sreader.hasNext()) {
695 String name = sreader.nextName();
696 FilterRepresentation filter = creatFilterFromName(name);
697 if (filter == null) {
698 Log.w(LOGTAG, "UNKNOWN FILTER! " + name);
701 filter.deSerializeRepresentation(sreader);
708 FilterRepresentation creatFilterFromName(String name) {
709 if (FilterRotateRepresentation.SERIALIZATION_NAME.equals(name)) {
710 return new FilterRotateRepresentation();
711 } else if (FilterMirrorRepresentation.SERIALIZATION_NAME.equals(name)) {
712 return new FilterMirrorRepresentation();
713 } else if (FilterStraightenRepresentation.SERIALIZATION_NAME.equals(name)) {
714 return new FilterStraightenRepresentation();
715 } else if (FilterCropRepresentation.SERIALIZATION_NAME.equals(name)) {
716 return new FilterCropRepresentation();
718 FiltersManager filtersManager = FiltersManager.getManager();
719 return filtersManager.createFilterFromName(name);
722 public void updateWith(ImagePreset preset) {
723 if (preset.mFilters.size() != mFilters.size()) {
724 Log.e(LOGTAG, "Updating a preset with an incompatible one");
727 for (int i = 0; i < mFilters.size(); i++) {
728 FilterRepresentation destRepresentation = mFilters.elementAt(i);
729 FilterRepresentation sourceRepresentation = preset.mFilters.elementAt(i);
730 destRepresentation.useParametersFrom(sourceRepresentation);