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.FilterFxRepresentation;
30 import com.android.gallery3d.filtershow.filters.FilterImageBorderRepresentation;
31 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
32 import com.android.gallery3d.filtershow.filters.FiltersManager;
33 import com.android.gallery3d.filtershow.filters.ImageFilter;
34 import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
35 import com.android.gallery3d.filtershow.imageshow.MasterImage;
36 import com.android.gallery3d.filtershow.state.State;
37 import com.android.gallery3d.filtershow.state.StateAdapter;
38 import com.android.gallery3d.util.UsageStatistics;
40 import java.io.IOException;
41 import java.io.StringReader;
42 import java.io.StringWriter;
43 import java.util.Vector;
45 public class ImagePreset {
47 private static final String LOGTAG = "ImagePreset";
49 private Vector<FilterRepresentation> mFilters = new Vector<FilterRepresentation>();
51 protected boolean mIsFxPreset = false;
53 private boolean mDoApplyGeometry = true;
54 private boolean mDoApplyFilters = true;
56 private boolean mPartialRendering = false;
57 private Rect mPartialRenderingBounds;
58 private static final boolean DEBUG = false;
60 public ImagePreset() {
63 public ImagePreset(ImagePreset source) {
65 for (int i = 0; i < source.mFilters.size(); i++) {
66 FilterRepresentation representation = null;
67 FilterRepresentation sourceRepresentation = source.mFilters.elementAt(i);
68 if (sourceRepresentation instanceof GeometryMetadata) {
69 GeometryMetadata geoData = new GeometryMetadata();
70 GeometryMetadata srcGeo = (GeometryMetadata) sourceRepresentation;
72 representation = geoData;
74 // TODO: get rid of clone()...
75 representation = sourceRepresentation.clone();
77 addFilter(representation);
79 } catch (java.lang.CloneNotSupportedException e) {
80 Log.v(LOGTAG, "Exception trying to clone: " + e);
84 public FilterRepresentation getFilterRepresentation(int position) {
85 FilterRepresentation representation = null;
87 representation = mFilters.elementAt(position).clone();
88 } catch (CloneNotSupportedException e) {
91 return representation;
94 public int getPositionForRepresentation(FilterRepresentation representation) {
95 for (int i = 0; i < mFilters.size(); i++) {
96 if (mFilters.elementAt(i).getFilterClass() == representation.getFilterClass()) {
103 private FilterRepresentation getFilterRepresentationForType(int type) {
104 for (int i = 0; i < mFilters.size(); i++) {
105 if (mFilters.elementAt(i).getFilterType() == type) {
106 return mFilters.elementAt(i);
112 public int getPositionForType(int type) {
113 for (int i = 0; i < mFilters.size(); i++) {
114 if (mFilters.elementAt(i).getFilterType() == type) {
121 public FilterRepresentation getFilterRepresentationCopyFrom(FilterRepresentation filterRepresentation) {
122 // TODO: add concept of position in the filters (to allow multiple instances)
123 if (filterRepresentation == null) {
126 int position = getPositionForRepresentation(filterRepresentation);
127 if (position == -1) {
130 FilterRepresentation representation = mFilters.elementAt(position);
131 if (representation != null) {
133 representation = representation.clone();
134 } catch (CloneNotSupportedException e) {
138 return representation;
141 public void updateFilterRepresentation(FilterRepresentation representation) {
142 if (representation == null) {
145 if (representation instanceof GeometryMetadata) {
146 setGeometry((GeometryMetadata) representation);
148 int position = getPositionForRepresentation(representation);
149 if (position == -1) {
152 FilterRepresentation old = mFilters.elementAt(position);
153 old.useParametersFrom(representation);
155 MasterImage.getImage().invalidatePreview();
156 fillImageStateAdapter(MasterImage.getImage().getState());
159 public void setDoApplyGeometry(boolean value) {
160 mDoApplyGeometry = value;
163 public void setDoApplyFilters(boolean value) {
164 mDoApplyFilters = value;
167 public boolean getDoApplyFilters() {
168 return mDoApplyFilters;
171 public GeometryMetadata getGeometry() {
172 for (FilterRepresentation representation : mFilters) {
173 if (representation instanceof GeometryMetadata) {
174 return (GeometryMetadata) representation;
177 GeometryMetadata geo = new GeometryMetadata();
178 mFilters.add(0, geo); // Hard Requirement for now -- Geometry ought to be first.
182 public boolean hasModifications() {
183 for (int i = 0; i < mFilters.size(); i++) {
184 FilterRepresentation filter = mFilters.elementAt(i);
185 if (filter instanceof GeometryMetadata) {
186 if (((GeometryMetadata) filter).hasModifications()) {
189 } else if (!filter.isNil()) {
196 public boolean isPanoramaSafe() {
197 for (FilterRepresentation representation : mFilters) {
198 if (representation instanceof GeometryMetadata) {
199 if (((GeometryMetadata) representation).hasModifications()) {
203 if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER
204 && !representation.isNil()) {
207 if (representation.getFilterType() == FilterRepresentation.TYPE_VIGNETTE
208 && !representation.isNil()) {
211 if (representation.getFilterType() == FilterRepresentation.TYPE_TINYPLANET
212 && !representation.isNil()) {
219 public void setGeometry(GeometryMetadata representation) {
220 GeometryMetadata geoData = getGeometry();
221 if (geoData != representation) {
222 geoData.set(representation);
226 public boolean equals(ImagePreset preset) {
230 if (mDoApplyFilters && preset.mDoApplyFilters) {
231 for (int i = 0; i < preset.mFilters.size(); i++) {
232 FilterRepresentation a = preset.mFilters.elementAt(i);
233 FilterRepresentation b = mFilters.elementAt(i);
242 public boolean same(ImagePreset preset) {
243 if (preset == null) {
247 if (preset.mFilters.size() != mFilters.size()) {
251 if (mDoApplyGeometry != preset.mDoApplyGeometry) {
255 if (mDoApplyGeometry && !getGeometry().equals(preset.getGeometry())) {
259 if (mDoApplyFilters != preset.mDoApplyFilters) {
260 if (mFilters.size() > 0 || preset.mFilters.size() > 0) {
265 if (mDoApplyFilters && preset.mDoApplyFilters) {
266 for (int i = 0; i < preset.mFilters.size(); i++) {
267 FilterRepresentation a = preset.mFilters.elementAt(i);
268 FilterRepresentation b = mFilters.elementAt(i);
269 if (a instanceof GeometryMetadata) {
270 // Note: Geometry will always be at the same place
282 public int similarUpTo(ImagePreset preset) {
283 if (!getGeometry().equals(preset.getGeometry())) {
287 for (int i = 0; i < preset.mFilters.size(); i++) {
288 FilterRepresentation a = preset.mFilters.elementAt(i);
289 if (i < mFilters.size()) {
290 FilterRepresentation b = mFilters.elementAt(i);
301 return preset.mFilters.size();
304 public void showFilters() {
305 Log.v(LOGTAG, "\\\\\\ showFilters -- " + mFilters.size() + " filters");
307 for (FilterRepresentation representation : mFilters) {
308 Log.v(LOGTAG, " filter " + n + " : " + representation.toString());
311 Log.v(LOGTAG, "/// showFilters -- " + mFilters.size() + " filters");
314 public FilterRepresentation getLastRepresentation() {
315 if (mFilters.size() > 0) {
316 return mFilters.lastElement();
321 public void removeFilter(FilterRepresentation filterRepresentation) {
322 if (filterRepresentation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
323 for (int i = 0; i < mFilters.size();i++) {
324 if (mFilters.elementAt(i).getFilterType()
325 == filterRepresentation.getFilterType()) {
331 for (int i = 0; i < mFilters.size(); i++) {
332 if (mFilters.elementAt(i).getFilterClass()
333 == filterRepresentation.getFilterClass()) {
341 // If the filter is an "None" effect or border, then just don't add this
343 public void addFilter(FilterRepresentation representation) {
344 if (representation instanceof GeometryMetadata) {
345 setGeometry((GeometryMetadata) representation);
349 if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
350 removeFilter(representation);
351 if (!isNoneBorderFilter(representation)) {
352 mFilters.add(representation);
354 } else if (representation.getFilterType() == FilterRepresentation.TYPE_FX) {
355 boolean found = false;
356 for (int i = 0; i < mFilters.size(); i++) {
357 int type = mFilters.elementAt(i).getFilterType();
359 if (type != FilterRepresentation.TYPE_VIGNETTE) {
364 if (type == FilterRepresentation.TYPE_FX) {
366 if (!isNoneFxFilter(representation)) {
367 mFilters.add(i, representation);
373 if (!isNoneFxFilter(representation)) {
374 mFilters.add(representation);
378 mFilters.add(representation);
382 private boolean isNoneBorderFilter(FilterRepresentation representation) {
383 return representation instanceof FilterImageBorderRepresentation &&
384 ((FilterImageBorderRepresentation) representation).getDrawableResource() == 0;
387 private boolean isNoneFxFilter(FilterRepresentation representation) {
388 return representation instanceof FilterFxRepresentation &&
389 ((FilterFxRepresentation)representation).getNameResource() == R.string.none;
392 public FilterRepresentation getRepresentation(FilterRepresentation filterRepresentation) {
393 for (int i = 0; i < mFilters.size(); i++) {
394 FilterRepresentation representation = mFilters.elementAt(i);
395 if (representation.getFilterClass() == filterRepresentation.getFilterClass()) {
396 return representation;
402 public Bitmap apply(Bitmap original, FilterEnvironment environment) {
403 Bitmap bitmap = original;
404 bitmap = applyFilters(bitmap, -1, -1, environment);
405 return applyBorder(bitmap, environment);
408 public Bitmap applyGeometry(Bitmap bitmap, FilterEnvironment environment) {
409 // Apply any transform -- 90 rotate, flip, straighten, crop
410 // Returns a new bitmap.
411 if (mDoApplyGeometry) {
412 GeometryMetadata geoData = getGeometry();
413 bitmap = environment.applyRepresentation(geoData, bitmap);
418 public Bitmap applyBorder(Bitmap bitmap, FilterEnvironment environment) {
419 // get the border from the list of filters.
420 FilterRepresentation border = getFilterRepresentationForType(
421 FilterRepresentation.TYPE_BORDER);
422 if (border != null && mDoApplyGeometry) {
423 bitmap = environment.applyRepresentation(border, bitmap);
424 if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) {
425 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
426 "SaveBorder", border.getSerializationName(), 1);
432 public int nbFilters() {
433 return mFilters.size();
436 public Bitmap applyFilters(Bitmap bitmap, int from, int to, FilterEnvironment environment) {
437 if (mDoApplyFilters) {
442 to = mFilters.size();
444 if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) {
445 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
446 "SaveFilters", "Total", to - from + 1);
448 for (int i = from; i < to; i++) {
449 FilterRepresentation representation = mFilters.elementAt(i);
450 if (representation instanceof GeometryMetadata) {
451 // skip the geometry as it's already applied.
454 if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
455 // for now, let's skip the border as it will be applied in applyBorder()
456 // TODO: might be worth getting rid of applyBorder.
459 bitmap = environment.applyRepresentation(representation, bitmap);
460 if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) {
461 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
462 "SaveFilter", representation.getSerializationName(), 1);
464 if (environment.needsStop()) {
473 public void applyBorder(Allocation in, Allocation out,
474 boolean copyOut, FilterEnvironment environment) {
475 FilterRepresentation border = getFilterRepresentationForType(
476 FilterRepresentation.TYPE_BORDER);
477 if (border != null && mDoApplyGeometry) {
478 // TODO: should keep the bitmap around
479 Allocation bitmapIn = in;
481 bitmapIn = Allocation.createTyped(
482 CachingPipeline.getRenderScriptContext(), in.getType());
483 bitmapIn.copyFrom(out);
485 environment.applyRepresentation(border, bitmapIn, out);
489 public void applyFilters(int from, int to, Allocation in, Allocation out,
490 FilterEnvironment environment) {
491 if (mDoApplyFilters) {
496 to = mFilters.size();
498 for (int i = from; i < to; i++) {
499 FilterRepresentation representation = mFilters.elementAt(i);
500 if (representation instanceof GeometryMetadata) {
501 // skip the geometry as it's already applied.
504 if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
505 // for now, let's skip the border as it will be applied in applyBorder()
511 environment.applyRepresentation(representation, in, out);
516 public boolean canDoPartialRendering() {
517 if (MasterImage.getImage().getZoomOrientation() != ImageLoader.ORI_NORMAL) {
520 for (int i = 0; i < mFilters.size(); i++) {
521 FilterRepresentation representation = mFilters.elementAt(i);
522 if (representation instanceof GeometryMetadata
523 && ((GeometryMetadata) representation).hasModifications()) {
526 if (!representation.supportsPartialRendering()) {
533 public void fillImageStateAdapter(StateAdapter imageStateAdapter) {
534 if (imageStateAdapter == null) {
537 Vector<State> states = new Vector<State>();
538 for (FilterRepresentation filter : mFilters) {
539 if (filter instanceof GeometryMetadata) {
540 // TODO: supports Geometry representations in the state panel.
543 State state = new State(filter.getName());
544 state.setFilterRepresentation(filter);
547 imageStateAdapter.fill(states);
550 public void setPartialRendering(boolean partialRendering, Rect bounds) {
551 mPartialRendering = partialRendering;
552 mPartialRenderingBounds = bounds;
555 public boolean isPartialRendering() {
556 return mPartialRendering;
559 public Rect getPartialRenderingBounds() {
560 return mPartialRenderingBounds;
563 public Vector<ImageFilter> getUsedFilters(BaseFiltersManager filtersManager) {
564 Vector<ImageFilter> usedFilters = new Vector<ImageFilter>();
565 for (int i = 0; i < mFilters.size(); i++) {
566 FilterRepresentation representation = mFilters.elementAt(i);
567 ImageFilter filter = filtersManager.getFilterForRepresentation(representation);
568 usedFilters.add(filter);
573 public String getJsonString(String name) {
574 StringWriter swriter = new StringWriter();
576 JsonWriter writer = new JsonWriter(swriter);
577 writeJson(writer, name);
579 } catch (IOException e) {
582 return swriter.toString();
585 public void writeJson(JsonWriter writer, String name) {
586 int numFilters = mFilters.size();
588 writer.beginObject();
589 GeometryMetadata geoData = getGeometry();
590 writer.name(geoData.getSerializationName());
591 writer.beginObject();
593 String[][] rep = geoData.serializeRepresentation();
594 for (int i = 0; i < rep.length; i++) {
595 writer.name(rep[i][0]);
596 writer.value(rep[i][1]);
601 for (int i = 0; i < numFilters; i++) {
602 FilterRepresentation filter = mFilters.get(i);
603 if (filter instanceof GeometryMetadata) {
606 String sname = filter.getSerializationName();
608 Log.v(LOGTAG, "Serialization: " + sname);
610 Log.v(LOGTAG, "Serialization name null for filter: " + filter);
614 filter.serializeRepresentation(writer);
618 } catch (IOException e) {
624 * populates preset from JSON string
625 * @param filterString a JSON string
626 * @return true on success if false ImagePreset is undefined
628 public boolean readJsonFromString(String filterString) {
630 Log.v(LOGTAG,"reading preset: \""+filterString+"\"");
632 StringReader sreader = new StringReader(filterString);
634 JsonReader reader = new JsonReader(sreader);
635 boolean ok = readJson(reader);
641 } catch (Exception e) {
642 Log.e(LOGTAG,"parsing the filter parameters:",e);
649 * populates preset from JSON stream
650 * @param sreader a JSON string
651 * @return true on success if false ImagePreset is undefined
653 public boolean readJson(JsonReader sreader) throws IOException {
654 sreader.beginObject();
656 while (sreader.hasNext()) {
657 String name = sreader.nextName();
658 FilterRepresentation filter = creatFilterFromName(name);
659 if (filter == null) {
660 Log.w(LOGTAG,"UNKNOWN FILTER! "+name);
663 filter.deSerializeRepresentation(sreader);
670 FilterRepresentation creatFilterFromName(String name) {
671 if (GeometryMetadata.SERIALIZATION_NAME.equalsIgnoreCase(name)) {
672 return new GeometryMetadata();
674 FiltersManager filtersManager = FiltersManager.getManager();
675 return filtersManager.createFilterFromName(name);
678 public void updateWith(ImagePreset preset) {
679 if (preset.mFilters.size() != mFilters.size()) {
680 Log.e(LOGTAG, "Updating a preset with an incompatible one");
683 for (int i = 0; i < mFilters.size(); i++) {
684 FilterRepresentation destRepresentation = mFilters.elementAt(i);
685 FilterRepresentation sourceRepresentation = preset.mFilters.elementAt(i);
686 destRepresentation.useParametersFrom(sourceRepresentation);