MasterImage.java revision d2e9ea0ce1949e192e80ce806e702606694440ac
1/*
2 * Copyright (C) 2013 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.imageshow;
18
19import android.graphics.Bitmap;
20import android.graphics.Matrix;
21import android.graphics.Point;
22import android.graphics.Rect;
23import android.graphics.RectF;
24import android.os.Handler;
25import android.os.Message;
26
27import com.android.gallery3d.filtershow.FilterShowActivity;
28import com.android.gallery3d.filtershow.history.HistoryAdapter;
29import com.android.gallery3d.filtershow.history.HistoryItem;
30import com.android.gallery3d.filtershow.cache.FilteringPipeline;
31import com.android.gallery3d.filtershow.cache.ImageLoader;
32import com.android.gallery3d.filtershow.cache.RenderingRequest;
33import com.android.gallery3d.filtershow.cache.RenderingRequestCaller;
34import com.android.gallery3d.filtershow.cache.TripleBufferBitmap;
35import com.android.gallery3d.filtershow.filters.FilterRepresentation;
36import com.android.gallery3d.filtershow.filters.ImageFilter;
37import com.android.gallery3d.filtershow.presets.ImagePreset;
38import com.android.gallery3d.filtershow.state.StateAdapter;
39
40import java.util.Vector;
41
42public class MasterImage implements RenderingRequestCaller {
43
44    private static final String LOGTAG = "MasterImage";
45    private boolean DEBUG  = false;
46    private static final boolean DISABLEZOOM = false;
47    private static MasterImage sMasterImage = null;
48    private static int sIconSeedSize = 128;
49    private static float sHistoryPreviewSize = 128.0f;
50
51    private boolean mSupportsHighRes = false;
52
53    private ImageFilter mCurrentFilter = null;
54    private ImagePreset mPreset = null;
55    private ImagePreset mLoadedPreset = null;
56    private ImagePreset mGeometryOnlyPreset = null;
57    private ImagePreset mFiltersOnlyPreset = null;
58
59    private TripleBufferBitmap mFilteredPreview = new TripleBufferBitmap();
60
61    private Bitmap mGeometryOnlyBitmap = null;
62    private Bitmap mFiltersOnlyBitmap = null;
63    private Bitmap mPartialBitmap = null;
64    private Bitmap mHighresBitmap = null;
65
66    private ImageLoader mLoader = null;
67    private HistoryAdapter mHistory = null;
68    private StateAdapter mState = null;
69
70    private FilterShowActivity mActivity = null;
71
72    private Vector<ImageShow> mObservers = new Vector<ImageShow>();
73    private FilterRepresentation mCurrentFilterRepresentation;
74    private Vector<GeometryListener> mGeometryListeners = new Vector<GeometryListener>();
75
76    private GeometryMetadata mPreviousGeometry = null;
77
78    private float mScaleFactor = 1.0f;
79    private float mMaxScaleFactor = 3.0f; // TODO: base this on the current view / image
80    private Point mTranslation = new Point();
81    private Point mOriginalTranslation = new Point();
82
83    private Point mImageShowSize = new Point();
84
85    private boolean mShowsOriginal;
86
87    final private static int NEW_GEOMETRY = 1;
88
89    private final Handler mHandler = new Handler() {
90            @Override
91        public void handleMessage(Message msg) {
92            switch (msg.what) {
93                case NEW_GEOMETRY: {
94                hasNewGeometry();
95                break;
96            }
97            }
98        }
99    };
100
101    private MasterImage() {
102    }
103
104    // TODO: remove singleton
105    public static void setMaster(MasterImage master) {
106        sMasterImage = master;
107    }
108
109    public static MasterImage getImage() {
110        if (sMasterImage == null) {
111            sMasterImage = new MasterImage();
112        }
113        return sMasterImage;
114    }
115
116    public void setSupportsHighRes(boolean value) {
117        mSupportsHighRes = value;
118    }
119
120    public static void setIconSeedSize(int iconSeedSize) {
121        sIconSeedSize = iconSeedSize;
122    }
123
124    public void addObserver(ImageShow observer) {
125        if (mObservers.contains(observer)) {
126            return;
127        }
128        mObservers.add(observer);
129    }
130
131    public void setActivity(FilterShowActivity activity) {
132        mActivity = activity;
133    }
134
135    public ImageLoader getLoader() {
136        return mLoader;
137    }
138
139    public synchronized ImagePreset getPreset() {
140        return mPreset;
141    }
142
143    public synchronized ImagePreset getGeometryPreset() {
144        return mGeometryOnlyPreset;
145    }
146
147    public synchronized ImagePreset getFiltersOnlyPreset() {
148        return mFiltersOnlyPreset;
149    }
150
151    public synchronized void setPreset(ImagePreset preset,
152                                       FilterRepresentation change,
153                                       boolean addToHistory) {
154        if (DEBUG) {
155            preset.showFilters();
156        }
157        mPreset = preset;
158        mPreset.setImageLoader(mLoader);
159        setGeometry();
160        mPreset.fillImageStateAdapter(mState);
161        if (addToHistory) {
162            HistoryItem historyItem = new HistoryItem(mPreset, change);
163            mHistory.addHistoryItem(historyItem);
164        }
165        updatePresets(true);
166        GeometryMetadata geo = mPreset.getGeometry();
167        if (!geo.equals(mPreviousGeometry)) {
168            notifyGeometryChange();
169        }
170        mPreviousGeometry = new GeometryMetadata(geo);
171        mActivity.updateCategories();
172    }
173
174    private void setGeometry() {
175        Bitmap image = mLoader.getOriginalBitmapLarge();
176        if (image == null) {
177            return;
178        }
179        float w = image.getWidth();
180        float h = image.getHeight();
181        GeometryMetadata geo = mPreset.getGeometry();
182        RectF pb = geo.getPhotoBounds();
183        if (w == pb.width() && h == pb.height()) {
184            return;
185        }
186        RectF r = new RectF(0, 0, w, h);
187        geo.setPhotoBounds(r);
188        geo.setCropBounds(r);
189        mPreset.setGeometry(geo);
190    }
191
192    public void onHistoryItemClick(int position) {
193        HistoryItem historyItem = mHistory.getItem(position);
194        // We need a copy from the history
195        ImagePreset newPreset = new ImagePreset(historyItem.getImagePreset());
196        // don't need to add it to the history
197        setPreset(newPreset, historyItem.getFilterRepresentation(), false);
198        mHistory.setCurrentPreset(position);
199    }
200
201    public HistoryAdapter getHistory() {
202        return mHistory;
203    }
204
205    public StateAdapter getState() {
206        return mState;
207    }
208
209    public void setHistoryAdapter(HistoryAdapter adapter) {
210        mHistory = adapter;
211    }
212
213    public void setStateAdapter(StateAdapter adapter) {
214        mState = adapter;
215    }
216
217    public void setImageLoader(ImageLoader loader) {
218        mLoader = loader;
219    }
220
221    public ImageLoader getImageLoader() {
222        return mLoader;
223    }
224
225    public void setCurrentFilter(ImageFilter filter) {
226        mCurrentFilter = filter;
227    }
228
229    public ImageFilter getCurrentFilter() {
230        return mCurrentFilter;
231    }
232
233    public synchronized boolean hasModifications() {
234        if (mPreset == null) {
235            return getLoadedPreset() != null;
236        } else {
237            // TODO: same() is quite strict check here. We should be only
238            // checking for functionality parity.
239            return !mPreset.same(getLoadedPreset());
240        }
241    }
242
243    public TripleBufferBitmap getDoubleBuffer() {
244        return mFilteredPreview;
245    }
246
247    public void setOriginalGeometry(Bitmap originalBitmapLarge) {
248        GeometryMetadata geo = getPreset().getGeometry();
249        float w = originalBitmapLarge.getWidth();
250        float h = originalBitmapLarge.getHeight();
251        RectF r = new RectF(0, 0, w, h);
252        geo.setPhotoBounds(r);
253        geo.setCropBounds(r);
254        getPreset().setGeometry(geo);
255    }
256
257    public Bitmap getFilteredImage() {
258        return mFilteredPreview.getConsumer();
259    }
260
261    public Bitmap getFiltersOnlyImage() {
262        return mFiltersOnlyBitmap;
263    }
264
265    public Bitmap getGeometryOnlyImage() {
266        return mGeometryOnlyBitmap;
267    }
268
269    public Bitmap getPartialImage() {
270        return mPartialBitmap;
271    }
272
273    public Bitmap getHighresImage() {
274        return mHighresBitmap;
275    }
276
277    public void notifyObservers() {
278        for (ImageShow observer : mObservers) {
279            observer.invalidate();
280        }
281    }
282
283    public void updatePresets(boolean force) {
284        if (force || mGeometryOnlyPreset == null) {
285            ImagePreset newPreset = new ImagePreset(mPreset);
286            newPreset.setDoApplyFilters(false);
287            newPreset.setDoApplyGeometry(true);
288            if (force || mGeometryOnlyPreset == null
289                    || !newPreset.same(mGeometryOnlyPreset)) {
290                mGeometryOnlyPreset = newPreset;
291                RenderingRequest.post(mLoader.getOriginalBitmapLarge(),
292                        mGeometryOnlyPreset, RenderingRequest.GEOMETRY_RENDERING, this);
293            }
294        }
295        if (force || mFiltersOnlyPreset == null) {
296            ImagePreset newPreset = new ImagePreset(mPreset);
297            newPreset.setDoApplyFilters(true);
298            newPreset.setDoApplyGeometry(false);
299            if (force || mFiltersOnlyPreset == null
300                    || !newPreset.same(mFiltersOnlyPreset)) {
301                mFiltersOnlyPreset = newPreset;
302                RenderingRequest.post(mLoader.getOriginalBitmapLarge(),
303                        mFiltersOnlyPreset, RenderingRequest.FILTERS_RENDERING, this);
304            }
305        }
306        invalidatePreview();
307        mActivity.enableSave(hasModifications());
308    }
309
310    public FilterRepresentation getCurrentFilterRepresentation() {
311        return mCurrentFilterRepresentation;
312    }
313
314    public void setCurrentFilterRepresentation(FilterRepresentation currentFilterRepresentation) {
315        mCurrentFilterRepresentation = currentFilterRepresentation;
316    }
317
318    public void invalidateFiltersOnly() {
319        mFiltersOnlyPreset = null;
320        updatePresets(false);
321    }
322
323    public void invalidatePartialPreview() {
324        if (mPartialBitmap != null) {
325            mPartialBitmap = null;
326            notifyObservers();
327        }
328    }
329
330    public void invalidateHighresPreview() {
331        if (mHighresBitmap != null) {
332            mHighresBitmap = null;
333            notifyObservers();
334        }
335    }
336
337    public void invalidatePreview() {
338        mFilteredPreview.invalidate();
339        invalidatePartialPreview();
340        invalidateHighresPreview();
341        needsUpdatePartialPreview();
342        needsUpdateHighResPreview();
343        FilteringPipeline.getPipeline().updatePreviewBuffer();
344    }
345
346    public void setImageShowSize(int w, int h) {
347        if (mImageShowSize.x != w || mImageShowSize.y != h) {
348            mImageShowSize.set(w, h);
349            needsUpdatePartialPreview();
350            needsUpdateHighResPreview();
351        }
352    }
353
354    private Matrix getImageToScreenMatrix(boolean reflectRotation) {
355        GeometryMetadata geo = mPreset.getGeometry();
356        if (geo == null || mLoader == null
357                || mLoader.getOriginalBounds() == null
358                || mImageShowSize.x == 0) {
359            return new Matrix();
360        }
361        Matrix m = geo.getOriginalToScreen(reflectRotation,
362                mLoader.getOriginalBounds().width(),
363                mLoader.getOriginalBounds().height(), mImageShowSize.x, mImageShowSize.y);
364        Point translate = getTranslation();
365        float scaleFactor = getScaleFactor();
366        m.postTranslate(translate.x, translate.y);
367        m.postScale(scaleFactor, scaleFactor, mImageShowSize.x/2.0f, mImageShowSize.y/2.0f);
368        return m;
369    }
370
371    private Matrix getScreenToImageMatrix(boolean reflectRotation) {
372        Matrix m = getImageToScreenMatrix(reflectRotation);
373        Matrix invert = new Matrix();
374        m.invert(invert);
375        return invert;
376    }
377
378    public void needsUpdateHighResPreview() {
379        if (!mSupportsHighRes) {
380            return;
381        }
382        RenderingRequest.post(null, mPreset, RenderingRequest.HIGHRES_RENDERING, this);
383        invalidateHighresPreview();
384    }
385
386    public void needsUpdatePartialPreview() {
387        if (!mPreset.canDoPartialRendering()) {
388            invalidatePartialPreview();
389            return;
390        }
391        Matrix m = getScreenToImageMatrix(true);
392        RectF r = new RectF(0, 0, mImageShowSize.x, mImageShowSize.y);
393        RectF dest = new RectF();
394        m.mapRect(dest, r);
395        Rect bounds = new Rect();
396        dest.roundOut(bounds);
397        RenderingRequest.post(null, mPreset, RenderingRequest.PARTIAL_RENDERING,
398                this, bounds, new Rect(0, 0, mImageShowSize.x, mImageShowSize.y));
399        invalidatePartialPreview();
400    }
401
402    @Override
403    public void available(RenderingRequest request) {
404        if (request.getBitmap() == null) {
405            return;
406        }
407        if (request.getType() == RenderingRequest.GEOMETRY_RENDERING) {
408            mGeometryOnlyBitmap = request.getBitmap();
409        }
410        if (request.getType() == RenderingRequest.FILTERS_RENDERING) {
411            mFiltersOnlyBitmap = request.getBitmap();
412        }
413        if (request.getType() == RenderingRequest.PARTIAL_RENDERING
414                && request.getScaleFactor() == getScaleFactor()) {
415            mPartialBitmap = request.getBitmap();
416            notifyObservers();
417        }
418        if (request.getType() == RenderingRequest.HIGHRES_RENDERING) {
419            mHighresBitmap = request.getBitmap();
420            notifyObservers();
421        }
422    }
423
424    public static void reset() {
425        sMasterImage = null;
426    }
427
428    public void addGeometryListener(GeometryListener listener) {
429        mGeometryListeners.add(listener);
430    }
431
432    public void notifyGeometryChange() {
433        if (mHandler.hasMessages(NEW_GEOMETRY)) {
434            return;
435        }
436        mHandler.sendEmptyMessage(NEW_GEOMETRY);
437    }
438
439    public void hasNewGeometry() {
440        updatePresets(true);
441        for (GeometryListener listener : mGeometryListeners) {
442            listener.geometryChanged();
443        }
444    }
445
446
447    public float getScaleFactor() {
448        return mScaleFactor;
449    }
450
451    public void setScaleFactor(float scaleFactor) {
452        if (DISABLEZOOM) {
453            return;
454        }
455        if (scaleFactor == mScaleFactor) {
456            return;
457        }
458        mScaleFactor = scaleFactor;
459        invalidatePartialPreview();
460    }
461
462    public Point getTranslation() {
463        return mTranslation;
464    }
465
466    public void setTranslation(Point translation) {
467        if (DISABLEZOOM) {
468            mTranslation.x = 0;
469            mTranslation.y = 0;
470            return;
471        }
472        mTranslation.x = translation.x;
473        mTranslation.y = translation.y;
474        needsUpdatePartialPreview();
475    }
476
477    public Point getOriginalTranslation() {
478        return mOriginalTranslation;
479    }
480
481    public void setOriginalTranslation(Point originalTranslation) {
482        if (DISABLEZOOM) {
483            return;
484        }
485        mOriginalTranslation.x = originalTranslation.x;
486        mOriginalTranslation.y = originalTranslation.y;
487    }
488
489    public void resetTranslation() {
490        mTranslation.x = 0;
491        mTranslation.y = 0;
492        needsUpdatePartialPreview();
493    }
494
495    public Bitmap getThumbnailBitmap() {
496        return mLoader.getOriginalBitmapSmall();
497    }
498
499    public Bitmap getLargeThumbnailBitmap() {
500        return mLoader.getOriginalBitmapLarge();
501    }
502
503    public float getMaxScaleFactor() {
504        if (DISABLEZOOM) {
505            return 1;
506        }
507        return mMaxScaleFactor;
508    }
509
510    public void setMaxScaleFactor(float maxScaleFactor) {
511        mMaxScaleFactor = maxScaleFactor;
512    }
513
514    public boolean supportsHighRes() {
515        return mSupportsHighRes;
516    }
517
518    public void setShowsOriginal(boolean value) {
519        mShowsOriginal = value;
520        notifyObservers();
521    }
522
523    public boolean showsOriginal() {
524        return mShowsOriginal;
525    }
526
527    public void setLoadedPreset(ImagePreset preset) {
528        mLoadedPreset = preset;
529    }
530
531    public ImagePreset getLoadedPreset() {
532        return mLoadedPreset;
533    }
534}
535