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.animation.Animator;
20 import android.animation.ValueAnimator;
21 import android.graphics.Bitmap;
22 import android.graphics.Canvas;
23 import android.graphics.Matrix;
24 import android.graphics.Point;
25 import android.graphics.Rect;
26 import android.graphics.RectF;
27 import android.net.Uri;
28 
29 import com.android.gallery3d.exif.ExifTag;
30 import com.android.gallery3d.filtershow.FilterShowActivity;
31 import com.android.gallery3d.filtershow.cache.BitmapCache;
32 import com.android.gallery3d.filtershow.cache.ImageLoader;
33 import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation;
34 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
35 import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation;
36 import com.android.gallery3d.filtershow.filters.FilterUserPresetRepresentation;
37 import com.android.gallery3d.filtershow.filters.ImageFilter;
38 import com.android.gallery3d.filtershow.history.HistoryItem;
39 import com.android.gallery3d.filtershow.history.HistoryManager;
40 import com.android.gallery3d.filtershow.pipeline.Buffer;
41 import com.android.gallery3d.filtershow.pipeline.ImagePreset;
42 import com.android.gallery3d.filtershow.pipeline.RenderingRequest;
43 import com.android.gallery3d.filtershow.pipeline.RenderingRequestCaller;
44 import com.android.gallery3d.filtershow.pipeline.SharedBuffer;
45 import com.android.gallery3d.filtershow.pipeline.SharedPreset;
46 import com.android.gallery3d.filtershow.state.StateAdapter;
47 
48 import java.util.List;
49 import java.util.Vector;
50 
51 public class PrimaryImage implements RenderingRequestCaller {
52 
53     private static final String LOGTAG = "PrimaryImage";
54     private boolean DEBUG  = false;
55     private static final boolean DISABLEZOOM = false;
56     public static final int SMALL_BITMAP_DIM = 160;
57     public static final int MAX_BITMAP_DIM = 900;
58     private static PrimaryImage sPrimaryImage = null;
59 
60     private boolean mSupportsHighRes = false;
61 
62     private ImageFilter mCurrentFilter = null;
63     private ImagePreset mPreset = null;
64     private ImagePreset mLoadedPreset = null;
65     private ImagePreset mGeometryOnlyPreset = null;
66     private ImagePreset mFiltersOnlyPreset = null;
67 
68     private SharedBuffer mPreviewBuffer = new SharedBuffer();
69     private SharedPreset mPreviewPreset = new SharedPreset();
70 
71     private Bitmap mOriginalBitmapSmall = null;
72     private Bitmap mOriginalBitmapLarge = null;
73     private Bitmap mOriginalBitmapHighres = null;
74     private Bitmap mTemporaryThumbnail = null;
75     private int mOrientation;
76     private Rect mOriginalBounds;
77     private final Vector<ImageShow> mLoadListeners = new Vector<ImageShow>();
78     private Uri mUri = null;
79     private int mZoomOrientation = ImageLoader.ORI_NORMAL;
80 
81     private Bitmap mGeometryOnlyBitmap = null;
82     private Bitmap mFiltersOnlyBitmap = null;
83     private Bitmap mPartialBitmap = null;
84     private Bitmap mHighresBitmap = null;
85     private Bitmap mPreviousImage = null;
86     private int mShadowMargin = 15; // not scaled, fixed in the asset
87     private Rect mPartialBounds = new Rect();
88 
89     private ValueAnimator mAnimator = null;
90     private float mMaskScale = 1;
91     private boolean mOnGoingNewLookAnimation = false;
92     private float mAnimRotationValue = 0;
93     private float mCurrentAnimRotationStartValue = 0;
94     private float mAnimFraction = 0;
95     private int mCurrentLookAnimation = 0;
96     public static final int CIRCLE_ANIMATION = 1;
97     public static final int ROTATE_ANIMATION = 2;
98     public static final int MIRROR_ANIMATION = 3;
99 
100     private HistoryManager mHistory = null;
101     private StateAdapter mState = null;
102 
103     private FilterShowActivity mActivity = null;
104 
105     private Vector<ImageShow> mObservers = new Vector<ImageShow>();
106     private FilterRepresentation mCurrentFilterRepresentation;
107 
108     private float mScaleFactor = 1.0f;
109     private float mMaxScaleFactor = 3.0f; // TODO: base this on the current view / image
110     private Point mTranslation = new Point();
111     private Point mOriginalTranslation = new Point();
112 
113     private Point mImageShowSize = new Point();
114 
115     private boolean mShowsOriginal;
116     private List<ExifTag> mEXIF;
117     private BitmapCache mBitmapCache = new BitmapCache();
118 
PrimaryImage()119     private PrimaryImage() {
120     }
121 
122     // TODO: remove singleton
setPrimary(PrimaryImage primary)123     public static void setPrimary(PrimaryImage primary) {
124         sPrimaryImage = primary;
125     }
126 
getImage()127     public static PrimaryImage getImage() {
128         if (sPrimaryImage == null) {
129             sPrimaryImage = new PrimaryImage();
130         }
131         return sPrimaryImage;
132     }
133 
getOriginalBitmapSmall()134     public Bitmap getOriginalBitmapSmall() {
135         return mOriginalBitmapSmall;
136     }
137 
getOriginalBitmapLarge()138     public Bitmap getOriginalBitmapLarge() {
139         return mOriginalBitmapLarge;
140     }
141 
getOriginalBitmapHighres()142     public Bitmap getOriginalBitmapHighres() {
143         if (mOriginalBitmapHighres == null) {
144             return mOriginalBitmapLarge;
145         }
146         return mOriginalBitmapHighres;
147     }
148 
setOriginalBitmapHighres(Bitmap mOriginalBitmapHighres)149     public void setOriginalBitmapHighres(Bitmap mOriginalBitmapHighres) {
150         this.mOriginalBitmapHighres = mOriginalBitmapHighres;
151     }
152 
getOrientation()153     public int getOrientation() {
154         return mOrientation;
155     }
156 
getOriginalBounds()157     public Rect getOriginalBounds() {
158         return mOriginalBounds;
159     }
160 
setOriginalBounds(Rect r)161     public void setOriginalBounds(Rect r) {
162         mOriginalBounds = r;
163     }
164 
getUri()165     public Uri getUri() {
166         return mUri;
167     }
168 
setUri(Uri uri)169     public void setUri(Uri uri) {
170         mUri = uri;
171     }
172 
getZoomOrientation()173     public int getZoomOrientation() {
174         return mZoomOrientation;
175     }
176 
addListener(ImageShow imageShow)177     public void addListener(ImageShow imageShow) {
178         if (!mLoadListeners.contains(imageShow)) {
179             mLoadListeners.add(imageShow);
180         }
181     }
182 
warnListeners()183     public void warnListeners() {
184         mActivity.runOnUiThread(mWarnListenersRunnable);
185     }
186 
187     private Runnable mWarnListenersRunnable = new Runnable() {
188         @Override
189         public void run() {
190             for (int i = 0; i < mLoadListeners.size(); i++) {
191                 ImageShow imageShow = mLoadListeners.elementAt(i);
192                 imageShow.imageLoaded();
193             }
194             invalidatePreview();
195         }
196     };
197 
loadBitmap(Uri uri, int size)198     public boolean loadBitmap(Uri uri, int size) {
199         setUri(uri);
200         mEXIF = ImageLoader.getExif(getActivity(), uri);
201         mOrientation = ImageLoader.getMetadataOrientation(mActivity, uri);
202         Rect originalBounds = new Rect();
203         mOriginalBitmapLarge = ImageLoader.loadOrientedConstrainedBitmap(uri, mActivity,
204                 Math.min(MAX_BITMAP_DIM, size),
205                 mOrientation, originalBounds);
206         setOriginalBounds(originalBounds);
207         if (mOriginalBitmapLarge == null) {
208             return false;
209         }
210         int sw = SMALL_BITMAP_DIM;
211         int sh = (int) (sw * (float) mOriginalBitmapLarge.getHeight() / mOriginalBitmapLarge
212                 .getWidth());
213         mOriginalBitmapSmall = Bitmap.createScaledBitmap(mOriginalBitmapLarge, sw, sh, true);
214         mZoomOrientation = mOrientation;
215         warnListeners();
216         return true;
217     }
218 
setSupportsHighRes(boolean value)219     public void setSupportsHighRes(boolean value) {
220         mSupportsHighRes = value;
221     }
222 
addObserver(ImageShow observer)223     public void addObserver(ImageShow observer) {
224         if (mObservers.contains(observer)) {
225             return;
226         }
227         mObservers.add(observer);
228     }
229 
removeObserver(ImageShow observer)230     public void removeObserver(ImageShow observer) {
231         mObservers.remove(observer);
232     }
233 
setActivity(FilterShowActivity activity)234     public void setActivity(FilterShowActivity activity) {
235         mActivity = activity;
236     }
237 
getActivity()238     public FilterShowActivity getActivity() {
239         return mActivity;
240     }
241 
getPreset()242     public synchronized ImagePreset getPreset() {
243         return mPreset;
244     }
245 
getGeometryPreset()246     public synchronized ImagePreset getGeometryPreset() {
247         return mGeometryOnlyPreset;
248     }
249 
getFiltersOnlyPreset()250     public synchronized ImagePreset getFiltersOnlyPreset() {
251         return mFiltersOnlyPreset;
252     }
253 
setPreset(ImagePreset preset, FilterRepresentation change, boolean addToHistory)254     public synchronized void setPreset(ImagePreset preset,
255                                        FilterRepresentation change,
256                                        boolean addToHistory) {
257         if (DEBUG) {
258             preset.showFilters();
259         }
260         mPreset = preset;
261         mPreset.fillImageStateAdapter(mState);
262         if (addToHistory) {
263             HistoryItem historyItem = new HistoryItem(mPreset, change);
264             mHistory.addHistoryItem(historyItem);
265         }
266         updatePresets(true);
267         resetGeometryImages(false);
268         mActivity.updateCategories();
269     }
270 
onHistoryItemClick(int position)271     public void onHistoryItemClick(int position) {
272         HistoryItem historyItem = mHistory.getItem(position);
273         // We need a copy from the history
274         ImagePreset newPreset = new ImagePreset(historyItem.getImagePreset());
275         // don't need to add it to the history
276         setPreset(newPreset, historyItem.getFilterRepresentation(), false);
277         mHistory.setCurrentPreset(position);
278     }
279 
getHistory()280     public HistoryManager getHistory() {
281         return mHistory;
282     }
283 
getState()284     public StateAdapter getState() {
285         return mState;
286     }
287 
setHistoryManager(HistoryManager adapter)288     public void setHistoryManager(HistoryManager adapter) {
289         mHistory = adapter;
290     }
291 
setStateAdapter(StateAdapter adapter)292     public void setStateAdapter(StateAdapter adapter) {
293         mState = adapter;
294     }
295 
setCurrentFilter(ImageFilter filter)296     public void setCurrentFilter(ImageFilter filter) {
297         mCurrentFilter = filter;
298     }
299 
getCurrentFilter()300     public ImageFilter getCurrentFilter() {
301         return mCurrentFilter;
302     }
303 
hasModifications()304     public synchronized boolean hasModifications() {
305         // TODO: We need to have a better same effects check to see if two
306         // presets are functionally the same. Right now, we are relying on a
307         // stricter check as equals().
308         ImagePreset loadedPreset = getLoadedPreset();
309         if (mPreset == null) {
310             if (loadedPreset == null) {
311                 return false;
312             } else {
313                 return loadedPreset.hasModifications();
314             }
315         } else {
316             if (loadedPreset == null) {
317                 return mPreset.hasModifications();
318             } else {
319                 return !mPreset.equals(loadedPreset);
320             }
321         }
322     }
323 
getPreviewBuffer()324     public SharedBuffer getPreviewBuffer() {
325         return mPreviewBuffer;
326     }
327 
getPreviewPreset()328     public SharedPreset getPreviewPreset() {
329         return mPreviewPreset;
330     }
331 
getFilteredImage()332     public Bitmap getFilteredImage() {
333         mPreviewBuffer.swapConsumerIfNeeded(); // get latest bitmap
334         Buffer consumer = mPreviewBuffer.getConsumer();
335         if (consumer != null) {
336             return consumer.getBitmap();
337         }
338         return null;
339     }
340 
getFiltersOnlyImage()341     public Bitmap getFiltersOnlyImage() {
342         return mFiltersOnlyBitmap;
343     }
344 
getGeometryOnlyImage()345     public Bitmap getGeometryOnlyImage() {
346         return mGeometryOnlyBitmap;
347     }
348 
getPartialImage()349     public Bitmap getPartialImage() {
350         return mPartialBitmap;
351     }
352 
getPartialBounds()353     public Rect getPartialBounds() {
354         return mPartialBounds;
355     }
356 
getHighresImage()357     public Bitmap getHighresImage() {
358         if (mHighresBitmap == null) {
359             return getFilteredImage();
360         }
361         return mHighresBitmap;
362     }
363 
getPreviousImage()364     public Bitmap getPreviousImage() {
365         return mPreviousImage;
366     }
367 
getCurrentPreset()368     public ImagePreset getCurrentPreset() {
369         return getPreviewBuffer().getConsumer().getPreset();
370     }
371 
getMaskScale()372     public float getMaskScale() {
373         return mMaskScale;
374     }
375 
setMaskScale(float scale)376     public void setMaskScale(float scale) {
377         mMaskScale = scale;
378         notifyObservers();
379     }
380 
getAnimRotationValue()381     public float getAnimRotationValue() {
382         return mAnimRotationValue;
383     }
384 
setAnimRotation(float rotation)385     public void setAnimRotation(float rotation) {
386         mAnimRotationValue = mCurrentAnimRotationStartValue + rotation;
387         notifyObservers();
388     }
389 
setAnimFraction(float fraction)390     public void setAnimFraction(float fraction) {
391         mAnimFraction = fraction;
392     }
393 
getAnimFraction()394     public float getAnimFraction() {
395         return mAnimFraction;
396     }
397 
onGoingNewLookAnimation()398     public boolean onGoingNewLookAnimation() {
399         return mOnGoingNewLookAnimation;
400     }
401 
getCurrentLookAnimation()402     public int getCurrentLookAnimation() {
403         return mCurrentLookAnimation;
404     }
405 
resetAnimBitmap()406     public void resetAnimBitmap() {
407         mBitmapCache.cache(mPreviousImage);
408         mPreviousImage = null;
409     }
410 
onNewLook(FilterRepresentation newRepresentation)411     public void onNewLook(FilterRepresentation newRepresentation) {
412         if (getFilteredImage() == null) {
413             return;
414         }
415         if (mAnimator != null) {
416             mAnimator.cancel();
417             if (mCurrentLookAnimation == ROTATE_ANIMATION) {
418                 mCurrentAnimRotationStartValue += 90;
419             }
420         } else {
421             resetAnimBitmap();
422             mPreviousImage = mBitmapCache.getBitmapCopy(getFilteredImage(), BitmapCache.NEW_LOOK);
423         }
424         if (newRepresentation instanceof FilterUserPresetRepresentation) {
425             mCurrentLookAnimation = CIRCLE_ANIMATION;
426             mAnimator = ValueAnimator.ofFloat(0, 1);
427             mAnimator.setDuration(650);
428         }
429         if (newRepresentation instanceof FilterRotateRepresentation) {
430             mCurrentLookAnimation = ROTATE_ANIMATION;
431             mAnimator = ValueAnimator.ofFloat(0, 90);
432             mAnimator.setDuration(500);
433         }
434         if (newRepresentation instanceof FilterMirrorRepresentation) {
435             mCurrentLookAnimation = MIRROR_ANIMATION;
436             mAnimator = ValueAnimator.ofFloat(1, 0, -1);
437             mAnimator.setDuration(500);
438         }
439         mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
440             @Override
441             public void onAnimationUpdate(ValueAnimator animation) {
442                 if (mCurrentLookAnimation == CIRCLE_ANIMATION) {
443                     setMaskScale((Float) animation.getAnimatedValue());
444                 } else if (mCurrentLookAnimation == ROTATE_ANIMATION
445                         || mCurrentLookAnimation == MIRROR_ANIMATION) {
446                     setAnimRotation((Float) animation.getAnimatedValue());
447                     setAnimFraction(animation.getAnimatedFraction());
448                 }
449             }
450         });
451         mAnimator.addListener(new Animator.AnimatorListener() {
452             @Override
453             public void onAnimationStart(Animator animation) {
454                 mOnGoingNewLookAnimation = true;
455             }
456 
457             @Override
458             public void onAnimationEnd(Animator animation) {
459                 mOnGoingNewLookAnimation = false;
460                 mCurrentAnimRotationStartValue = 0;
461                 mAnimator = null;
462                 notifyObservers();
463             }
464 
465             @Override
466             public void onAnimationCancel(Animator animation) {
467 
468             }
469 
470             @Override
471             public void onAnimationRepeat(Animator animation) {
472 
473             }
474         });
475         mAnimator.start();
476         notifyObservers();
477     }
478 
notifyObservers()479     public void notifyObservers() {
480         for (ImageShow observer : mObservers) {
481             observer.invalidate();
482         }
483     }
484 
resetGeometryImages(boolean force)485     public void resetGeometryImages(boolean force) {
486         if (mPreset == null) {
487             return;
488         }
489         ImagePreset newPresetGeometryOnly = new ImagePreset(mPreset);
490         newPresetGeometryOnly.setDoApplyFilters(false);
491         newPresetGeometryOnly.setDoApplyGeometry(true);
492         if (force || mGeometryOnlyPreset == null
493                 || !newPresetGeometryOnly.equals(mGeometryOnlyPreset)) {
494             mGeometryOnlyPreset = newPresetGeometryOnly;
495             RenderingRequest.post(mActivity, null,
496                     mGeometryOnlyPreset, RenderingRequest.GEOMETRY_RENDERING, this);
497         }
498         ImagePreset newPresetFiltersOnly = new ImagePreset(mPreset);
499         newPresetFiltersOnly.setDoApplyFilters(true);
500         newPresetFiltersOnly.setDoApplyGeometry(false);
501         if (force || mFiltersOnlyPreset == null
502                 || !newPresetFiltersOnly.same(mFiltersOnlyPreset)) {
503             mFiltersOnlyPreset = newPresetFiltersOnly;
504             RenderingRequest.post(mActivity, null,
505                     mFiltersOnlyPreset, RenderingRequest.FILTERS_RENDERING, this);
506         }
507     }
508 
updatePresets(boolean force)509     public void updatePresets(boolean force) {
510         invalidatePreview();
511     }
512 
getCurrentFilterRepresentation()513     public FilterRepresentation getCurrentFilterRepresentation() {
514         return mCurrentFilterRepresentation;
515     }
516 
setCurrentFilterRepresentation(FilterRepresentation currentFilterRepresentation)517     public void setCurrentFilterRepresentation(FilterRepresentation currentFilterRepresentation) {
518         mCurrentFilterRepresentation = currentFilterRepresentation;
519     }
520 
invalidateFiltersOnly()521     public void invalidateFiltersOnly() {
522         mFiltersOnlyPreset = null;
523         invalidatePreview();
524     }
525 
invalidatePartialPreview()526     public void invalidatePartialPreview() {
527         if (mPartialBitmap != null) {
528             mBitmapCache.cache(mPartialBitmap);
529             mPartialBitmap = null;
530             notifyObservers();
531         }
532     }
533 
invalidateHighresPreview()534     public void invalidateHighresPreview() {
535         if (mHighresBitmap != null) {
536             mBitmapCache.cache(mHighresBitmap);
537             mHighresBitmap = null;
538             notifyObservers();
539         }
540     }
541 
invalidatePreview()542     public void invalidatePreview() {
543         if (mPreset == null) {
544             return;
545         }
546 
547         mPreviewPreset.enqueuePreset(mPreset);
548         mPreviewBuffer.invalidate();
549         invalidatePartialPreview();
550         invalidateHighresPreview();
551         needsUpdatePartialPreview();
552         needsUpdateHighResPreview();
553         mActivity.getProcessingService().updatePreviewBuffer();
554     }
555 
setImageShowSize(int w, int h)556     public void setImageShowSize(int w, int h) {
557         if (mImageShowSize.x != w || mImageShowSize.y != h) {
558             mImageShowSize.set(w, h);
559             float maxWidth = mOriginalBounds.width() / (float) w;
560             float maxHeight = mOriginalBounds.height() / (float) h;
561             mMaxScaleFactor = Math.max(3.f, Math.max(maxWidth, maxHeight));
562             needsUpdatePartialPreview();
563             needsUpdateHighResPreview();
564         }
565     }
566 
originalImageToScreen()567     public Matrix originalImageToScreen() {
568         return computeImageToScreen(null, 0, true);
569     }
570 
computeImageToScreen(Bitmap bitmapToDraw, float rotate, boolean applyGeometry)571     public Matrix computeImageToScreen(Bitmap bitmapToDraw,
572                                        float rotate,
573                                        boolean applyGeometry) {
574         if (getOriginalBounds() == null
575                 || mImageShowSize.x == 0
576                 || mImageShowSize.y == 0) {
577             return null;
578         }
579 
580         Matrix m = null;
581         float scale = 1f;
582         float translateX = 0;
583         float translateY = 0;
584 
585         if (applyGeometry) {
586             GeometryMathUtils.GeometryHolder holder = GeometryMathUtils.unpackGeometry(
587                     mPreset.getGeometryFilters());
588             m = GeometryMathUtils.getCropSelectionToScreenMatrix(null, holder,
589                     getOriginalBounds().width(), getOriginalBounds().height(),
590                     mImageShowSize.x, mImageShowSize.y);
591         } else if (bitmapToDraw != null) {
592             m = new Matrix();
593             RectF size = new RectF(0, 0,
594                     bitmapToDraw.getWidth(),
595                     bitmapToDraw.getHeight());
596             scale = mImageShowSize.x / size.width();
597             if (size.width() < size.height()) {
598                 scale = mImageShowSize.y / size.height();
599             }
600             translateX = (mImageShowSize.x - (size.width() * scale)) / 2.0f;
601             translateY = (mImageShowSize.y - (size.height() * scale)) / 2.0f;
602         } else {
603             return null;
604         }
605 
606         Point translation = getTranslation();
607         m.postScale(scale, scale);
608         m.postRotate(rotate, mImageShowSize.x / 2.0f, mImageShowSize.y / 2.0f);
609         m.postTranslate(translateX, translateY);
610         m.postTranslate(mShadowMargin, mShadowMargin);
611         m.postScale(getScaleFactor(), getScaleFactor(),
612                 mImageShowSize.x / 2.0f,
613                 mImageShowSize.y / 2.0f);
614         m.postTranslate(translation.x * getScaleFactor(),
615                         translation.y * getScaleFactor());
616         return m;
617     }
618 
getImageToScreenMatrix(boolean reflectRotation)619     private Matrix getImageToScreenMatrix(boolean reflectRotation) {
620         if (getOriginalBounds() == null || mImageShowSize.x == 0 || mImageShowSize.y == 0) {
621             return new Matrix();
622         }
623         Matrix m = GeometryMathUtils.getImageToScreenMatrix(mPreset.getGeometryFilters(),
624                 reflectRotation, getOriginalBounds(), mImageShowSize.x, mImageShowSize.y);
625         if (m == null) {
626             m = new Matrix();
627             m.reset();
628             return m;
629         }
630         Point translate = getTranslation();
631         float scaleFactor = getScaleFactor();
632         m.postTranslate(translate.x, translate.y);
633         m.postScale(scaleFactor, scaleFactor, mImageShowSize.x / 2.0f, mImageShowSize.y / 2.0f);
634         return m;
635     }
636 
getScreenToImageMatrix(boolean reflectRotation)637     private Matrix getScreenToImageMatrix(boolean reflectRotation) {
638         Matrix m = getImageToScreenMatrix(reflectRotation);
639         Matrix invert = new Matrix();
640         m.invert(invert);
641         return invert;
642     }
643 
needsUpdateHighResPreview()644     public void needsUpdateHighResPreview() {
645         if (!mSupportsHighRes) {
646             return;
647         }
648         if (mActivity.getProcessingService() == null) {
649             return;
650         }
651         if (mPreset == null) {
652             return;
653         }
654         mActivity.getProcessingService().postHighresRenderingRequest(mPreset,
655                 getScaleFactor(), this);
656         invalidateHighresPreview();
657     }
658 
needsUpdatePartialPreview()659     public void needsUpdatePartialPreview() {
660         if (mPreset == null) {
661             return;
662         }
663         if (!mPreset.canDoPartialRendering()) {
664             invalidatePartialPreview();
665             return;
666         }
667         Matrix originalToScreen = PrimaryImage.getImage().originalImageToScreen();
668         if (originalToScreen == null) {
669             return;
670         }
671         Matrix screenToOriginal = new Matrix();
672         originalToScreen.invert(screenToOriginal);
673         RectF bounds = new RectF(0, 0,
674                 mImageShowSize.x + 2 * mShadowMargin,
675                 mImageShowSize.y + 2 * mShadowMargin);
676         screenToOriginal.mapRect(bounds);
677         Rect rBounds = new Rect();
678         bounds.roundOut(rBounds);
679 
680         mActivity.getProcessingService().postFullresRenderingRequest(mPreset,
681                 getScaleFactor(), rBounds,
682                 new Rect(0, 0, mImageShowSize.x, mImageShowSize.y), this);
683         invalidatePartialPreview();
684     }
685 
686     @Override
available(RenderingRequest request)687     public void available(RenderingRequest request) {
688         if (request.getBitmap() == null) {
689             return;
690         }
691 
692         boolean needsCheckModification = false;
693         if (request.getType() == RenderingRequest.GEOMETRY_RENDERING) {
694             mBitmapCache.cache(mGeometryOnlyBitmap);
695             mGeometryOnlyBitmap = request.getBitmap();
696             needsCheckModification = true;
697         }
698         if (request.getType() == RenderingRequest.FILTERS_RENDERING) {
699             mBitmapCache.cache(mFiltersOnlyBitmap);
700             mFiltersOnlyBitmap = request.getBitmap();
701             notifyObservers();
702             needsCheckModification = true;
703         }
704         if (request.getType() == RenderingRequest.PARTIAL_RENDERING
705                 && request.getScaleFactor() == getScaleFactor()) {
706             mBitmapCache.cache(mPartialBitmap);
707             mPartialBitmap = request.getBitmap();
708             mPartialBounds.set(request.getBounds());
709             notifyObservers();
710             needsCheckModification = true;
711         }
712         if (request.getType() == RenderingRequest.HIGHRES_RENDERING) {
713             mBitmapCache.cache(mHighresBitmap);
714             mHighresBitmap = request.getBitmap();
715             notifyObservers();
716             needsCheckModification = true;
717         }
718         if (needsCheckModification) {
719             mActivity.enableSave(hasModifications());
720         }
721     }
722 
reset()723     public static void reset() {
724         sPrimaryImage = null;
725     }
726 
getScaleFactor()727     public float getScaleFactor() {
728         return mScaleFactor;
729     }
730 
setScaleFactor(float scaleFactor)731     public void setScaleFactor(float scaleFactor) {
732         if (DISABLEZOOM) {
733             return;
734         }
735         if (scaleFactor == mScaleFactor) {
736             return;
737         }
738         mScaleFactor = scaleFactor;
739         invalidatePartialPreview();
740     }
741 
getTranslation()742     public Point getTranslation() {
743         return mTranslation;
744     }
745 
setTranslation(Point translation)746     public void setTranslation(Point translation) {
747         if (DISABLEZOOM) {
748             mTranslation.x = 0;
749             mTranslation.y = 0;
750             return;
751         }
752         mTranslation.x = translation.x;
753         mTranslation.y = translation.y;
754         needsUpdatePartialPreview();
755     }
756 
getOriginalTranslation()757     public Point getOriginalTranslation() {
758         return mOriginalTranslation;
759     }
760 
setOriginalTranslation(Point originalTranslation)761     public void setOriginalTranslation(Point originalTranslation) {
762         if (DISABLEZOOM) {
763             return;
764         }
765         mOriginalTranslation.x = originalTranslation.x;
766         mOriginalTranslation.y = originalTranslation.y;
767     }
768 
resetTranslation()769     public void resetTranslation() {
770         mTranslation.x = 0;
771         mTranslation.y = 0;
772         needsUpdatePartialPreview();
773     }
774 
getTemporaryThumbnailBitmap()775     public Bitmap getTemporaryThumbnailBitmap() {
776         if (mTemporaryThumbnail == null
777                 && getOriginalBitmapSmall() != null) {
778             mTemporaryThumbnail = getOriginalBitmapSmall().copy(Bitmap.Config.ARGB_8888, true);
779             Canvas canvas = new Canvas(mTemporaryThumbnail);
780             canvas.drawARGB(200, 80, 80, 80);
781         }
782         return mTemporaryThumbnail;
783     }
784 
getThumbnailBitmap()785     public Bitmap getThumbnailBitmap() {
786         return getOriginalBitmapSmall();
787     }
788 
getLargeThumbnailBitmap()789     public Bitmap getLargeThumbnailBitmap() {
790         return getOriginalBitmapLarge();
791     }
792 
getMaxScaleFactor()793     public float getMaxScaleFactor() {
794         if (DISABLEZOOM) {
795             return 1;
796         }
797         return mMaxScaleFactor;
798     }
799 
setMaxScaleFactor(float maxScaleFactor)800     public void setMaxScaleFactor(float maxScaleFactor) {
801         mMaxScaleFactor = maxScaleFactor;
802     }
803 
supportsHighRes()804     public boolean supportsHighRes() {
805         return mSupportsHighRes;
806     }
807 
setShowsOriginal(boolean value)808     public void setShowsOriginal(boolean value) {
809         mShowsOriginal = value;
810         notifyObservers();
811     }
812 
showsOriginal()813     public boolean showsOriginal() {
814         return mShowsOriginal;
815     }
816 
setLoadedPreset(ImagePreset preset)817     public void setLoadedPreset(ImagePreset preset) {
818         mLoadedPreset = preset;
819     }
820 
getLoadedPreset()821     public ImagePreset getLoadedPreset() {
822         return mLoadedPreset;
823     }
824 
getEXIF()825     public List<ExifTag> getEXIF() {
826         return mEXIF;
827     }
828 
getBitmapCache()829     public BitmapCache getBitmapCache() {
830         return mBitmapCache;
831     }
832 
hasTinyPlanet()833     public boolean hasTinyPlanet() {
834         return mPreset.contains(FilterRepresentation.TYPE_TINYPLANET);
835     }
836 }
837