1666ea1b28a76aeba74744148b15099254d918671Owen Lin/*
2666ea1b28a76aeba74744148b15099254d918671Owen Lin * Copyright (C) 2009 The Android Open Source Project
3666ea1b28a76aeba74744148b15099254d918671Owen Lin *
4666ea1b28a76aeba74744148b15099254d918671Owen Lin * Licensed under the Apache License, Version 2.0 (the "License");
5666ea1b28a76aeba74744148b15099254d918671Owen Lin * you may not use this file except in compliance with the License.
6666ea1b28a76aeba74744148b15099254d918671Owen Lin * You may obtain a copy of the License at
7666ea1b28a76aeba74744148b15099254d918671Owen Lin *
8666ea1b28a76aeba74744148b15099254d918671Owen Lin *      http://www.apache.org/licenses/LICENSE-2.0
9666ea1b28a76aeba74744148b15099254d918671Owen Lin *
10666ea1b28a76aeba74744148b15099254d918671Owen Lin * Unless required by applicable law or agreed to in writing, software
11666ea1b28a76aeba74744148b15099254d918671Owen Lin * distributed under the License is distributed on an "AS IS" BASIS,
12666ea1b28a76aeba74744148b15099254d918671Owen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13666ea1b28a76aeba74744148b15099254d918671Owen Lin * See the License for the specific language governing permissions and
14666ea1b28a76aeba74744148b15099254d918671Owen Lin * limitations under the License.
15666ea1b28a76aeba74744148b15099254d918671Owen Lin */
16666ea1b28a76aeba74744148b15099254d918671Owen Lin
17666ea1b28a76aeba74744148b15099254d918671Owen Linpackage com.android.camera;
18666ea1b28a76aeba74744148b15099254d918671Owen Lin
19666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.content.Context;
20666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.Bitmap;
21666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.Matrix;
22666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.RectF;
23666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.drawable.Drawable;
24666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.os.Handler;
25666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.util.AttributeSet;
26666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.view.KeyEvent;
27666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.widget.ImageView;
28666ea1b28a76aeba74744148b15099254d918671Owen Lin
29666ea1b28a76aeba74744148b15099254d918671Owen Linabstract class ImageViewTouchBase extends ImageView {
30666ea1b28a76aeba74744148b15099254d918671Owen Lin
31666ea1b28a76aeba74744148b15099254d918671Owen Lin    @SuppressWarnings("unused")
32666ea1b28a76aeba74744148b15099254d918671Owen Lin    private static final String TAG = "ImageViewTouchBase";
33666ea1b28a76aeba74744148b15099254d918671Owen Lin
34666ea1b28a76aeba74744148b15099254d918671Owen Lin    // This is the base transformation which is used to show the image
35666ea1b28a76aeba74744148b15099254d918671Owen Lin    // initially.  The current computation for this shows the image in
36666ea1b28a76aeba74744148b15099254d918671Owen Lin    // it's entirety, letterboxing as needed.  One could choose to
37666ea1b28a76aeba74744148b15099254d918671Owen Lin    // show the image as cropped instead.
38666ea1b28a76aeba74744148b15099254d918671Owen Lin    //
39666ea1b28a76aeba74744148b15099254d918671Owen Lin    // This matrix is recomputed when we go from the thumbnail image to
40666ea1b28a76aeba74744148b15099254d918671Owen Lin    // the full size image.
41666ea1b28a76aeba74744148b15099254d918671Owen Lin    protected Matrix mBaseMatrix = new Matrix();
42666ea1b28a76aeba74744148b15099254d918671Owen Lin
43666ea1b28a76aeba74744148b15099254d918671Owen Lin    // This is the supplementary transformation which reflects what
44666ea1b28a76aeba74744148b15099254d918671Owen Lin    // the user has done in terms of zooming and panning.
45666ea1b28a76aeba74744148b15099254d918671Owen Lin    //
46666ea1b28a76aeba74744148b15099254d918671Owen Lin    // This matrix remains the same when we go from the thumbnail image
47666ea1b28a76aeba74744148b15099254d918671Owen Lin    // to the full size image.
48666ea1b28a76aeba74744148b15099254d918671Owen Lin    protected Matrix mSuppMatrix = new Matrix();
49666ea1b28a76aeba74744148b15099254d918671Owen Lin
50666ea1b28a76aeba74744148b15099254d918671Owen Lin    // This is the final matrix which is computed as the concatentation
51666ea1b28a76aeba74744148b15099254d918671Owen Lin    // of the base matrix and the supplementary matrix.
52666ea1b28a76aeba74744148b15099254d918671Owen Lin    private final Matrix mDisplayMatrix = new Matrix();
53666ea1b28a76aeba74744148b15099254d918671Owen Lin
54666ea1b28a76aeba74744148b15099254d918671Owen Lin    // Temporary buffer used for getting the values out of a matrix.
55666ea1b28a76aeba74744148b15099254d918671Owen Lin    private final float[] mMatrixValues = new float[9];
56666ea1b28a76aeba74744148b15099254d918671Owen Lin
57666ea1b28a76aeba74744148b15099254d918671Owen Lin    // The current bitmap being displayed.
58666ea1b28a76aeba74744148b15099254d918671Owen Lin    protected final RotateBitmap mBitmapDisplayed = new RotateBitmap(null);
59666ea1b28a76aeba74744148b15099254d918671Owen Lin
60666ea1b28a76aeba74744148b15099254d918671Owen Lin    int mThisWidth = -1, mThisHeight = -1;
61666ea1b28a76aeba74744148b15099254d918671Owen Lin
62666ea1b28a76aeba74744148b15099254d918671Owen Lin    float mMaxZoom;
63666ea1b28a76aeba74744148b15099254d918671Owen Lin
64666ea1b28a76aeba74744148b15099254d918671Owen Lin    // ImageViewTouchBase will pass a Bitmap to the Recycler if it has finished
65666ea1b28a76aeba74744148b15099254d918671Owen Lin    // its use of that Bitmap.
66666ea1b28a76aeba74744148b15099254d918671Owen Lin    public interface Recycler {
67666ea1b28a76aeba74744148b15099254d918671Owen Lin        public void recycle(Bitmap b);
68666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
69666ea1b28a76aeba74744148b15099254d918671Owen Lin
70666ea1b28a76aeba74744148b15099254d918671Owen Lin    public void setRecycler(Recycler r) {
71666ea1b28a76aeba74744148b15099254d918671Owen Lin        mRecycler = r;
72666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
73666ea1b28a76aeba74744148b15099254d918671Owen Lin
74666ea1b28a76aeba74744148b15099254d918671Owen Lin    private Recycler mRecycler;
75666ea1b28a76aeba74744148b15099254d918671Owen Lin
76666ea1b28a76aeba74744148b15099254d918671Owen Lin    @Override
77666ea1b28a76aeba74744148b15099254d918671Owen Lin    protected void onLayout(boolean changed, int left, int top,
78666ea1b28a76aeba74744148b15099254d918671Owen Lin                            int right, int bottom) {
79666ea1b28a76aeba74744148b15099254d918671Owen Lin        super.onLayout(changed, left, top, right, bottom);
80666ea1b28a76aeba74744148b15099254d918671Owen Lin        mThisWidth = right - left;
81666ea1b28a76aeba74744148b15099254d918671Owen Lin        mThisHeight = bottom - top;
82666ea1b28a76aeba74744148b15099254d918671Owen Lin        Runnable r = mOnLayoutRunnable;
83666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (r != null) {
84666ea1b28a76aeba74744148b15099254d918671Owen Lin            mOnLayoutRunnable = null;
85666ea1b28a76aeba74744148b15099254d918671Owen Lin            r.run();
86666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
87666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (mBitmapDisplayed.getBitmap() != null) {
88666ea1b28a76aeba74744148b15099254d918671Owen Lin            getProperBaseMatrix(mBitmapDisplayed, mBaseMatrix);
89666ea1b28a76aeba74744148b15099254d918671Owen Lin            setImageMatrix(getImageViewMatrix());
90666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
91666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
92666ea1b28a76aeba74744148b15099254d918671Owen Lin
93666ea1b28a76aeba74744148b15099254d918671Owen Lin    @Override
94666ea1b28a76aeba74744148b15099254d918671Owen Lin    public boolean onKeyDown(int keyCode, KeyEvent event) {
95666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (keyCode == KeyEvent.KEYCODE_BACK
96666ea1b28a76aeba74744148b15099254d918671Owen Lin                && event.getRepeatCount() == 0) {
97666ea1b28a76aeba74744148b15099254d918671Owen Lin            event.startTracking();
98666ea1b28a76aeba74744148b15099254d918671Owen Lin            return true;
99666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
100666ea1b28a76aeba74744148b15099254d918671Owen Lin        return super.onKeyDown(keyCode, event);
101666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
102666ea1b28a76aeba74744148b15099254d918671Owen Lin
103666ea1b28a76aeba74744148b15099254d918671Owen Lin    @Override
104666ea1b28a76aeba74744148b15099254d918671Owen Lin    public boolean onKeyUp(int keyCode, KeyEvent event) {
105666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
106666ea1b28a76aeba74744148b15099254d918671Owen Lin                && !event.isCanceled()) {
107666ea1b28a76aeba74744148b15099254d918671Owen Lin            if (getScale() > 1.0f) {
108666ea1b28a76aeba74744148b15099254d918671Owen Lin                // If we're zoomed in, pressing Back jumps out to show the
109666ea1b28a76aeba74744148b15099254d918671Owen Lin                // entire image, otherwise Back returns the user to the gallery.
110666ea1b28a76aeba74744148b15099254d918671Owen Lin                zoomTo(1.0f);
111666ea1b28a76aeba74744148b15099254d918671Owen Lin                return true;
112666ea1b28a76aeba74744148b15099254d918671Owen Lin            }
113666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
114666ea1b28a76aeba74744148b15099254d918671Owen Lin        return super.onKeyUp(keyCode, event);
115666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
116666ea1b28a76aeba74744148b15099254d918671Owen Lin
117666ea1b28a76aeba74744148b15099254d918671Owen Lin    protected Handler mHandler = new Handler();
118666ea1b28a76aeba74744148b15099254d918671Owen Lin
119666ea1b28a76aeba74744148b15099254d918671Owen Lin    @Override
120666ea1b28a76aeba74744148b15099254d918671Owen Lin    public void setImageBitmap(Bitmap bitmap) {
121666ea1b28a76aeba74744148b15099254d918671Owen Lin        setImageBitmap(bitmap, 0);
122666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
123666ea1b28a76aeba74744148b15099254d918671Owen Lin
124666ea1b28a76aeba74744148b15099254d918671Owen Lin    private void setImageBitmap(Bitmap bitmap, int rotation) {
125666ea1b28a76aeba74744148b15099254d918671Owen Lin        super.setImageBitmap(bitmap);
126666ea1b28a76aeba74744148b15099254d918671Owen Lin        Drawable d = getDrawable();
127666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (d != null) {
128666ea1b28a76aeba74744148b15099254d918671Owen Lin            d.setDither(true);
129666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
130666ea1b28a76aeba74744148b15099254d918671Owen Lin
131666ea1b28a76aeba74744148b15099254d918671Owen Lin        Bitmap old = mBitmapDisplayed.getBitmap();
132666ea1b28a76aeba74744148b15099254d918671Owen Lin        mBitmapDisplayed.setBitmap(bitmap);
133666ea1b28a76aeba74744148b15099254d918671Owen Lin        mBitmapDisplayed.setRotation(rotation);
134666ea1b28a76aeba74744148b15099254d918671Owen Lin
135666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (old != null && old != bitmap && mRecycler != null) {
136666ea1b28a76aeba74744148b15099254d918671Owen Lin            mRecycler.recycle(old);
137666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
138666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
139666ea1b28a76aeba74744148b15099254d918671Owen Lin
140666ea1b28a76aeba74744148b15099254d918671Owen Lin    public void clear() {
141666ea1b28a76aeba74744148b15099254d918671Owen Lin        setImageBitmapResetBase(null, true);
142666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
143666ea1b28a76aeba74744148b15099254d918671Owen Lin
144666ea1b28a76aeba74744148b15099254d918671Owen Lin    private Runnable mOnLayoutRunnable = null;
145666ea1b28a76aeba74744148b15099254d918671Owen Lin
146666ea1b28a76aeba74744148b15099254d918671Owen Lin    // This function changes bitmap, reset base matrix according to the size
147666ea1b28a76aeba74744148b15099254d918671Owen Lin    // of the bitmap, and optionally reset the supplementary matrix.
148666ea1b28a76aeba74744148b15099254d918671Owen Lin    public void setImageBitmapResetBase(final Bitmap bitmap,
149666ea1b28a76aeba74744148b15099254d918671Owen Lin            final boolean resetSupp) {
150666ea1b28a76aeba74744148b15099254d918671Owen Lin        setImageRotateBitmapResetBase(new RotateBitmap(bitmap), resetSupp);
151666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
152666ea1b28a76aeba74744148b15099254d918671Owen Lin
153666ea1b28a76aeba74744148b15099254d918671Owen Lin    public void setImageRotateBitmapResetBase(final RotateBitmap bitmap,
154666ea1b28a76aeba74744148b15099254d918671Owen Lin            final boolean resetSupp) {
155666ea1b28a76aeba74744148b15099254d918671Owen Lin        final int viewWidth = getWidth();
156666ea1b28a76aeba74744148b15099254d918671Owen Lin
157666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (viewWidth <= 0)  {
158666ea1b28a76aeba74744148b15099254d918671Owen Lin            mOnLayoutRunnable = new Runnable() {
159666ea1b28a76aeba74744148b15099254d918671Owen Lin                public void run() {
160666ea1b28a76aeba74744148b15099254d918671Owen Lin                    setImageRotateBitmapResetBase(bitmap, resetSupp);
161666ea1b28a76aeba74744148b15099254d918671Owen Lin                }
162666ea1b28a76aeba74744148b15099254d918671Owen Lin            };
163666ea1b28a76aeba74744148b15099254d918671Owen Lin            return;
164666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
165666ea1b28a76aeba74744148b15099254d918671Owen Lin
166666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (bitmap.getBitmap() != null) {
167666ea1b28a76aeba74744148b15099254d918671Owen Lin            getProperBaseMatrix(bitmap, mBaseMatrix);
168666ea1b28a76aeba74744148b15099254d918671Owen Lin            setImageBitmap(bitmap.getBitmap(), bitmap.getRotation());
169666ea1b28a76aeba74744148b15099254d918671Owen Lin        } else {
170666ea1b28a76aeba74744148b15099254d918671Owen Lin            mBaseMatrix.reset();
171666ea1b28a76aeba74744148b15099254d918671Owen Lin            setImageBitmap(null);
172666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
173666ea1b28a76aeba74744148b15099254d918671Owen Lin
174666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (resetSupp) {
175666ea1b28a76aeba74744148b15099254d918671Owen Lin            mSuppMatrix.reset();
176666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
177666ea1b28a76aeba74744148b15099254d918671Owen Lin        setImageMatrix(getImageViewMatrix());
178666ea1b28a76aeba74744148b15099254d918671Owen Lin        mMaxZoom = maxZoom();
179666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
180666ea1b28a76aeba74744148b15099254d918671Owen Lin
181666ea1b28a76aeba74744148b15099254d918671Owen Lin    // Center as much as possible in one or both axis.  Centering is
182666ea1b28a76aeba74744148b15099254d918671Owen Lin    // defined as follows:  if the image is scaled down below the
183666ea1b28a76aeba74744148b15099254d918671Owen Lin    // view's dimensions then center it (literally).  If the image
184666ea1b28a76aeba74744148b15099254d918671Owen Lin    // is scaled larger than the view and is translated out of view
185666ea1b28a76aeba74744148b15099254d918671Owen Lin    // then translate it back into view (i.e. eliminate black bars).
186666ea1b28a76aeba74744148b15099254d918671Owen Lin    protected void center(boolean horizontal, boolean vertical) {
187666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (mBitmapDisplayed.getBitmap() == null) {
188666ea1b28a76aeba74744148b15099254d918671Owen Lin            return;
189666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
190666ea1b28a76aeba74744148b15099254d918671Owen Lin
191666ea1b28a76aeba74744148b15099254d918671Owen Lin        Matrix m = getImageViewMatrix();
192666ea1b28a76aeba74744148b15099254d918671Owen Lin
193666ea1b28a76aeba74744148b15099254d918671Owen Lin        RectF rect = new RectF(0, 0,
194666ea1b28a76aeba74744148b15099254d918671Owen Lin                mBitmapDisplayed.getBitmap().getWidth(),
195666ea1b28a76aeba74744148b15099254d918671Owen Lin                mBitmapDisplayed.getBitmap().getHeight());
196666ea1b28a76aeba74744148b15099254d918671Owen Lin
197666ea1b28a76aeba74744148b15099254d918671Owen Lin        m.mapRect(rect);
198666ea1b28a76aeba74744148b15099254d918671Owen Lin
199666ea1b28a76aeba74744148b15099254d918671Owen Lin        float height = rect.height();
200666ea1b28a76aeba74744148b15099254d918671Owen Lin        float width  = rect.width();
201666ea1b28a76aeba74744148b15099254d918671Owen Lin
202666ea1b28a76aeba74744148b15099254d918671Owen Lin        float deltaX = 0, deltaY = 0;
203666ea1b28a76aeba74744148b15099254d918671Owen Lin
204666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (vertical) {
205666ea1b28a76aeba74744148b15099254d918671Owen Lin            int viewHeight = getHeight();
206666ea1b28a76aeba74744148b15099254d918671Owen Lin            if (height < viewHeight) {
207666ea1b28a76aeba74744148b15099254d918671Owen Lin                deltaY = (viewHeight - height) / 2 - rect.top;
208666ea1b28a76aeba74744148b15099254d918671Owen Lin            } else if (rect.top > 0) {
209666ea1b28a76aeba74744148b15099254d918671Owen Lin                deltaY = -rect.top;
210666ea1b28a76aeba74744148b15099254d918671Owen Lin            } else if (rect.bottom < viewHeight) {
211666ea1b28a76aeba74744148b15099254d918671Owen Lin                deltaY = getHeight() - rect.bottom;
212666ea1b28a76aeba74744148b15099254d918671Owen Lin            }
213666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
214666ea1b28a76aeba74744148b15099254d918671Owen Lin
215666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (horizontal) {
216666ea1b28a76aeba74744148b15099254d918671Owen Lin            int viewWidth = getWidth();
217666ea1b28a76aeba74744148b15099254d918671Owen Lin            if (width < viewWidth) {
218666ea1b28a76aeba74744148b15099254d918671Owen Lin                deltaX = (viewWidth - width) / 2 - rect.left;
219666ea1b28a76aeba74744148b15099254d918671Owen Lin            } else if (rect.left > 0) {
220666ea1b28a76aeba74744148b15099254d918671Owen Lin                deltaX = -rect.left;
221666ea1b28a76aeba74744148b15099254d918671Owen Lin            } else if (rect.right < viewWidth) {
222666ea1b28a76aeba74744148b15099254d918671Owen Lin                deltaX = viewWidth - rect.right;
223666ea1b28a76aeba74744148b15099254d918671Owen Lin            }
224666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
225666ea1b28a76aeba74744148b15099254d918671Owen Lin
226666ea1b28a76aeba74744148b15099254d918671Owen Lin        postTranslate(deltaX, deltaY);
227666ea1b28a76aeba74744148b15099254d918671Owen Lin        setImageMatrix(getImageViewMatrix());
228666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
229666ea1b28a76aeba74744148b15099254d918671Owen Lin
230666ea1b28a76aeba74744148b15099254d918671Owen Lin    public ImageViewTouchBase(Context context) {
231666ea1b28a76aeba74744148b15099254d918671Owen Lin        super(context);
232666ea1b28a76aeba74744148b15099254d918671Owen Lin        init();
233666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
234666ea1b28a76aeba74744148b15099254d918671Owen Lin
235666ea1b28a76aeba74744148b15099254d918671Owen Lin    public ImageViewTouchBase(Context context, AttributeSet attrs) {
236666ea1b28a76aeba74744148b15099254d918671Owen Lin        super(context, attrs);
237666ea1b28a76aeba74744148b15099254d918671Owen Lin        init();
238666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
239666ea1b28a76aeba74744148b15099254d918671Owen Lin
240666ea1b28a76aeba74744148b15099254d918671Owen Lin    private void init() {
241666ea1b28a76aeba74744148b15099254d918671Owen Lin        setScaleType(ImageView.ScaleType.MATRIX);
242666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
243666ea1b28a76aeba74744148b15099254d918671Owen Lin
244666ea1b28a76aeba74744148b15099254d918671Owen Lin    protected float getValue(Matrix matrix, int whichValue) {
245666ea1b28a76aeba74744148b15099254d918671Owen Lin        matrix.getValues(mMatrixValues);
246666ea1b28a76aeba74744148b15099254d918671Owen Lin        return mMatrixValues[whichValue];
247666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
248666ea1b28a76aeba74744148b15099254d918671Owen Lin
249666ea1b28a76aeba74744148b15099254d918671Owen Lin    // Get the scale factor out of the matrix.
250666ea1b28a76aeba74744148b15099254d918671Owen Lin    protected float getScale(Matrix matrix) {
251666ea1b28a76aeba74744148b15099254d918671Owen Lin        return getValue(matrix, Matrix.MSCALE_X);
252666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
253666ea1b28a76aeba74744148b15099254d918671Owen Lin
254666ea1b28a76aeba74744148b15099254d918671Owen Lin    protected float getScale() {
255666ea1b28a76aeba74744148b15099254d918671Owen Lin        return getScale(mSuppMatrix);
256666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
257666ea1b28a76aeba74744148b15099254d918671Owen Lin
258666ea1b28a76aeba74744148b15099254d918671Owen Lin    // Setup the base matrix so that the image is centered and scaled properly.
259666ea1b28a76aeba74744148b15099254d918671Owen Lin    private void getProperBaseMatrix(RotateBitmap bitmap, Matrix matrix) {
260666ea1b28a76aeba74744148b15099254d918671Owen Lin        float viewWidth = getWidth();
261666ea1b28a76aeba74744148b15099254d918671Owen Lin        float viewHeight = getHeight();
262666ea1b28a76aeba74744148b15099254d918671Owen Lin
263666ea1b28a76aeba74744148b15099254d918671Owen Lin        float w = bitmap.getWidth();
264666ea1b28a76aeba74744148b15099254d918671Owen Lin        float h = bitmap.getHeight();
265666ea1b28a76aeba74744148b15099254d918671Owen Lin        matrix.reset();
266666ea1b28a76aeba74744148b15099254d918671Owen Lin
267be2f475fe49538e38753ad391fb3176f9b0d1c69Owen Lin        // We limit up-scaling to 3x otherwise the result may look bad if it's
268666ea1b28a76aeba74744148b15099254d918671Owen Lin        // a small icon.
269be2f475fe49538e38753ad391fb3176f9b0d1c69Owen Lin        float widthScale = Math.min(viewWidth / w, 3.0f);
270be2f475fe49538e38753ad391fb3176f9b0d1c69Owen Lin        float heightScale = Math.min(viewHeight / h, 3.0f);
271666ea1b28a76aeba74744148b15099254d918671Owen Lin        float scale = Math.min(widthScale, heightScale);
272666ea1b28a76aeba74744148b15099254d918671Owen Lin
273666ea1b28a76aeba74744148b15099254d918671Owen Lin        matrix.postConcat(bitmap.getRotateMatrix());
274666ea1b28a76aeba74744148b15099254d918671Owen Lin        matrix.postScale(scale, scale);
275666ea1b28a76aeba74744148b15099254d918671Owen Lin
276666ea1b28a76aeba74744148b15099254d918671Owen Lin        matrix.postTranslate(
277666ea1b28a76aeba74744148b15099254d918671Owen Lin                (viewWidth  - w * scale) / 2F,
278666ea1b28a76aeba74744148b15099254d918671Owen Lin                (viewHeight - h * scale) / 2F);
279666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
280666ea1b28a76aeba74744148b15099254d918671Owen Lin
281666ea1b28a76aeba74744148b15099254d918671Owen Lin    // Combine the base matrix and the supp matrix to make the final matrix.
282666ea1b28a76aeba74744148b15099254d918671Owen Lin    protected Matrix getImageViewMatrix() {
283666ea1b28a76aeba74744148b15099254d918671Owen Lin        // The final matrix is computed as the concatentation of the base matrix
284666ea1b28a76aeba74744148b15099254d918671Owen Lin        // and the supplementary matrix.
285666ea1b28a76aeba74744148b15099254d918671Owen Lin        mDisplayMatrix.set(mBaseMatrix);
286666ea1b28a76aeba74744148b15099254d918671Owen Lin        mDisplayMatrix.postConcat(mSuppMatrix);
287666ea1b28a76aeba74744148b15099254d918671Owen Lin        return mDisplayMatrix;
288666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
289666ea1b28a76aeba74744148b15099254d918671Owen Lin
290666ea1b28a76aeba74744148b15099254d918671Owen Lin    static final float SCALE_RATE = 1.25F;
291666ea1b28a76aeba74744148b15099254d918671Owen Lin
292666ea1b28a76aeba74744148b15099254d918671Owen Lin    // Sets the maximum zoom, which is a scale relative to the base matrix. It
293666ea1b28a76aeba74744148b15099254d918671Owen Lin    // is calculated to show the image at 400% zoom regardless of screen or
294666ea1b28a76aeba74744148b15099254d918671Owen Lin    // image orientation. If in the future we decode the full 3 megapixel image,
295666ea1b28a76aeba74744148b15099254d918671Owen Lin    // rather than the current 1024x768, this should be changed down to 200%.
296666ea1b28a76aeba74744148b15099254d918671Owen Lin    protected float maxZoom() {
297666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (mBitmapDisplayed.getBitmap() == null) {
298666ea1b28a76aeba74744148b15099254d918671Owen Lin            return 1F;
299666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
300666ea1b28a76aeba74744148b15099254d918671Owen Lin
301666ea1b28a76aeba74744148b15099254d918671Owen Lin        float fw = (float) mBitmapDisplayed.getWidth()  / (float) mThisWidth;
302666ea1b28a76aeba74744148b15099254d918671Owen Lin        float fh = (float) mBitmapDisplayed.getHeight() / (float) mThisHeight;
303666ea1b28a76aeba74744148b15099254d918671Owen Lin        float max = Math.max(fw, fh) * 4;
304666ea1b28a76aeba74744148b15099254d918671Owen Lin        return max;
305666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
306666ea1b28a76aeba74744148b15099254d918671Owen Lin
307666ea1b28a76aeba74744148b15099254d918671Owen Lin    protected void zoomTo(float scale, float centerX, float centerY) {
308666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (scale > mMaxZoom) {
309666ea1b28a76aeba74744148b15099254d918671Owen Lin            scale = mMaxZoom;
310666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
311666ea1b28a76aeba74744148b15099254d918671Owen Lin
312666ea1b28a76aeba74744148b15099254d918671Owen Lin        float oldScale = getScale();
313666ea1b28a76aeba74744148b15099254d918671Owen Lin        float deltaScale = scale / oldScale;
314666ea1b28a76aeba74744148b15099254d918671Owen Lin
315666ea1b28a76aeba74744148b15099254d918671Owen Lin        mSuppMatrix.postScale(deltaScale, deltaScale, centerX, centerY);
316666ea1b28a76aeba74744148b15099254d918671Owen Lin        setImageMatrix(getImageViewMatrix());
317666ea1b28a76aeba74744148b15099254d918671Owen Lin        center(true, true);
318666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
319666ea1b28a76aeba74744148b15099254d918671Owen Lin
320666ea1b28a76aeba74744148b15099254d918671Owen Lin    protected void zoomTo(final float scale, final float centerX,
321666ea1b28a76aeba74744148b15099254d918671Owen Lin                          final float centerY, final float durationMs) {
322666ea1b28a76aeba74744148b15099254d918671Owen Lin        final float incrementPerMs = (scale - getScale()) / durationMs;
323666ea1b28a76aeba74744148b15099254d918671Owen Lin        final float oldScale = getScale();
324666ea1b28a76aeba74744148b15099254d918671Owen Lin        final long startTime = System.currentTimeMillis();
325666ea1b28a76aeba74744148b15099254d918671Owen Lin
326666ea1b28a76aeba74744148b15099254d918671Owen Lin        mHandler.post(new Runnable() {
327666ea1b28a76aeba74744148b15099254d918671Owen Lin            public void run() {
328666ea1b28a76aeba74744148b15099254d918671Owen Lin                long now = System.currentTimeMillis();
329666ea1b28a76aeba74744148b15099254d918671Owen Lin                float currentMs = Math.min(durationMs, now - startTime);
330666ea1b28a76aeba74744148b15099254d918671Owen Lin                float target = oldScale + (incrementPerMs * currentMs);
331666ea1b28a76aeba74744148b15099254d918671Owen Lin                zoomTo(target, centerX, centerY);
332666ea1b28a76aeba74744148b15099254d918671Owen Lin
333666ea1b28a76aeba74744148b15099254d918671Owen Lin                if (currentMs < durationMs) {
334666ea1b28a76aeba74744148b15099254d918671Owen Lin                    mHandler.post(this);
335666ea1b28a76aeba74744148b15099254d918671Owen Lin                }
336666ea1b28a76aeba74744148b15099254d918671Owen Lin            }
337666ea1b28a76aeba74744148b15099254d918671Owen Lin        });
338666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
339666ea1b28a76aeba74744148b15099254d918671Owen Lin
340666ea1b28a76aeba74744148b15099254d918671Owen Lin    protected void zoomTo(float scale) {
341666ea1b28a76aeba74744148b15099254d918671Owen Lin        float cx = getWidth() / 2F;
342666ea1b28a76aeba74744148b15099254d918671Owen Lin        float cy = getHeight() / 2F;
343666ea1b28a76aeba74744148b15099254d918671Owen Lin
344666ea1b28a76aeba74744148b15099254d918671Owen Lin        zoomTo(scale, cx, cy);
345666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
346666ea1b28a76aeba74744148b15099254d918671Owen Lin
347666ea1b28a76aeba74744148b15099254d918671Owen Lin    protected void zoomToPoint(float scale, float pointX, float pointY) {
348666ea1b28a76aeba74744148b15099254d918671Owen Lin        float cx = getWidth() / 2F;
349666ea1b28a76aeba74744148b15099254d918671Owen Lin        float cy = getHeight() / 2F;
350666ea1b28a76aeba74744148b15099254d918671Owen Lin
351666ea1b28a76aeba74744148b15099254d918671Owen Lin        panBy(cx - pointX, cy - pointY);
352666ea1b28a76aeba74744148b15099254d918671Owen Lin        zoomTo(scale, cx, cy);
353666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
354666ea1b28a76aeba74744148b15099254d918671Owen Lin
355666ea1b28a76aeba74744148b15099254d918671Owen Lin    protected void zoomIn() {
356666ea1b28a76aeba74744148b15099254d918671Owen Lin        zoomIn(SCALE_RATE);
357666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
358666ea1b28a76aeba74744148b15099254d918671Owen Lin
359666ea1b28a76aeba74744148b15099254d918671Owen Lin    protected void zoomOut() {
360666ea1b28a76aeba74744148b15099254d918671Owen Lin        zoomOut(SCALE_RATE);
361666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
362666ea1b28a76aeba74744148b15099254d918671Owen Lin
363666ea1b28a76aeba74744148b15099254d918671Owen Lin    protected void zoomIn(float rate) {
364666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (getScale() >= mMaxZoom) {
365666ea1b28a76aeba74744148b15099254d918671Owen Lin            return;     // Don't let the user zoom into the molecular level.
366666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
367666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (mBitmapDisplayed.getBitmap() == null) {
368666ea1b28a76aeba74744148b15099254d918671Owen Lin            return;
369666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
370666ea1b28a76aeba74744148b15099254d918671Owen Lin
371666ea1b28a76aeba74744148b15099254d918671Owen Lin        float cx = getWidth() / 2F;
372666ea1b28a76aeba74744148b15099254d918671Owen Lin        float cy = getHeight() / 2F;
373666ea1b28a76aeba74744148b15099254d918671Owen Lin
374666ea1b28a76aeba74744148b15099254d918671Owen Lin        mSuppMatrix.postScale(rate, rate, cx, cy);
375666ea1b28a76aeba74744148b15099254d918671Owen Lin        setImageMatrix(getImageViewMatrix());
376666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
377666ea1b28a76aeba74744148b15099254d918671Owen Lin
378666ea1b28a76aeba74744148b15099254d918671Owen Lin    protected void zoomOut(float rate) {
379666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (mBitmapDisplayed.getBitmap() == null) {
380666ea1b28a76aeba74744148b15099254d918671Owen Lin            return;
381666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
382666ea1b28a76aeba74744148b15099254d918671Owen Lin
383666ea1b28a76aeba74744148b15099254d918671Owen Lin        float cx = getWidth() / 2F;
384666ea1b28a76aeba74744148b15099254d918671Owen Lin        float cy = getHeight() / 2F;
385666ea1b28a76aeba74744148b15099254d918671Owen Lin
386666ea1b28a76aeba74744148b15099254d918671Owen Lin        // Zoom out to at most 1x.
387666ea1b28a76aeba74744148b15099254d918671Owen Lin        Matrix tmp = new Matrix(mSuppMatrix);
388666ea1b28a76aeba74744148b15099254d918671Owen Lin        tmp.postScale(1F / rate, 1F / rate, cx, cy);
389666ea1b28a76aeba74744148b15099254d918671Owen Lin
390666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (getScale(tmp) < 1F) {
391666ea1b28a76aeba74744148b15099254d918671Owen Lin            mSuppMatrix.setScale(1F, 1F, cx, cy);
392666ea1b28a76aeba74744148b15099254d918671Owen Lin        } else {
393666ea1b28a76aeba74744148b15099254d918671Owen Lin            mSuppMatrix.postScale(1F / rate, 1F / rate, cx, cy);
394666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
395666ea1b28a76aeba74744148b15099254d918671Owen Lin        setImageMatrix(getImageViewMatrix());
396666ea1b28a76aeba74744148b15099254d918671Owen Lin        center(true, true);
397666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
398666ea1b28a76aeba74744148b15099254d918671Owen Lin
399666ea1b28a76aeba74744148b15099254d918671Owen Lin    protected void postTranslate(float dx, float dy) {
400666ea1b28a76aeba74744148b15099254d918671Owen Lin        mSuppMatrix.postTranslate(dx, dy);
401666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
402666ea1b28a76aeba74744148b15099254d918671Owen Lin
403666ea1b28a76aeba74744148b15099254d918671Owen Lin    protected void panBy(float dx, float dy) {
404666ea1b28a76aeba74744148b15099254d918671Owen Lin        postTranslate(dx, dy);
405666ea1b28a76aeba74744148b15099254d918671Owen Lin        setImageMatrix(getImageViewMatrix());
406666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
407666ea1b28a76aeba74744148b15099254d918671Owen Lin}
408