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 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 // user preset replace everything but geometry
352 for (int i = 0; i < preset.nbFilters(); i++) {
353 addFilter(preset.getFilterRepresentation(i));
355 mFilters.add(representation);
356 } else if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
357 // Add geometry filter, removing duplicates and do-nothing operations.
358 for (int i = 0; i < mFilters.size(); i++) {
359 if (sameSerializationName(representation, mFilters.elementAt(i))) {
364 for (; index < mFilters.size(); index++) {
365 FilterRepresentation rep = mFilters.elementAt(index);
366 if (rep.getFilterType() != FilterRepresentation.TYPE_GEOMETRY) {
370 if (!representation.isNil()) {
371 mFilters.insertElementAt(representation, index);
373 } else if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
374 removeFilter(representation);
375 if (!isNoneBorderFilter(representation)) {
376 mFilters.add(representation);
378 } else if (representation.getFilterType() == FilterRepresentation.TYPE_FX) {
379 boolean found = false;
380 for (int i = 0; i < mFilters.size(); i++) {
381 FilterRepresentation current = mFilters.elementAt(i);
382 int type = current.getFilterType();
384 if (type != FilterRepresentation.TYPE_VIGNETTE) {
389 if (type == FilterRepresentation.TYPE_FX) {
390 if (current instanceof FilterUserPresetRepresentation) {
391 ImagePreset preset = ((FilterUserPresetRepresentation) current)
393 // If we had an existing user preset, let's remove all the presets that
395 for (int j = 0; j < preset.nbFilters(); j++) {
396 FilterRepresentation rep = preset.getFilterRepresentation(j);
397 int pos = getPositionForRepresentation(rep);
399 mFilters.remove(pos);
402 int pos = getPositionForRepresentation(current);
404 mFilters.remove(pos);
408 if (!isNoneFxFilter(representation)) {
409 mFilters.add(pos, representation);
414 if (!isNoneFxFilter(representation)) {
415 mFilters.add(i, representation);
422 if (!isNoneFxFilter(representation)) {
423 mFilters.add(representation);
427 mFilters.add(representation);
431 private boolean isNoneBorderFilter(FilterRepresentation representation) {
432 return representation instanceof FilterImageBorderRepresentation &&
433 ((FilterImageBorderRepresentation) representation).getDrawableResource() == 0;
436 private boolean isNoneFxFilter(FilterRepresentation representation) {
437 return representation instanceof FilterFxRepresentation &&
438 ((FilterFxRepresentation) representation).getNameResource() == R.string.none;
441 public FilterRepresentation getRepresentation(FilterRepresentation filterRepresentation) {
442 for (int i = 0; i < mFilters.size(); i++) {
443 FilterRepresentation representation = mFilters.elementAt(i);
444 if (sameSerializationName(representation, filterRepresentation)) {
445 return representation;
451 public Bitmap apply(Bitmap original, FilterEnvironment environment) {
452 Bitmap bitmap = original;
453 bitmap = applyFilters(bitmap, -1, -1, environment);
454 return applyBorder(bitmap, environment);
457 public Collection<FilterRepresentation> getGeometryFilters() {
458 ArrayList<FilterRepresentation> geometry = new ArrayList<FilterRepresentation>();
459 for (FilterRepresentation r : mFilters) {
460 if (r.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
467 public FilterRepresentation getFilterWithSerializationName(String serializationName) {
468 for (FilterRepresentation r : mFilters) {
470 if (sameSerializationName(r.getSerializationName(), serializationName)) {
478 public Rect finalGeometryRect(int width, int height) {
479 return GeometryMathUtils.finalGeometryRect(width, height, getGeometryFilters());
482 public Bitmap applyGeometry(Bitmap bitmap, FilterEnvironment environment) {
483 // Apply any transform -- 90 rotate, flip, straighten, crop
484 // Returns a new bitmap.
485 if (mDoApplyGeometry) {
486 Bitmap bmp = GeometryMathUtils.applyGeometryRepresentations(
487 getGeometryFilters(), bitmap);
489 environment.cache(bitmap);
496 public Bitmap applyBorder(Bitmap bitmap, FilterEnvironment environment) {
497 // get the border from the list of filters.
498 FilterRepresentation border = getFilterRepresentationForType(
499 FilterRepresentation.TYPE_BORDER);
500 if (border != null && mDoApplyGeometry) {
501 bitmap = environment.applyRepresentation(border, bitmap);
502 if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) {
503 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
504 "SaveBorder", border.getSerializationName(), 1);
510 public int nbFilters() {
511 return mFilters.size();
514 public Bitmap applyFilters(Bitmap bitmap, int from, int to, FilterEnvironment environment) {
515 if (mDoApplyFilters) {
520 to = mFilters.size();
522 if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) {
523 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
524 "SaveFilters", "Total", to - from + 1);
526 for (int i = from; i < to; i++) {
527 FilterRepresentation representation = mFilters.elementAt(i);
528 if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
529 // skip the geometry as it's already applied.
532 if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
533 // for now, let's skip the border as it will be applied in
535 // TODO: might be worth getting rid of applyBorder.
539 bitmap = environment.applyRepresentation(representation, bitmap);
541 environment.cache(tmp);
543 if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) {
544 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
545 "SaveFilter", representation.getSerializationName(), 1);
547 if (environment.needsStop()) {
556 public void applyBorder(Allocation in, Allocation out,
557 boolean copyOut, FilterEnvironment environment) {
558 FilterRepresentation border = getFilterRepresentationForType(
559 FilterRepresentation.TYPE_BORDER);
560 if (border != null && mDoApplyGeometry) {
561 // TODO: should keep the bitmap around
562 Allocation bitmapIn = in;
564 bitmapIn = Allocation.createTyped(
565 CachingPipeline.getRenderScriptContext(), in.getType());
566 bitmapIn.copyFrom(out);
568 environment.applyRepresentation(border, bitmapIn, out);
572 public void applyFilters(int from, int to, Allocation in, Allocation out,
573 FilterEnvironment environment) {
574 if (mDoApplyFilters) {
579 to = mFilters.size();
581 for (int i = from; i < to; i++) {
582 FilterRepresentation representation = mFilters.elementAt(i);
583 if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY
584 || representation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
590 environment.applyRepresentation(representation, in, out);
595 public boolean canDoPartialRendering() {
596 if (MasterImage.getImage().getZoomOrientation() != ImageLoader.ORI_NORMAL) {
599 for (int i = 0; i < mFilters.size(); i++) {
600 FilterRepresentation representation = mFilters.elementAt(i);
601 if (!representation.supportsPartialRendering()) {
608 public void fillImageStateAdapter(StateAdapter imageStateAdapter) {
609 if (imageStateAdapter == null) {
612 Vector<State> states = new Vector<State>();
613 for (FilterRepresentation filter : mFilters) {
614 if (filter instanceof FilterUserPresetRepresentation) {
615 // do not show the user preset itself in the state panel
618 State state = new State(filter.getName());
619 state.setFilterRepresentation(filter);
622 imageStateAdapter.fill(states);
625 public void setPartialRendering(boolean partialRendering, Rect bounds) {
626 mPartialRendering = partialRendering;
627 mPartialRenderingBounds = bounds;
630 public boolean isPartialRendering() {
631 return mPartialRendering;
634 public Rect getPartialRenderingBounds() {
635 return mPartialRenderingBounds;
638 public Vector<ImageFilter> getUsedFilters(BaseFiltersManager filtersManager) {
639 Vector<ImageFilter> usedFilters = new Vector<ImageFilter>();
640 for (int i = 0; i < mFilters.size(); i++) {
641 FilterRepresentation representation = mFilters.elementAt(i);
642 ImageFilter filter = filtersManager.getFilterForRepresentation(representation);
643 usedFilters.add(filter);
648 public String getJsonString(String name) {
649 StringWriter swriter = new StringWriter();
651 JsonWriter writer = new JsonWriter(swriter);
652 writeJson(writer, name);
654 } catch (IOException e) {
657 return swriter.toString();
660 public void writeJson(JsonWriter writer, String name) {
661 int numFilters = mFilters.size();
663 writer.beginObject();
664 for (int i = 0; i < numFilters; i++) {
665 FilterRepresentation filter = mFilters.get(i);
666 if (filter instanceof FilterUserPresetRepresentation) {
669 String sname = filter.getSerializationName();
671 Log.v(LOGTAG, "Serialization: " + sname);
673 Log.v(LOGTAG, "Serialization name null for filter: " + filter);
677 filter.serializeRepresentation(writer);
681 } catch (IOException e) {
682 Log.e(LOGTAG,"Error encoding JASON",e);
687 * populates preset from JSON string
689 * @param filterString a JSON string
690 * @return true on success if false ImagePreset is undefined
692 public boolean readJsonFromString(String filterString) {
694 Log.v(LOGTAG, "reading preset: \"" + filterString + "\"");
696 StringReader sreader = new StringReader(filterString);
698 JsonReader reader = new JsonReader(sreader);
699 boolean ok = readJson(reader);
705 } catch (Exception e) {
706 Log.e(LOGTAG, "parsing the filter parameters:", e);
713 * populates preset from JSON stream
715 * @param sreader a JSON string
716 * @return true on success if false ImagePreset is undefined
718 public boolean readJson(JsonReader sreader) throws IOException {
719 sreader.beginObject();
721 while (sreader.hasNext()) {
722 String name = sreader.nextName();
723 FilterRepresentation filter = creatFilterFromName(name);
724 if (filter == null) {
725 Log.w(LOGTAG, "UNKNOWN FILTER! " + name);
728 filter.deSerializeRepresentation(sreader);
735 FilterRepresentation creatFilterFromName(String name) {
736 if (FilterRotateRepresentation.SERIALIZATION_NAME.equals(name)) {
737 return new FilterRotateRepresentation();
738 } else if (FilterMirrorRepresentation.SERIALIZATION_NAME.equals(name)) {
739 return new FilterMirrorRepresentation();
740 } else if (FilterStraightenRepresentation.SERIALIZATION_NAME.equals(name)) {
741 return new FilterStraightenRepresentation();
742 } else if (FilterCropRepresentation.SERIALIZATION_NAME.equals(name)) {
743 return new FilterCropRepresentation();
745 FiltersManager filtersManager = FiltersManager.getManager();
746 return filtersManager.createFilterFromName(name);
749 public void updateWith(ImagePreset preset) {
750 if (preset.mFilters.size() != mFilters.size()) {
751 Log.e(LOGTAG, "Updating a preset with an incompatible one");
754 for (int i = 0; i < mFilters.size(); i++) {
755 FilterRepresentation destRepresentation = mFilters.elementAt(i);
756 FilterRepresentation sourceRepresentation = preset.mFilters.elementAt(i);
757 destRepresentation.useParametersFrom(sourceRepresentation);