OSDN Git Service

Add background processing service
[android-x86/packages-apps-Gallery2.git] / src / com / android / gallery3d / filtershow / imageshow / MasterImage.java
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
17 package com.android.gallery3d.filtershow.imageshow;
18
19 import android.graphics.Bitmap;
20 import android.graphics.Matrix;
21 import android.graphics.Point;
22 import android.graphics.Rect;
23 import android.graphics.RectF;
24 import android.net.Uri;
25 import android.os.Handler;
26 import android.os.Message;
27
28 import com.android.gallery3d.filtershow.FilterShowActivity;
29 import com.android.gallery3d.filtershow.cache.ImageLoader;
30 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
31 import com.android.gallery3d.filtershow.filters.ImageFilter;
32 import com.android.gallery3d.filtershow.history.HistoryItem;
33 import com.android.gallery3d.filtershow.history.HistoryManager;
34 import com.android.gallery3d.filtershow.pipeline.Buffer;
35 import com.android.gallery3d.filtershow.pipeline.FilteringPipeline;
36 import com.android.gallery3d.filtershow.pipeline.ImagePreset;
37 import com.android.gallery3d.filtershow.pipeline.RenderingRequest;
38 import com.android.gallery3d.filtershow.pipeline.RenderingRequestCaller;
39 import com.android.gallery3d.filtershow.pipeline.SharedBuffer;
40 import com.android.gallery3d.filtershow.pipeline.SharedPreset;
41 import com.android.gallery3d.filtershow.state.StateAdapter;
42
43 import java.util.Vector;
44
45 public class MasterImage implements RenderingRequestCaller {
46
47     private static final String LOGTAG = "MasterImage";
48     private boolean DEBUG  = false;
49     private static final boolean DISABLEZOOM = false;
50     public static final int SMALL_BITMAP_DIM = 160;
51     public static final int MAX_BITMAP_DIM = 900;
52     private static MasterImage sMasterImage = null;
53
54     private boolean mSupportsHighRes = false;
55
56     private ImageFilter mCurrentFilter = null;
57     private ImagePreset mPreset = null;
58     private ImagePreset mLoadedPreset = null;
59     private ImagePreset mGeometryOnlyPreset = null;
60     private ImagePreset mFiltersOnlyPreset = null;
61
62     private SharedBuffer mPreviewBuffer = new SharedBuffer();
63     private SharedPreset mPreviewPreset = new SharedPreset();
64
65     private Bitmap mOriginalBitmapSmall = null;
66     private Bitmap mOriginalBitmapLarge = null;
67     private Bitmap mOriginalBitmapHighres = null;
68     private int mOrientation;
69     private Rect mOriginalBounds;
70     private final Vector<ImageShow> mLoadListeners = new Vector<ImageShow>();
71     private Uri mUri = null;
72     private int mZoomOrientation = ImageLoader.ORI_NORMAL;
73
74     private Bitmap mGeometryOnlyBitmap = null;
75     private Bitmap mFiltersOnlyBitmap = null;
76     private Bitmap mPartialBitmap = null;
77     private Bitmap mHighresBitmap = null;
78
79     private HistoryManager mHistory = null;
80     private StateAdapter mState = null;
81
82     private FilterShowActivity mActivity = null;
83
84     private Vector<ImageShow> mObservers = new Vector<ImageShow>();
85     private FilterRepresentation mCurrentFilterRepresentation;
86     private Vector<GeometryListener> mGeometryListeners = new Vector<GeometryListener>();
87
88     private GeometryMetadata mPreviousGeometry = null;
89
90     private float mScaleFactor = 1.0f;
91     private float mMaxScaleFactor = 3.0f; // TODO: base this on the current view / image
92     private Point mTranslation = new Point();
93     private Point mOriginalTranslation = new Point();
94
95     private Point mImageShowSize = new Point();
96
97     private boolean mShowsOriginal;
98
99     final private static int NEW_GEOMETRY = 1;
100
101     private final Handler mHandler = new Handler() {
102             @Override
103         public void handleMessage(Message msg) {
104             switch (msg.what) {
105                 case NEW_GEOMETRY: {
106                 hasNewGeometry();
107                 break;
108             }
109             }
110         }
111     };
112
113     private MasterImage() {
114     }
115
116     // TODO: remove singleton
117     public static void setMaster(MasterImage master) {
118         sMasterImage = master;
119     }
120
121     public static MasterImage getImage() {
122         if (sMasterImage == null) {
123             sMasterImage = new MasterImage();
124         }
125         return sMasterImage;
126     }
127
128     public Bitmap getOriginalBitmapSmall() {
129         return mOriginalBitmapSmall;
130     }
131
132     public Bitmap getOriginalBitmapLarge() {
133         return mOriginalBitmapLarge;
134     }
135
136     public Bitmap getOriginalBitmapHighres() {
137         return mOriginalBitmapHighres;
138     }
139
140     public void setOriginalBitmapHighres(Bitmap mOriginalBitmapHighres) {
141         this.mOriginalBitmapHighres = mOriginalBitmapHighres;
142     }
143
144     public int getOrientation() {
145         return mOrientation;
146     }
147
148     public Rect getOriginalBounds() {
149         return mOriginalBounds;
150     }
151
152     public void setOriginalBounds(Rect r) {
153         mOriginalBounds = r;
154     }
155
156     public Uri getUri() {
157         return mUri;
158     }
159
160     public void setUri(Uri uri) {
161         mUri = uri;
162     }
163
164     public int getZoomOrientation() {
165         return mZoomOrientation;
166     }
167
168     public void addListener(ImageShow imageShow) {
169         if (!mLoadListeners.contains(imageShow)) {
170             mLoadListeners.add(imageShow);
171         }
172     }
173
174     public void warnListeners() {
175         mActivity.runOnUiThread(mWarnListenersRunnable);
176     }
177
178     private Runnable mWarnListenersRunnable = new Runnable() {
179         @Override
180         public void run() {
181             for (int i = 0; i < mLoadListeners.size(); i++) {
182                 ImageShow imageShow = mLoadListeners.elementAt(i);
183                 imageShow.imageLoaded();
184             }
185             invalidatePreview();
186         }
187     };
188
189     public boolean loadBitmap(Uri uri, int size) {
190         setUri(uri);
191         mOrientation = ImageLoader.getMetadataOrientation(mActivity, uri);
192         Rect originalBounds = new Rect();
193         mOriginalBitmapLarge = ImageLoader.loadOrientedConstrainedBitmap(uri, mActivity,
194                 Math.min(MAX_BITMAP_DIM, size),
195                 mOrientation, originalBounds);
196         setOriginalBounds(originalBounds);
197         if (mOriginalBitmapLarge == null) {
198             return false;
199         }
200         int sw = SMALL_BITMAP_DIM;
201         int sh = (int) (sw * (float) mOriginalBitmapLarge.getHeight() / mOriginalBitmapLarge
202                 .getWidth());
203         mOriginalBitmapSmall = Bitmap.createScaledBitmap(mOriginalBitmapLarge, sw, sh, true);
204         mZoomOrientation = mOrientation;
205         warnListeners();
206         return true;
207     }
208
209     public void setSupportsHighRes(boolean value) {
210         mSupportsHighRes = value;
211     }
212
213     public void addObserver(ImageShow observer) {
214         if (mObservers.contains(observer)) {
215             return;
216         }
217         mObservers.add(observer);
218     }
219
220     public void setActivity(FilterShowActivity activity) {
221         mActivity = activity;
222     }
223
224     public FilterShowActivity getActivity() {
225         return mActivity;
226     }
227
228     public synchronized ImagePreset getPreset() {
229         return mPreset;
230     }
231
232     public synchronized ImagePreset getGeometryPreset() {
233         return mGeometryOnlyPreset;
234     }
235
236     public synchronized ImagePreset getFiltersOnlyPreset() {
237         return mFiltersOnlyPreset;
238     }
239
240     public synchronized void setPreset(ImagePreset preset,
241                                        FilterRepresentation change,
242                                        boolean addToHistory) {
243         if (DEBUG) {
244             preset.showFilters();
245         }
246         mPreset = preset;
247         setGeometry();
248         mPreset.fillImageStateAdapter(mState);
249         if (addToHistory) {
250             HistoryItem historyItem = new HistoryItem(mPreset, change);
251             mHistory.addHistoryItem(historyItem);
252         }
253         updatePresets(true);
254         GeometryMetadata geo = mPreset.getGeometry();
255         if (!geo.equals(mPreviousGeometry)) {
256             notifyGeometryChange();
257         }
258         mPreviousGeometry = new GeometryMetadata(geo);
259         mActivity.updateCategories();
260     }
261
262     private void setGeometry() {
263         Bitmap image = getOriginalBitmapLarge();
264         if (image == null) {
265             return;
266         }
267         float w = image.getWidth();
268         float h = image.getHeight();
269         GeometryMetadata geo = mPreset.getGeometry();
270         RectF pb = geo.getPhotoBounds();
271         if (w == pb.width() && h == pb.height()) {
272             return;
273         }
274         RectF r = new RectF(0, 0, w, h);
275         geo.setPhotoBounds(r);
276         geo.setCropBounds(r);
277         mPreset.setGeometry(geo);
278     }
279
280     public void onHistoryItemClick(int position) {
281         HistoryItem historyItem = mHistory.getItem(position);
282         // We need a copy from the history
283         ImagePreset newPreset = new ImagePreset(historyItem.getImagePreset());
284         // don't need to add it to the history
285         setPreset(newPreset, historyItem.getFilterRepresentation(), false);
286         mHistory.setCurrentPreset(position);
287     }
288
289     public HistoryManager getHistory() {
290         return mHistory;
291     }
292
293     public StateAdapter getState() {
294         return mState;
295     }
296
297     public void setHistoryManager(HistoryManager adapter) {
298         mHistory = adapter;
299     }
300
301     public void setStateAdapter(StateAdapter adapter) {
302         mState = adapter;
303     }
304
305     public void setCurrentFilter(ImageFilter filter) {
306         mCurrentFilter = filter;
307     }
308
309     public ImageFilter getCurrentFilter() {
310         return mCurrentFilter;
311     }
312
313     public synchronized boolean hasModifications() {
314         // TODO: We need to have a better same effects check to see if two
315         // presets are functionally the same. Right now, we are relying on a
316         // stricter check as equals().
317         ImagePreset loadedPreset = getLoadedPreset();
318         if (mPreset == null) {
319             if (loadedPreset == null) {
320                 return false;
321             } else {
322                 return loadedPreset.hasModifications();
323             }
324         } else {
325             if (loadedPreset == null) {
326                 return mPreset.hasModifications();
327             } else {
328                 return !mPreset.equals(loadedPreset);
329             }
330         }
331     }
332
333     public SharedBuffer getPreviewBuffer() {
334         return mPreviewBuffer;
335     }
336
337     public SharedPreset getPreviewPreset() {
338         return mPreviewPreset;
339     }
340
341     public void setOriginalGeometry(Bitmap originalBitmapLarge) {
342         GeometryMetadata geo = getPreset().getGeometry();
343         float w = originalBitmapLarge.getWidth();
344         float h = originalBitmapLarge.getHeight();
345         RectF r = new RectF(0, 0, w, h);
346         geo.setPhotoBounds(r);
347         geo.setCropBounds(r);
348         getPreset().setGeometry(geo);
349     }
350
351     public Bitmap getFilteredImage() {
352         mPreviewBuffer.swapConsumerIfNeeded(); // get latest bitmap
353         Buffer consumer = mPreviewBuffer.getConsumer();
354         if (consumer != null) {
355             return consumer.getBitmap();
356         }
357         return null;
358     }
359
360     public Bitmap getFiltersOnlyImage() {
361         return mFiltersOnlyBitmap;
362     }
363
364     public Bitmap getGeometryOnlyImage() {
365         return mGeometryOnlyBitmap;
366     }
367
368     public Bitmap getPartialImage() {
369         return mPartialBitmap;
370     }
371
372     public Bitmap getHighresImage() {
373         return mHighresBitmap;
374     }
375
376     public void notifyObservers() {
377         for (ImageShow observer : mObservers) {
378             observer.invalidate();
379         }
380     }
381
382     public void updatePresets(boolean force) {
383         if (force || mGeometryOnlyPreset == null) {
384             ImagePreset newPreset = new ImagePreset(mPreset);
385             newPreset.setDoApplyFilters(false);
386             newPreset.setDoApplyGeometry(true);
387             if (force || mGeometryOnlyPreset == null
388                     || !newPreset.same(mGeometryOnlyPreset)) {
389                 mGeometryOnlyPreset = newPreset;
390                 RenderingRequest.post(getOriginalBitmapLarge(),
391                         mGeometryOnlyPreset, RenderingRequest.GEOMETRY_RENDERING, this);
392             }
393         }
394         if (force || mFiltersOnlyPreset == null) {
395             ImagePreset newPreset = new ImagePreset(mPreset);
396             newPreset.setDoApplyFilters(true);
397             newPreset.setDoApplyGeometry(false);
398             if (force || mFiltersOnlyPreset == null
399                     || !newPreset.same(mFiltersOnlyPreset)) {
400                 mFiltersOnlyPreset = newPreset;
401                 RenderingRequest.post(MasterImage.getImage().getOriginalBitmapLarge(),
402                         mFiltersOnlyPreset, RenderingRequest.FILTERS_RENDERING, this);
403             }
404         }
405         invalidatePreview();
406     }
407
408     public FilterRepresentation getCurrentFilterRepresentation() {
409         return mCurrentFilterRepresentation;
410     }
411
412     public void setCurrentFilterRepresentation(FilterRepresentation currentFilterRepresentation) {
413         mCurrentFilterRepresentation = currentFilterRepresentation;
414     }
415
416     public void invalidateFiltersOnly() {
417         mFiltersOnlyPreset = null;
418         updatePresets(false);
419     }
420
421     public void invalidatePartialPreview() {
422         if (mPartialBitmap != null) {
423             mPartialBitmap = null;
424             notifyObservers();
425         }
426     }
427
428     public void invalidateHighresPreview() {
429         if (mHighresBitmap != null) {
430             mHighresBitmap = null;
431             notifyObservers();
432         }
433     }
434
435     public void invalidatePreview() {
436         mPreviewPreset.enqueuePreset(mPreset);
437         mPreviewBuffer.invalidate();
438         invalidatePartialPreview();
439         invalidateHighresPreview();
440         needsUpdatePartialPreview();
441         needsUpdateHighResPreview();
442         FilteringPipeline.getPipeline().updatePreviewBuffer();
443     }
444
445     public void setImageShowSize(int w, int h) {
446         if (mImageShowSize.x != w || mImageShowSize.y != h) {
447             mImageShowSize.set(w, h);
448             needsUpdatePartialPreview();
449             needsUpdateHighResPreview();
450         }
451     }
452
453     private Matrix getImageToScreenMatrix(boolean reflectRotation) {
454         GeometryMetadata geo = mPreset.getGeometry();
455         if (geo == null || getOriginalBounds() == null
456                 || mImageShowSize.x == 0) {
457             return new Matrix();
458         }
459         Matrix m = geo.getOriginalToScreen(reflectRotation,
460                 getOriginalBounds().width(),
461                 getOriginalBounds().height(), mImageShowSize.x, mImageShowSize.y);
462         Point translate = getTranslation();
463         float scaleFactor = getScaleFactor();
464         m.postTranslate(translate.x, translate.y);
465         m.postScale(scaleFactor, scaleFactor, mImageShowSize.x / 2.0f, mImageShowSize.y / 2.0f);
466         return m;
467     }
468
469     private Matrix getScreenToImageMatrix(boolean reflectRotation) {
470         Matrix m = getImageToScreenMatrix(reflectRotation);
471         Matrix invert = new Matrix();
472         m.invert(invert);
473         return invert;
474     }
475
476     public void needsUpdateHighResPreview() {
477         if (!mSupportsHighRes) {
478             return;
479         }
480         RenderingRequest.post(null, mPreset, RenderingRequest.HIGHRES_RENDERING, this);
481         invalidateHighresPreview();
482     }
483
484     public void needsUpdatePartialPreview() {
485         if (mPreset == null) {
486             return;
487         }
488         if (!mPreset.canDoPartialRendering()) {
489             invalidatePartialPreview();
490             return;
491         }
492         Matrix m = getScreenToImageMatrix(true);
493         RectF r = new RectF(0, 0, mImageShowSize.x, mImageShowSize.y);
494         RectF dest = new RectF();
495         m.mapRect(dest, r);
496         Rect bounds = new Rect();
497         dest.roundOut(bounds);
498         RenderingRequest.post(null, mPreset, RenderingRequest.PARTIAL_RENDERING,
499                 this, bounds, new Rect(0, 0, mImageShowSize.x, mImageShowSize.y));
500         invalidatePartialPreview();
501     }
502
503     @Override
504     public void available(RenderingRequest request) {
505         if (request.getBitmap() == null) {
506             return;
507         }
508
509         boolean needsCheckModification = false;
510         if (request.getType() == RenderingRequest.GEOMETRY_RENDERING) {
511             mGeometryOnlyBitmap = request.getBitmap();
512             needsCheckModification = true;
513         }
514         if (request.getType() == RenderingRequest.FILTERS_RENDERING) {
515             mFiltersOnlyBitmap = request.getBitmap();
516             needsCheckModification = true;
517         }
518         if (request.getType() == RenderingRequest.PARTIAL_RENDERING
519                 && request.getScaleFactor() == getScaleFactor()) {
520             mPartialBitmap = request.getBitmap();
521             notifyObservers();
522             needsCheckModification = true;
523         }
524         if (request.getType() == RenderingRequest.HIGHRES_RENDERING) {
525             mHighresBitmap = request.getBitmap();
526             notifyObservers();
527             needsCheckModification = true;
528         }
529         if (needsCheckModification) {
530             mActivity.enableSave(hasModifications());
531         }
532     }
533
534     public static void reset() {
535         sMasterImage = null;
536     }
537
538     public void addGeometryListener(GeometryListener listener) {
539         mGeometryListeners.add(listener);
540     }
541
542     public void notifyGeometryChange() {
543         if (mHandler.hasMessages(NEW_GEOMETRY)) {
544             return;
545         }
546         mHandler.sendEmptyMessage(NEW_GEOMETRY);
547     }
548
549     public void hasNewGeometry() {
550         updatePresets(true);
551         for (GeometryListener listener : mGeometryListeners) {
552             listener.geometryChanged();
553         }
554     }
555
556     public float getScaleFactor() {
557         return mScaleFactor;
558     }
559
560     public void setScaleFactor(float scaleFactor) {
561         if (DISABLEZOOM) {
562             return;
563         }
564         if (scaleFactor == mScaleFactor) {
565             return;
566         }
567         mScaleFactor = scaleFactor;
568         invalidatePartialPreview();
569     }
570
571     public Point getTranslation() {
572         return mTranslation;
573     }
574
575     public void setTranslation(Point translation) {
576         if (DISABLEZOOM) {
577             mTranslation.x = 0;
578             mTranslation.y = 0;
579             return;
580         }
581         mTranslation.x = translation.x;
582         mTranslation.y = translation.y;
583         needsUpdatePartialPreview();
584     }
585
586     public Point getOriginalTranslation() {
587         return mOriginalTranslation;
588     }
589
590     public void setOriginalTranslation(Point originalTranslation) {
591         if (DISABLEZOOM) {
592             return;
593         }
594         mOriginalTranslation.x = originalTranslation.x;
595         mOriginalTranslation.y = originalTranslation.y;
596     }
597
598     public void resetTranslation() {
599         mTranslation.x = 0;
600         mTranslation.y = 0;
601         needsUpdatePartialPreview();
602     }
603
604     public Bitmap getThumbnailBitmap() {
605         return getOriginalBitmapSmall();
606     }
607
608     public Bitmap getLargeThumbnailBitmap() {
609         return getOriginalBitmapLarge();
610     }
611
612     public float getMaxScaleFactor() {
613         if (DISABLEZOOM) {
614             return 1;
615         }
616         return mMaxScaleFactor;
617     }
618
619     public void setMaxScaleFactor(float maxScaleFactor) {
620         mMaxScaleFactor = maxScaleFactor;
621     }
622
623     public boolean supportsHighRes() {
624         return mSupportsHighRes;
625     }
626
627     public void setShowsOriginal(boolean value) {
628         mShowsOriginal = value;
629         notifyObservers();
630     }
631
632     public boolean showsOriginal() {
633         return mShowsOriginal;
634     }
635
636     public void setLoadedPreset(ImagePreset preset) {
637         mLoadedPreset = preset;
638     }
639
640     public ImagePreset getLoadedPreset() {
641         return mLoadedPreset;
642     }
643
644 }