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.ImageLoader;
28 import com.android.gallery3d.filtershow.filters.BaseFiltersManager;
29 import com.android.gallery3d.filtershow.filters.FilterCropRepresentation;
30 import com.android.gallery3d.filtershow.filters.FilterFxRepresentation;
31 import com.android.gallery3d.filtershow.filters.FilterImageBorderRepresentation;
32 import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation;
33 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
34 import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation;
35 import com.android.gallery3d.filtershow.filters.FilterStraightenRepresentation;
36 import com.android.gallery3d.filtershow.filters.FilterUserPresetRepresentation;
37 import com.android.gallery3d.filtershow.filters.FiltersManager;
38 import com.android.gallery3d.filtershow.filters.ImageFilter;
39 import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils;
40 import com.android.gallery3d.filtershow.imageshow.MasterImage;
41 import com.android.gallery3d.filtershow.state.State;
42 import com.android.gallery3d.filtershow.state.StateAdapter;
43 import com.android.gallery3d.util.UsageStatistics;
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";
56 private Vector<FilterRepresentation> mFilters = new Vector<FilterRepresentation>();
58 private boolean mDoApplyGeometry = true;
59 private boolean mDoApplyFilters = true;
61 private boolean mPartialRendering = false;
62 private Rect mPartialRenderingBounds;
63 private static final boolean DEBUG = false;
65 public ImagePreset() {
68 public ImagePreset(ImagePreset source) {
69 for (int i = 0; i < source.mFilters.size(); i++) {
70 FilterRepresentation sourceRepresentation = source.mFilters.elementAt(i);
71 mFilters.add(sourceRepresentation.copy());
75 public Vector<FilterRepresentation> getFilters() {
79 public FilterRepresentation getFilterRepresentation(int position) {
80 FilterRepresentation representation = null;
82 representation = mFilters.elementAt(position).copy();
84 return representation;
87 private static boolean sameSerializationName(String a, String b) {
88 if (a != null && b != null) {
91 return a == null && b == null;
95 public static boolean sameSerializationName(FilterRepresentation a, FilterRepresentation b) {
96 if (a == null || b == null) {
99 return sameSerializationName(a.getSerializationName(), b.getSerializationName());
102 public int getPositionForRepresentation(FilterRepresentation representation) {
103 for (int i = 0; i < mFilters.size(); i++) {
104 if (sameSerializationName(mFilters.elementAt(i), representation)) {
111 private FilterRepresentation getFilterRepresentationForType(int type) {
112 for (int i = 0; i < mFilters.size(); i++) {
113 if (mFilters.elementAt(i).getFilterType() == type) {
114 return mFilters.elementAt(i);
120 public int getPositionForType(int type) {
121 for (int i = 0; i < mFilters.size(); i++) {
122 if (mFilters.elementAt(i).getFilterType() == type) {
129 public FilterRepresentation getFilterRepresentationCopyFrom(
130 FilterRepresentation filterRepresentation) {
131 // TODO: add concept of position in the filters (to allow multiple instances)
132 if (filterRepresentation == null) {
135 int position = getPositionForRepresentation(filterRepresentation);
136 if (position == -1) {
139 FilterRepresentation representation = mFilters.elementAt(position);
140 if (representation != null) {
141 representation = representation.copy();
143 return representation;
146 public void updateFilterRepresentations(Collection<FilterRepresentation> reps) {
147 for (FilterRepresentation r : reps) {
148 updateOrAddFilterRepresentation(r);
152 public void updateOrAddFilterRepresentation(FilterRepresentation rep) {
153 int pos = getPositionForRepresentation(rep);
155 mFilters.elementAt(pos).useParametersFrom(rep);
157 addFilter(rep.copy());
161 public void setDoApplyGeometry(boolean value) {
162 mDoApplyGeometry = value;
165 public void setDoApplyFilters(boolean value) {
166 mDoApplyFilters = value;
169 public boolean getDoApplyFilters() {
170 return mDoApplyFilters;
173 public boolean hasModifications() {
174 for (int i = 0; i < mFilters.size(); i++) {
175 FilterRepresentation filter = mFilters.elementAt(i);
176 if (!filter.isNil()) {
183 public boolean isPanoramaSafe() {
184 for (FilterRepresentation representation : mFilters) {
185 if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY
186 && !representation.isNil()) {
189 if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER
190 && !representation.isNil()) {
193 if (representation.getFilterType() == FilterRepresentation.TYPE_VIGNETTE
194 && !representation.isNil()) {
197 if (representation.getFilterType() == FilterRepresentation.TYPE_TINYPLANET
198 && !representation.isNil()) {
205 public boolean same(ImagePreset preset) {
206 if (preset == null) {
210 if (preset.mFilters.size() != mFilters.size()) {
214 if (mDoApplyGeometry != preset.mDoApplyGeometry) {
218 if (mDoApplyFilters != preset.mDoApplyFilters) {
219 if (mFilters.size() > 0 || preset.mFilters.size() > 0) {
224 if (mDoApplyFilters && preset.mDoApplyFilters) {
225 for (int i = 0; i < preset.mFilters.size(); i++) {
226 FilterRepresentation a = preset.mFilters.elementAt(i);
227 FilterRepresentation b = mFilters.elementAt(i);
238 public int similarUpTo(ImagePreset preset) {
239 for (int i = 0; i < preset.mFilters.size(); i++) {
240 FilterRepresentation a = preset.mFilters.elementAt(i);
241 if (i < mFilters.size()) {
242 FilterRepresentation b = mFilters.elementAt(i);
253 return preset.mFilters.size();
256 public void showFilters() {
257 Log.v(LOGTAG, "\\\\\\ showFilters -- " + mFilters.size() + " filters");
259 for (FilterRepresentation representation : mFilters) {
260 Log.v(LOGTAG, " filter " + n + " : " + representation.toString());
263 Log.v(LOGTAG, "/// showFilters -- " + mFilters.size() + " filters");
266 public FilterRepresentation getLastRepresentation() {
267 if (mFilters.size() > 0) {
268 return mFilters.lastElement();
273 public void removeFilter(FilterRepresentation filterRepresentation) {
274 if (filterRepresentation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
275 for (int i = 0; i < mFilters.size(); i++) {
276 if (mFilters.elementAt(i).getFilterType()
277 == filterRepresentation.getFilterType()) {
283 for (int i = 0; i < mFilters.size(); i++) {
284 if (sameSerializationName(mFilters.elementAt(i), filterRepresentation)) {
292 // If the filter is an "None" effect or border, then just don't add this filter.
293 public void addFilter(FilterRepresentation representation) {
294 if (representation instanceof FilterUserPresetRepresentation) {
295 ImagePreset preset = ((FilterUserPresetRepresentation) representation).getImagePreset();
296 // user preset replace everything but geometry
298 for (int i = 0; i < preset.nbFilters(); i++) {
299 addFilter(preset.getFilterRepresentation(i));
301 mFilters.add(representation);
302 } else if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
303 // Add geometry filter, removing duplicates and do-nothing operations.
304 for (int i = 0; i < mFilters.size(); i++) {
305 if (sameSerializationName(representation, mFilters.elementAt(i))) {
309 if (!representation.isNil()) {
310 mFilters.add(representation);
312 } else if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
313 removeFilter(representation);
314 if (!isNoneBorderFilter(representation)) {
315 mFilters.add(representation);
317 } else if (representation.getFilterType() == FilterRepresentation.TYPE_FX) {
318 boolean found = false;
319 for (int i = 0; i < mFilters.size(); i++) {
320 FilterRepresentation current = mFilters.elementAt(i);
321 int type = current.getFilterType();
323 if (type != FilterRepresentation.TYPE_VIGNETTE) {
328 if (type == FilterRepresentation.TYPE_FX) {
329 if (current instanceof FilterUserPresetRepresentation) {
330 ImagePreset preset = ((FilterUserPresetRepresentation) current)
332 // If we had an existing user preset, let's remove all the presets that
334 for (int j = 0; j < preset.nbFilters(); j++) {
335 FilterRepresentation rep = preset.getFilterRepresentation(j);
336 int pos = getPositionForRepresentation(rep);
338 mFilters.remove(pos);
341 int pos = getPositionForRepresentation(current);
343 mFilters.remove(pos);
347 if (!isNoneFxFilter(representation)) {
348 mFilters.add(pos, representation);
353 if (!isNoneFxFilter(representation)) {
354 mFilters.add(i, representation);
361 if (!isNoneFxFilter(representation)) {
362 mFilters.add(representation);
366 mFilters.add(representation);
370 private boolean isNoneBorderFilter(FilterRepresentation representation) {
371 return representation instanceof FilterImageBorderRepresentation &&
372 ((FilterImageBorderRepresentation) representation).getDrawableResource() == 0;
375 private boolean isNoneFxFilter(FilterRepresentation representation) {
376 return representation instanceof FilterFxRepresentation &&
377 ((FilterFxRepresentation) representation).getNameResource() == R.string.none;
380 public FilterRepresentation getRepresentation(FilterRepresentation filterRepresentation) {
381 for (int i = 0; i < mFilters.size(); i++) {
382 FilterRepresentation representation = mFilters.elementAt(i);
383 if (sameSerializationName(representation, filterRepresentation)) {
384 return representation;
390 public Bitmap apply(Bitmap original, FilterEnvironment environment) {
391 Bitmap bitmap = original;
392 bitmap = applyFilters(bitmap, -1, -1, environment);
393 return applyBorder(bitmap, environment);
396 public Collection<FilterRepresentation> getGeometryFilters() {
397 ArrayList<FilterRepresentation> geometry = new ArrayList<FilterRepresentation>();
398 for (FilterRepresentation r : mFilters) {
399 if (r.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
406 public FilterRepresentation getFilterWithSerializationName(String serializationName) {
407 for (FilterRepresentation r : mFilters) {
409 if (sameSerializationName(r.getSerializationName(), serializationName)) {
417 public Bitmap applyGeometry(Bitmap bitmap, FilterEnvironment environment) {
418 // Apply any transform -- 90 rotate, flip, straighten, crop
419 // Returns a new bitmap.
420 if (mDoApplyGeometry) {
421 bitmap = GeometryMathUtils.applyGeometryRepresentations(getGeometryFilters(), bitmap);
426 public Bitmap applyBorder(Bitmap bitmap, FilterEnvironment environment) {
427 // get the border from the list of filters.
428 FilterRepresentation border = getFilterRepresentationForType(
429 FilterRepresentation.TYPE_BORDER);
430 if (border != null && mDoApplyGeometry) {
431 bitmap = environment.applyRepresentation(border, bitmap);
432 if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) {
433 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
434 "SaveBorder", border.getSerializationName(), 1);
440 public int nbFilters() {
441 return mFilters.size();
444 public Bitmap applyFilters(Bitmap bitmap, int from, int to, FilterEnvironment environment) {
445 if (mDoApplyFilters) {
450 to = mFilters.size();
452 if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) {
453 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
454 "SaveFilters", "Total", to - from + 1);
456 for (int i = from; i < to; i++) {
457 FilterRepresentation representation = mFilters.elementAt(i);
458 if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
459 // skip the geometry as it's already applied.
462 if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
463 // for now, let's skip the border as it will be applied in
465 // TODO: might be worth getting rid of applyBorder.
468 bitmap = environment.applyRepresentation(representation, bitmap);
469 if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) {
470 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
471 "SaveFilter", representation.getSerializationName(), 1);
473 if (environment.needsStop()) {
482 public void applyBorder(Allocation in, Allocation out,
483 boolean copyOut, FilterEnvironment environment) {
484 FilterRepresentation border = getFilterRepresentationForType(
485 FilterRepresentation.TYPE_BORDER);
486 if (border != null && mDoApplyGeometry) {
487 // TODO: should keep the bitmap around
488 Allocation bitmapIn = in;
490 bitmapIn = Allocation.createTyped(
491 CachingPipeline.getRenderScriptContext(), in.getType());
492 bitmapIn.copyFrom(out);
494 environment.applyRepresentation(border, bitmapIn, out);
498 public void applyFilters(int from, int to, Allocation in, Allocation out,
499 FilterEnvironment environment) {
500 if (mDoApplyFilters) {
505 to = mFilters.size();
507 for (int i = from; i < to; i++) {
508 FilterRepresentation representation = mFilters.elementAt(i);
509 if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY
510 || representation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
516 environment.applyRepresentation(representation, in, out);
521 public boolean canDoPartialRendering() {
522 if (MasterImage.getImage().getZoomOrientation() != ImageLoader.ORI_NORMAL) {
525 for (int i = 0; i < mFilters.size(); i++) {
526 FilterRepresentation representation = mFilters.elementAt(i);
527 if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY
528 && !representation.isNil()) {
531 if (!representation.supportsPartialRendering()) {
538 public void fillImageStateAdapter(StateAdapter imageStateAdapter) {
539 if (imageStateAdapter == null) {
542 Vector<State> states = new Vector<State>();
543 for (FilterRepresentation filter : mFilters) {
544 if (filter.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
545 // TODO: supports Geometry representations in the state panel.
548 if (filter instanceof FilterUserPresetRepresentation) {
549 // do not show the user preset itself in the state panel
552 State state = new State(filter.getName());
553 state.setFilterRepresentation(filter);
556 imageStateAdapter.fill(states);
559 public void setPartialRendering(boolean partialRendering, Rect bounds) {
560 mPartialRendering = partialRendering;
561 mPartialRenderingBounds = bounds;
564 public boolean isPartialRendering() {
565 return mPartialRendering;
568 public Rect getPartialRenderingBounds() {
569 return mPartialRenderingBounds;
572 public Vector<ImageFilter> getUsedFilters(BaseFiltersManager filtersManager) {
573 Vector<ImageFilter> usedFilters = new Vector<ImageFilter>();
574 for (int i = 0; i < mFilters.size(); i++) {
575 FilterRepresentation representation = mFilters.elementAt(i);
576 ImageFilter filter = filtersManager.getFilterForRepresentation(representation);
577 usedFilters.add(filter);
582 public String getJsonString(String name) {
583 StringWriter swriter = new StringWriter();
585 JsonWriter writer = new JsonWriter(swriter);
586 writeJson(writer, name);
588 } catch (IOException e) {
591 return swriter.toString();
594 public void writeJson(JsonWriter writer, String name) {
595 int numFilters = mFilters.size();
597 writer.beginObject();
598 for (int i = 0; i < numFilters; i++) {
599 FilterRepresentation filter = mFilters.get(i);
600 if (filter instanceof FilterUserPresetRepresentation) {
603 String sname = filter.getSerializationName();
605 Log.v(LOGTAG, "Serialization: " + sname);
607 Log.v(LOGTAG, "Serialization name null for filter: " + filter);
611 filter.serializeRepresentation(writer);
615 } catch (IOException e) {
621 * populates preset from JSON string
623 * @param filterString a JSON string
624 * @return true on success if false ImagePreset is undefined
626 public boolean readJsonFromString(String filterString) {
628 Log.v(LOGTAG, "reading preset: \"" + filterString + "\"");
630 StringReader sreader = new StringReader(filterString);
632 JsonReader reader = new JsonReader(sreader);
633 boolean ok = readJson(reader);
639 } catch (Exception e) {
640 Log.e(LOGTAG, "parsing the filter parameters:", e);
647 * populates preset from JSON stream
649 * @param sreader a JSON string
650 * @return true on success if false ImagePreset is undefined
652 public boolean readJson(JsonReader sreader) throws IOException {
653 sreader.beginObject();
655 while (sreader.hasNext()) {
656 String name = sreader.nextName();
657 FilterRepresentation filter = creatFilterFromName(name);
658 if (filter == null) {
659 Log.w(LOGTAG, "UNKNOWN FILTER! " + name);
662 filter.deSerializeRepresentation(sreader);
669 FilterRepresentation creatFilterFromName(String name) {
670 if (FilterRotateRepresentation.SERIALIZATION_NAME.equals(name)) {
671 return new FilterRotateRepresentation();
672 } else if (FilterMirrorRepresentation.SERIALIZATION_NAME.equals(name)) {
673 return new FilterMirrorRepresentation();
674 } else if (FilterStraightenRepresentation.SERIALIZATION_NAME.equals(name)) {
675 return new FilterStraightenRepresentation();
676 } else if (FilterCropRepresentation.SERIALIZATION_NAME.equals(name)) {
677 return new FilterCropRepresentation();
679 FiltersManager filtersManager = FiltersManager.getManager();
680 return filtersManager.createFilterFromName(name);
683 public void updateWith(ImagePreset preset) {
684 if (preset.mFilters.size() != mFilters.size()) {
685 Log.e(LOGTAG, "Updating a preset with an incompatible one");
688 for (int i = 0; i < mFilters.size(); i++) {
689 FilterRepresentation destRepresentation = mFilters.elementAt(i);
690 FilterRepresentation sourceRepresentation = preset.mFilters.elementAt(i);
691 destRepresentation.useParametersFrom(sourceRepresentation);