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