ImagePreset.java revision fa474a198019851ecc3824a1dfbac94cd1928efc
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
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
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17package com.android.gallery3d.filtershow.pipeline;
18
19import android.graphics.Bitmap;
20import android.graphics.Rect;
21import android.support.v8.renderscript.Allocation;
22import android.util.JsonReader;
23import android.util.JsonWriter;
24import android.util.Log;
25
26import com.android.gallery3d.R;
27import com.android.gallery3d.filtershow.cache.ImageLoader;
28import com.android.gallery3d.filtershow.filters.BaseFiltersManager;
29import com.android.gallery3d.filtershow.filters.FilterCropRepresentation;
30import com.android.gallery3d.filtershow.filters.FilterFxRepresentation;
31import com.android.gallery3d.filtershow.filters.FilterImageBorderRepresentation;
32import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation;
33import com.android.gallery3d.filtershow.filters.FilterRepresentation;
34import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation;
35import com.android.gallery3d.filtershow.filters.FilterStraightenRepresentation;
36import com.android.gallery3d.filtershow.filters.FilterUserPresetRepresentation;
37import com.android.gallery3d.filtershow.filters.FiltersManager;
38import com.android.gallery3d.filtershow.filters.ImageFilter;
39import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
40import com.android.gallery3d.filtershow.imageshow.MasterImage;
41import com.android.gallery3d.filtershow.state.State;
42import com.android.gallery3d.filtershow.state.StateAdapter;
43import com.android.gallery3d.util.UsageStatistics;
44
45import java.io.IOException;
46import java.io.StringReader;
47import java.io.StringWriter;
48import java.util.Vector;
49
50public class ImagePreset {
51
52    private static final String LOGTAG = "ImagePreset";
53
54    private Vector<FilterRepresentation> mFilters = new Vector<FilterRepresentation>();
55
56    private boolean mDoApplyGeometry = true;
57    private boolean mDoApplyFilters = true;
58
59    private boolean mPartialRendering = false;
60    private Rect mPartialRenderingBounds;
61    private static final boolean DEBUG = false;
62
63    public ImagePreset() {
64    }
65
66    public ImagePreset(ImagePreset source) {
67        for (int i = 0; i < source.mFilters.size(); i++) {
68            FilterRepresentation representation = null;
69            FilterRepresentation sourceRepresentation = source.mFilters.elementAt(i);
70            if (sourceRepresentation instanceof GeometryMetadata) {
71                GeometryMetadata geoData = new GeometryMetadata();
72                GeometryMetadata srcGeo = (GeometryMetadata) sourceRepresentation;
73                geoData.set(srcGeo);
74                representation = geoData;
75            } else {
76                representation = sourceRepresentation.copy();
77            }
78            mFilters.add(representation);
79        }
80    }
81
82    public FilterRepresentation getFilterRepresentation(int position) {
83        FilterRepresentation representation = null;
84
85        representation = mFilters.elementAt(position).copy();
86
87        return representation;
88    }
89
90    public int getPositionForRepresentation(FilterRepresentation representation) {
91        for (int i = 0; i < mFilters.size(); i++) {
92            if (mFilters.elementAt(i).getFilterClass() == representation.getFilterClass()) {
93                return i;
94            }
95        }
96        return -1;
97    }
98
99    private FilterRepresentation getFilterRepresentationForType(int type) {
100        for (int i = 0; i < mFilters.size(); i++) {
101            if (mFilters.elementAt(i).getFilterType() == type) {
102                return mFilters.elementAt(i);
103            }
104        }
105        return null;
106    }
107
108    public int getPositionForType(int type) {
109        for (int i = 0; i < mFilters.size(); i++) {
110            if (mFilters.elementAt(i).getFilterType() == type) {
111                return i;
112            }
113        }
114        return -1;
115    }
116
117    public FilterRepresentation getFilterRepresentationCopyFrom(FilterRepresentation filterRepresentation) {
118        // TODO: add concept of position in the filters (to allow multiple instances)
119        if (filterRepresentation == null) {
120            return null;
121        }
122        int position = getPositionForRepresentation(filterRepresentation);
123        if (position == -1) {
124            return null;
125        }
126        FilterRepresentation representation = mFilters.elementAt(position);
127        if (representation != null) {
128            representation = representation.copy();
129        }
130        return representation;
131    }
132
133    public void updateFilterRepresentation(FilterRepresentation representation) {
134        if (representation == null) {
135            return;
136        }
137        if (representation instanceof GeometryMetadata) {
138            setGeometry((GeometryMetadata) representation);
139        } else {
140            int position = getPositionForRepresentation(representation);
141            if (position == -1) {
142                return;
143            }
144            FilterRepresentation old = mFilters.elementAt(position);
145            old.useParametersFrom(representation);
146        }
147        MasterImage.getImage().invalidatePreview();
148        fillImageStateAdapter(MasterImage.getImage().getState());
149    }
150
151    public void setDoApplyGeometry(boolean value) {
152        mDoApplyGeometry = value;
153    }
154
155    public void setDoApplyFilters(boolean value) {
156        mDoApplyFilters = value;
157    }
158
159    public boolean getDoApplyFilters() {
160        return mDoApplyFilters;
161    }
162
163    public GeometryMetadata getGeometry() {
164        for (FilterRepresentation representation : mFilters) {
165            if (representation instanceof GeometryMetadata) {
166                return (GeometryMetadata) representation;
167            }
168        }
169        GeometryMetadata geo = new GeometryMetadata();
170        mFilters.add(0, geo); // Hard Requirement for now -- Geometry ought to be first.
171        return geo;
172    }
173
174    public boolean hasModifications() {
175        for (int i = 0; i < mFilters.size(); i++) {
176            FilterRepresentation filter = mFilters.elementAt(i);
177            if (filter instanceof GeometryMetadata) {
178                if (((GeometryMetadata) filter).hasModifications()) {
179                    return true;
180                }
181            } else if (!filter.isNil()) {
182                return true;
183            }
184        }
185        return false;
186    }
187
188    public boolean isPanoramaSafe() {
189        for (FilterRepresentation representation : mFilters) {
190            if (representation instanceof GeometryMetadata) {
191                if (((GeometryMetadata) representation).hasModifications()) {
192                    return false;
193                }
194            }
195            if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER
196                    && !representation.isNil()) {
197                return false;
198            }
199            if (representation.getFilterType() == FilterRepresentation.TYPE_VIGNETTE
200                    && !representation.isNil()) {
201                return false;
202            }
203            if (representation.getFilterType() == FilterRepresentation.TYPE_TINYPLANET
204                    && !representation.isNil()) {
205                return false;
206            }
207        }
208        return true;
209    }
210
211    public void setGeometry(GeometryMetadata representation) {
212        GeometryMetadata geoData = getGeometry();
213        if (geoData != representation) {
214            geoData.set(representation);
215        }
216    }
217
218    public boolean equals(ImagePreset preset) {
219        if (!same(preset)) {
220            return false;
221        }
222        if (mDoApplyFilters && preset.mDoApplyFilters) {
223            for (int i = 0; i < preset.mFilters.size(); i++) {
224                FilterRepresentation a = preset.mFilters.elementAt(i);
225                FilterRepresentation b = mFilters.elementAt(i);
226                if (!a.equals(b)) {
227                    return false;
228                }
229            }
230        }
231        return true;
232    }
233
234    public boolean same(ImagePreset preset) {
235        if (preset == null) {
236            return false;
237        }
238
239        if (preset.mFilters.size() != mFilters.size()) {
240            return false;
241        }
242
243        if (mDoApplyGeometry != preset.mDoApplyGeometry) {
244            return false;
245        }
246
247        if (mDoApplyGeometry && !getGeometry().equals(preset.getGeometry())) {
248            return false;
249        }
250
251        if (mDoApplyFilters != preset.mDoApplyFilters) {
252            if (mFilters.size() > 0 || preset.mFilters.size() > 0) {
253                return false;
254            }
255        }
256
257        if (mDoApplyFilters && preset.mDoApplyFilters) {
258            for (int i = 0; i < preset.mFilters.size(); i++) {
259                FilterRepresentation a = preset.mFilters.elementAt(i);
260                FilterRepresentation b = mFilters.elementAt(i);
261                if (a instanceof GeometryMetadata) {
262                    // Note: Geometry will always be at the same place
263                    continue;
264                }
265                if (!a.same(b)) {
266                    return false;
267                }
268            }
269        }
270
271        return true;
272    }
273
274    public int similarUpTo(ImagePreset preset) {
275        if (!getGeometry().equals(preset.getGeometry())) {
276            return -1;
277        }
278
279        for (int i = 0; i < preset.mFilters.size(); i++) {
280            FilterRepresentation a = preset.mFilters.elementAt(i);
281            if (i < mFilters.size()) {
282                FilterRepresentation b = mFilters.elementAt(i);
283                if (!a.same(b)) {
284                    return i;
285                }
286                if (!a.equals(b)) {
287                    return i;
288                }
289            } else {
290                return i;
291            }
292        }
293        return preset.mFilters.size();
294    }
295
296    public void showFilters() {
297        Log.v(LOGTAG, "\\\\\\ showFilters -- " + mFilters.size() + " filters");
298        int n = 0;
299        for (FilterRepresentation representation : mFilters) {
300            Log.v(LOGTAG, " filter " + n + " : " + representation.toString());
301            n++;
302        }
303        Log.v(LOGTAG, "/// showFilters -- " + mFilters.size() + " filters");
304    }
305
306    public FilterRepresentation getLastRepresentation() {
307        if (mFilters.size() > 0) {
308            return mFilters.lastElement();
309        }
310        return null;
311    }
312
313    public void removeFilter(FilterRepresentation filterRepresentation) {
314        if (filterRepresentation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
315            for (int i = 0; i < mFilters.size();i++) {
316                if (mFilters.elementAt(i).getFilterType()
317                        == filterRepresentation.getFilterType()) {
318                    mFilters.remove(i);
319                    break;
320                }
321            }
322        } else {
323            for (int i = 0; i < mFilters.size(); i++) {
324                if (mFilters.elementAt(i).getFilterClass()
325                        == filterRepresentation.getFilterClass()) {
326                    mFilters.remove(i);
327                    break;
328                }
329            }
330        }
331    }
332
333    // If the filter is an "None" effect or border, then just don't add this
334    // filter.
335    public void addFilter(FilterRepresentation representation) {
336        if (representation instanceof GeometryMetadata) {
337            setGeometry((GeometryMetadata) representation);
338            return;
339        }
340        if (representation instanceof FilterUserPresetRepresentation) {
341            ImagePreset preset = ((FilterUserPresetRepresentation) representation).getImagePreset();
342            // user preset replace everything but geometry
343            GeometryMetadata geometry = getGeometry();
344            mFilters.clear();
345            mFilters.add(geometry);
346            for (int i = 0; i < preset.nbFilters(); i++) {
347                FilterRepresentation rep = preset.getFilterRepresentation(i);
348                if (!(representation instanceof GeometryMetadata)) {
349                    addFilter(rep);
350                }
351            }
352            mFilters.add(representation);
353            return;
354        }
355        if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
356            removeFilter(representation);
357            if (!isNoneBorderFilter(representation)) {
358                mFilters.add(representation);
359            }
360        } else if (representation.getFilterType() == FilterRepresentation.TYPE_FX) {
361            boolean found = false;
362            for (int i = 0; i < mFilters.size(); i++) {
363                FilterRepresentation current = mFilters.elementAt(i);
364                int type = current.getFilterType();
365                if (found) {
366                    if (type != FilterRepresentation.TYPE_VIGNETTE) {
367                        mFilters.remove(i);
368                        continue;
369                    }
370                }
371                if (type == FilterRepresentation.TYPE_FX) {
372                    if (current instanceof FilterUserPresetRepresentation) {
373                        ImagePreset preset = ((FilterUserPresetRepresentation) current)
374                                .getImagePreset();
375                        // If we had an existing user preset, let's remove all the presets that
376                        // were added by it
377                        for (int j = 0; j < preset.nbFilters(); j++) {
378                            FilterRepresentation rep = preset.getFilterRepresentation(j);
379                            int pos = getPositionForRepresentation(rep);
380                            if (pos != -1) {
381                                mFilters.remove(pos);
382                            }
383                        }
384                        int pos = getPositionForRepresentation(current);
385                        if (pos != -1) {
386                            mFilters.remove(pos);
387                        } else {
388                            pos = 0;
389                        }
390                        if (!isNoneFxFilter(representation)) {
391                            mFilters.add(pos, representation);
392                        }
393
394                    } else {
395                        mFilters.remove(i);
396                        if (!isNoneFxFilter(representation)) {
397                            mFilters.add(i, representation);
398                        }
399                    }
400                    found = true;
401                }
402            }
403            if (!found) {
404                if (!isNoneFxFilter(representation)) {
405                    mFilters.add(representation);
406                }
407            }
408        } else {
409            mFilters.add(representation);
410        }
411    }
412
413    private boolean isNoneBorderFilter(FilterRepresentation representation) {
414        return representation instanceof FilterImageBorderRepresentation &&
415                ((FilterImageBorderRepresentation) representation).getDrawableResource() == 0;
416    }
417
418    private boolean isNoneFxFilter(FilterRepresentation representation) {
419        return representation instanceof FilterFxRepresentation &&
420                ((FilterFxRepresentation)representation).getNameResource() == R.string.none;
421    }
422
423    public FilterRepresentation getRepresentation(FilterRepresentation filterRepresentation) {
424        for (int i = 0; i < mFilters.size(); i++) {
425            FilterRepresentation representation = mFilters.elementAt(i);
426            if (representation.getFilterClass() == filterRepresentation.getFilterClass()) {
427                return representation;
428            }
429        }
430        return null;
431    }
432
433    public Bitmap apply(Bitmap original, FilterEnvironment environment) {
434        Bitmap bitmap = original;
435        bitmap = applyFilters(bitmap, -1, -1, environment);
436        return applyBorder(bitmap, environment);
437    }
438
439    public Bitmap applyGeometry(Bitmap bitmap, FilterEnvironment environment) {
440        // Apply any transform -- 90 rotate, flip, straighten, crop
441        // Returns a new bitmap.
442        if (mDoApplyGeometry) {
443            GeometryMetadata geoData = getGeometry();
444            bitmap = environment.applyRepresentation(geoData, bitmap);
445        }
446        return bitmap;
447    }
448
449    public Bitmap applyBorder(Bitmap bitmap, FilterEnvironment environment) {
450        // get the border from the list of filters.
451        FilterRepresentation border = getFilterRepresentationForType(
452                FilterRepresentation.TYPE_BORDER);
453        if (border != null && mDoApplyGeometry) {
454            bitmap = environment.applyRepresentation(border, bitmap);
455            if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) {
456                UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
457                        "SaveBorder", border.getSerializationName(), 1);
458            }
459        }
460        return bitmap;
461    }
462
463    public int nbFilters() {
464        return mFilters.size();
465    }
466
467    public Bitmap applyFilters(Bitmap bitmap, int from, int to, FilterEnvironment environment) {
468        if (mDoApplyFilters) {
469            if (from < 0) {
470                from = 0;
471            }
472            if (to == -1) {
473                to = mFilters.size();
474            }
475            if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) {
476                UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
477                        "SaveFilters", "Total", to - from + 1);
478            }
479            for (int i = from; i < to; i++) {
480                FilterRepresentation representation = mFilters.elementAt(i);
481                if (representation instanceof GeometryMetadata) {
482                    // skip the geometry as it's already applied.
483                    continue;
484                }
485                if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
486                    // for now, let's skip the border as it will be applied in applyBorder()
487                    // TODO: might be worth getting rid of applyBorder.
488                    continue;
489                }
490                bitmap = environment.applyRepresentation(representation, bitmap);
491                if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) {
492                    UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
493                            "SaveFilter", representation.getSerializationName(), 1);
494                }
495                if (environment.needsStop()) {
496                    return bitmap;
497                }
498            }
499        }
500
501        return bitmap;
502    }
503
504    public void applyBorder(Allocation in, Allocation out,
505                            boolean copyOut, FilterEnvironment environment) {
506        FilterRepresentation border = getFilterRepresentationForType(
507                FilterRepresentation.TYPE_BORDER);
508        if (border != null && mDoApplyGeometry) {
509            // TODO: should keep the bitmap around
510            Allocation bitmapIn = in;
511            if (copyOut) {
512                bitmapIn = Allocation.createTyped(
513                        CachingPipeline.getRenderScriptContext(), in.getType());
514                bitmapIn.copyFrom(out);
515            }
516            environment.applyRepresentation(border, bitmapIn, out);
517        }
518    }
519
520    public void applyFilters(int from, int to, Allocation in, Allocation out,
521                             FilterEnvironment environment) {
522        if (mDoApplyFilters) {
523            if (from < 0) {
524                from = 0;
525            }
526            if (to == -1) {
527                to = mFilters.size();
528            }
529            for (int i = from; i < to; i++) {
530                FilterRepresentation representation = mFilters.elementAt(i);
531                if (representation instanceof GeometryMetadata) {
532                    // skip the geometry as it's already applied.
533                    continue;
534                }
535                if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) {
536                    // for now, let's skip the border as it will be applied in applyBorder()
537                    continue;
538                }
539                if (i > from) {
540                    in.copyFrom(out);
541                }
542                environment.applyRepresentation(representation, in, out);
543            }
544        }
545    }
546
547    public boolean canDoPartialRendering() {
548        if (MasterImage.getImage().getZoomOrientation() != ImageLoader.ORI_NORMAL) {
549            return false;
550        }
551        for (int i = 0; i < mFilters.size(); i++) {
552            FilterRepresentation representation = mFilters.elementAt(i);
553            if (representation instanceof GeometryMetadata
554                && ((GeometryMetadata) representation).hasModifications()) {
555                return false;
556            }
557            if (!representation.supportsPartialRendering()) {
558                return false;
559            }
560        }
561        return true;
562    }
563
564    public void fillImageStateAdapter(StateAdapter imageStateAdapter) {
565        if (imageStateAdapter == null) {
566            return;
567        }
568        Vector<State> states = new Vector<State>();
569        for (FilterRepresentation filter : mFilters) {
570            if (filter instanceof GeometryMetadata) {
571                // TODO: supports Geometry representations in the state panel.
572                continue;
573            }
574            if (filter instanceof FilterUserPresetRepresentation) {
575                // do not show the user preset itself in the state panel
576                continue;
577            }
578            State state = new State(filter.getName());
579            state.setFilterRepresentation(filter);
580            states.add(state);
581        }
582        imageStateAdapter.fill(states);
583    }
584
585    public void setPartialRendering(boolean partialRendering, Rect bounds) {
586        mPartialRendering = partialRendering;
587        mPartialRenderingBounds = bounds;
588    }
589
590    public boolean isPartialRendering() {
591        return mPartialRendering;
592    }
593
594    public Rect getPartialRenderingBounds() {
595        return mPartialRenderingBounds;
596    }
597
598    public Vector<ImageFilter> getUsedFilters(BaseFiltersManager filtersManager) {
599        Vector<ImageFilter> usedFilters = new Vector<ImageFilter>();
600        for (int i = 0; i < mFilters.size(); i++) {
601            FilterRepresentation representation = mFilters.elementAt(i);
602            ImageFilter filter = filtersManager.getFilterForRepresentation(representation);
603            usedFilters.add(filter);
604        }
605        return usedFilters;
606    }
607
608    public String getJsonString(String name) {
609        StringWriter swriter = new StringWriter();
610        try {
611            JsonWriter writer = new JsonWriter(swriter);
612            writeJson(writer, name);
613            writer.close();
614        } catch (IOException e) {
615            return null;
616        }
617        return swriter.toString();
618    }
619
620    public void writeJson(JsonWriter writer, String name) {
621        int numFilters =  mFilters.size();
622        try {
623            writer.beginObject();
624            for (int i = 0; i < numFilters; i++) {
625                FilterRepresentation filter = mFilters.get(i);
626                if (filter instanceof FilterUserPresetRepresentation) {
627                    continue;
628                }
629                String sname = filter.getSerializationName();
630                if (DEBUG) {
631                    Log.v(LOGTAG, "Serialization: " + sname);
632                    if (sname == null) {
633                        Log.v(LOGTAG, "Serialization name null for filter: " + filter);
634                    }
635                }
636                writer.name(sname);
637                filter.serializeRepresentation(writer);
638            }
639            writer.endObject();
640
641        } catch (IOException e) {
642            e.printStackTrace();
643        }
644    }
645
646    /**
647     * populates preset from JSON string
648     * @param filterString a JSON string
649     * @return true on success if false ImagePreset is undefined
650     */
651    public boolean readJsonFromString(String filterString) {
652        if (DEBUG) {
653            Log.v(LOGTAG,"reading preset: \""+filterString+"\"");
654        }
655        StringReader sreader = new StringReader(filterString);
656        try {
657            JsonReader reader = new JsonReader(sreader);
658            boolean ok = readJson(reader);
659            if (!ok) {
660                reader.close();
661                return false;
662            }
663            reader.close();
664        } catch (Exception e) {
665            Log.e(LOGTAG,"parsing the filter parameters:",e);
666            return false;
667        }
668        return true;
669    }
670
671    /**
672     * populates preset from JSON stream
673     * @param sreader a JSON string
674     * @return true on success if false ImagePreset is undefined
675     */
676    public boolean readJson(JsonReader sreader) throws IOException {
677        sreader.beginObject();
678
679        while (sreader.hasNext()) {
680            String name = sreader.nextName();
681            FilterRepresentation filter = creatFilterFromName(name);
682            if (filter == null) {
683                Log.w(LOGTAG,"UNKNOWN FILTER! "+name);
684                return false;
685            }
686            filter.deSerializeRepresentation(sreader);
687            addFilter(filter);
688        }
689        sreader.endObject();
690        return true;
691    }
692
693    FilterRepresentation creatFilterFromName(String name) {
694        // TODO: move these to FiltersManager pattern.
695        if (GeometryMetadata.SERIALIZATION_NAME.equalsIgnoreCase(name)) {
696            return new GeometryMetadata();
697        } else if (FilterRotateRepresentation.SERIALIZATION_NAME.equals(name)) {
698            return new FilterRotateRepresentation();
699        } else if (FilterMirrorRepresentation.SERIALIZATION_NAME.equals(name)) {
700            return new FilterMirrorRepresentation();
701        } else if (FilterStraightenRepresentation.SERIALIZATION_NAME.equals(name)) {
702            return new FilterStraightenRepresentation();
703        } else if (FilterCropRepresentation.SERIALIZATION_NAME.equals(name)) {
704            return new FilterCropRepresentation();
705        }
706        FiltersManager filtersManager = FiltersManager.getManager();
707        return filtersManager.createFilterFromName(name);
708    }
709
710    public void updateWith(ImagePreset preset) {
711        if (preset.mFilters.size() != mFilters.size()) {
712            Log.e(LOGTAG, "Updating a preset with an incompatible one");
713            return;
714        }
715        for (int i = 0; i < mFilters.size(); i++) {
716            FilterRepresentation destRepresentation = mFilters.elementAt(i);
717            FilterRepresentation sourceRepresentation = preset.mFilters.elementAt(i);
718            destRepresentation.useParametersFrom(sourceRepresentation);
719        }
720    }
721}
722