Home | History | Annotate | Download | only in imageshow
      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 MasterImage implements RenderingRequestCaller {
     52 
     53     private static final String LOGTAG = "MasterImage";
     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 MasterImage sMasterImage = 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 
    119     private MasterImage() {
    120     }
    121 
    122     // TODO: remove singleton
    123     public static void setMaster(MasterImage master) {
    124         sMasterImage = master;
    125     }
    126 
    127     public static MasterImage getImage() {
    128         if (sMasterImage == null) {
    129             sMasterImage = new MasterImage();
    130         }
    131         return sMasterImage;
    132     }
    133 
    134     public Bitmap getOriginalBitmapSmall() {
    135         return mOriginalBitmapSmall;
    136     }
    137 
    138     public Bitmap getOriginalBitmapLarge() {
    139         return mOriginalBitmapLarge;
    140     }
    141 
    142     public Bitmap getOriginalBitmapHighres() {
    143         if (mOriginalBitmapHighres == null) {
    144             return mOriginalBitmapLarge;
    145         }
    146         return mOriginalBitmapHighres;
    147     }
    148 
    149     public void setOriginalBitmapHighres(Bitmap mOriginalBitmapHighres) {
    150         this.mOriginalBitmapHighres = mOriginalBitmapHighres;
    151     }
    152 
    153     public int getOrientation() {
    154         return mOrientation;
    155     }
    156 
    157     public Rect getOriginalBounds() {
    158         return mOriginalBounds;
    159     }
    160 
    161     public void setOriginalBounds(Rect r) {
    162         mOriginalBounds = r;
    163     }
    164 
    165     public Uri getUri() {
    166         return mUri;
    167     }
    168 
    169     public void setUri(Uri uri) {
    170         mUri = uri;
    171     }
    172 
    173     public int getZoomOrientation() {
    174         return mZoomOrientation;
    175     }
    176 
    177     public void addListener(ImageShow imageShow) {
    178         if (!mLoadListeners.contains(imageShow)) {
    179             mLoadListeners.add(imageShow);
    180         }
    181     }
    182 
    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 
    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 
    219     public void setSupportsHighRes(boolean value) {
    220         mSupportsHighRes = value;
    221     }
    222 
    223     public void addObserver(ImageShow observer) {
    224         if (mObservers.contains(observer)) {
    225             return;
    226         }
    227         mObservers.add(observer);
    228     }
    229 
    230     public void removeObserver(ImageShow observer) {
    231         mObservers.remove(observer);
    232     }
    233 
    234     public void setActivity(FilterShowActivity activity) {
    235         mActivity = activity;
    236     }
    237 
    238     public FilterShowActivity getActivity() {
    239         return mActivity;
    240     }
    241 
    242     public synchronized ImagePreset getPreset() {
    243         return mPreset;
    244     }
    245 
    246     public synchronized ImagePreset getGeometryPreset() {
    247         return mGeometryOnlyPreset;
    248     }
    249 
    250     public synchronized ImagePreset getFiltersOnlyPreset() {
    251         return mFiltersOnlyPreset;
    252     }
    253 
    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 
    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 
    280     public HistoryManager getHistory() {
    281         return mHistory;
    282     }
    283 
    284     public StateAdapter getState() {
    285         return mState;
    286     }
    287 
    288     public void setHistoryManager(HistoryManager adapter) {
    289         mHistory = adapter;
    290     }
    291 
    292     public void setStateAdapter(StateAdapter adapter) {
    293         mState = adapter;
    294     }
    295 
    296     public void setCurrentFilter(ImageFilter filter) {
    297         mCurrentFilter = filter;
    298     }
    299 
    300     public ImageFilter getCurrentFilter() {
    301         return mCurrentFilter;
    302     }
    303 
    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 
    324     public SharedBuffer getPreviewBuffer() {
    325         return mPreviewBuffer;
    326     }
    327 
    328     public SharedPreset getPreviewPreset() {
    329         return mPreviewPreset;
    330     }
    331 
    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 
    341     public Bitmap getFiltersOnlyImage() {
    342         return mFiltersOnlyBitmap;
    343     }
    344 
    345     public Bitmap getGeometryOnlyImage() {
    346         return mGeometryOnlyBitmap;
    347     }
    348 
    349     public Bitmap getPartialImage() {
    350         return mPartialBitmap;
    351     }
    352 
    353     public Rect getPartialBounds() {
    354         return mPartialBounds;
    355     }
    356 
    357     public Bitmap getHighresImage() {
    358         if (mHighresBitmap == null) {
    359             return getFilteredImage();
    360         }
    361         return mHighresBitmap;
    362     }
    363 
    364     public Bitmap getPreviousImage() {
    365         return mPreviousImage;
    366     }
    367 
    368     public ImagePreset getCurrentPreset() {
    369         return getPreviewBuffer().getConsumer().getPreset();
    370     }
    371 
    372     public float getMaskScale() {
    373         return mMaskScale;
    374     }
    375 
    376     public void setMaskScale(float scale) {
    377         mMaskScale = scale;
    378         notifyObservers();
    379     }
    380 
    381     public float getAnimRotationValue() {
    382         return mAnimRotationValue;
    383     }
    384 
    385     public void setAnimRotation(float rotation) {
    386         mAnimRotationValue = mCurrentAnimRotationStartValue + rotation;
    387         notifyObservers();
    388     }
    389 
    390     public void setAnimFraction(float fraction) {
    391         mAnimFraction = fraction;
    392     }
    393 
    394     public float getAnimFraction() {
    395         return mAnimFraction;
    396     }
    397 
    398     public boolean onGoingNewLookAnimation() {
    399         return mOnGoingNewLookAnimation;
    400     }
    401 
    402     public int getCurrentLookAnimation() {
    403         return mCurrentLookAnimation;
    404     }
    405 
    406     public void resetAnimBitmap() {
    407         mBitmapCache.cache(mPreviousImage);
    408         mPreviousImage = null;
    409     }
    410 
    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 
    479     public void notifyObservers() {
    480         for (ImageShow observer : mObservers) {
    481             observer.invalidate();
    482         }
    483     }
    484 
    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 
    509     public void updatePresets(boolean force) {
    510         invalidatePreview();
    511     }
    512 
    513     public FilterRepresentation getCurrentFilterRepresentation() {
    514         return mCurrentFilterRepresentation;
    515     }
    516 
    517     public void setCurrentFilterRepresentation(FilterRepresentation currentFilterRepresentation) {
    518         mCurrentFilterRepresentation = currentFilterRepresentation;
    519     }
    520 
    521     public void invalidateFiltersOnly() {
    522         mFiltersOnlyPreset = null;
    523         invalidatePreview();
    524     }
    525 
    526     public void invalidatePartialPreview() {
    527         if (mPartialBitmap != null) {
    528             mBitmapCache.cache(mPartialBitmap);
    529             mPartialBitmap = null;
    530             notifyObservers();
    531         }
    532     }
    533 
    534     public void invalidateHighresPreview() {
    535         if (mHighresBitmap != null) {
    536             mBitmapCache.cache(mHighresBitmap);
    537             mHighresBitmap = null;
    538             notifyObservers();
    539         }
    540     }
    541 
    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 
    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 
    567     public Matrix originalImageToScreen() {
    568         return computeImageToScreen(null, 0, true);
    569     }
    570 
    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 
    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 
    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 
    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 
    659     public void needsUpdatePartialPreview() {
    660         if (mPreset == null) {
    661             return;
    662         }
    663         if (!mPreset.canDoPartialRendering()) {
    664             invalidatePartialPreview();
    665             return;
    666         }
    667         Matrix originalToScreen = MasterImage.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
    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 
    723     public static void reset() {
    724         sMasterImage = null;
    725     }
    726 
    727     public float getScaleFactor() {
    728         return mScaleFactor;
    729     }
    730 
    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 
    742     public Point getTranslation() {
    743         return mTranslation;
    744     }
    745 
    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 
    757     public Point getOriginalTranslation() {
    758         return mOriginalTranslation;
    759     }
    760 
    761     public void setOriginalTranslation(Point originalTranslation) {
    762         if (DISABLEZOOM) {
    763             return;
    764         }
    765         mOriginalTranslation.x = originalTranslation.x;
    766         mOriginalTranslation.y = originalTranslation.y;
    767     }
    768 
    769     public void resetTranslation() {
    770         mTranslation.x = 0;
    771         mTranslation.y = 0;
    772         needsUpdatePartialPreview();
    773     }
    774 
    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 
    785     public Bitmap getThumbnailBitmap() {
    786         return getOriginalBitmapSmall();
    787     }
    788 
    789     public Bitmap getLargeThumbnailBitmap() {
    790         return getOriginalBitmapLarge();
    791     }
    792 
    793     public float getMaxScaleFactor() {
    794         if (DISABLEZOOM) {
    795             return 1;
    796         }
    797         return mMaxScaleFactor;
    798     }
    799 
    800     public void setMaxScaleFactor(float maxScaleFactor) {
    801         mMaxScaleFactor = maxScaleFactor;
    802     }
    803 
    804     public boolean supportsHighRes() {
    805         return mSupportsHighRes;
    806     }
    807 
    808     public void setShowsOriginal(boolean value) {
    809         mShowsOriginal = value;
    810         notifyObservers();
    811     }
    812 
    813     public boolean showsOriginal() {
    814         return mShowsOriginal;
    815     }
    816 
    817     public void setLoadedPreset(ImagePreset preset) {
    818         mLoadedPreset = preset;
    819     }
    820 
    821     public ImagePreset getLoadedPreset() {
    822         return mLoadedPreset;
    823     }
    824 
    825     public List<ExifTag> getEXIF() {
    826         return mEXIF;
    827     }
    828 
    829     public BitmapCache getBitmapCache() {
    830         return mBitmapCache;
    831     }
    832 
    833     public boolean hasTinyPlanet() {
    834         return mPreset.contains(FilterRepresentation.TYPE_TINYPLANET);
    835     }
    836 }
    837