PositionController.java revision 532d93caddc91a7aa33ca113adbc0b8255d498eb
1ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang/* 2ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * Copyright (C) 2011 The Android Open Source Project 3ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * 4ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * Licensed under the Apache License, Version 2.0 (the "License"); 5ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * you may not use this file except in compliance with the License. 6ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * You may obtain a copy of the License at 7ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * 8ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * http://www.apache.org/licenses/LICENSE-2.0 9ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * 10ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * Unless required by applicable law or agreed to in writing, software 11ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * distributed under the License is distributed on an "AS IS" BASIS, 12ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * See the License for the specific language governing permissions and 14ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * limitations under the License. 15ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang */ 16ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 17ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Changpackage com.android.gallery3d.ui; 18ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 19ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Changimport com.android.gallery3d.R; 20ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Changimport com.android.gallery3d.app.GalleryActivity; 21ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Changimport com.android.gallery3d.common.Utils; 22ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Changimport com.android.gallery3d.data.Path; 23ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Changimport com.android.gallery3d.ui.PositionRepository.Position; 24ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 25ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Changimport android.content.Context; 26ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Changimport android.graphics.Bitmap; 27ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Changimport android.graphics.Color; 28ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Changimport android.graphics.RectF; 29ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Changimport android.os.Message; 30ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Changimport android.os.SystemClock; 31ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Changimport android.view.GestureDetector; 32ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Changimport android.view.MotionEvent; 33ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Changimport android.view.ScaleGestureDetector; 34b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Changimport android.widget.Scroller; 35ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 36ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Changclass PositionController { 37b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private static final String TAG = "PositionController"; 38ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private long mAnimationStartTime = NO_ANIMATION; 39ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private static final long NO_ANIMATION = -1; 40ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private static final long LAST_ANIMATION = -2; 41ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 42ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private int mAnimationKind; 43b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private float mAnimationDuration; 44ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private final static int ANIM_KIND_SCROLL = 0; 45ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private final static int ANIM_KIND_SCALE = 1; 46ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private final static int ANIM_KIND_SNAPBACK = 2; 47ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private final static int ANIM_KIND_SLIDE = 3; 48ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private final static int ANIM_KIND_ZOOM = 4; 49b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private final static int ANIM_KIND_FLING = 5; 50ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 51676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // Animation time in milliseconds. The order must match ANIM_KIND_* above. 52676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang private final static int ANIM_TIME[] = { 53676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 0, // ANIM_KIND_SCROLL 54676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 50, // ANIM_KIND_SCALE 55676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 600, // ANIM_KIND_SNAPBACK 56676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 400, // ANIM_KIND_SLIDE 57676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 300, // ANIM_KIND_ZOOM 58b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 0, // ANIM_KIND_FLING (the duration is calculated dynamically) 59676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang }; 60676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 61ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang // We try to scale up the image to fill the screen. But in order not to 62ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang // scale too much for small icons, we limit the max up-scaling factor here. 63ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private static final float SCALE_LIMIT = 4; 64ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 65ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private PhotoView mViewer; 66532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang private EdgeView mEdgeView; 67ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private int mImageW, mImageH; 68ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private int mViewW, mViewH; 69ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 70ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang // The X, Y are the coordinate on bitmap which shows on the center of 71676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // the view. We always keep the mCurrent{X,Y,Scale} sync with the actual 72ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang // values used currently. 73ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private int mCurrentX, mFromX, mToX; 74ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private int mCurrentY, mFromY, mToY; 75ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private float mCurrentScale, mFromScale, mToScale; 76ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 77676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // The focus point of the scaling gesture (in bitmap coordinates). 78b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private int mFocusBitmapX; 79b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private int mFocusBitmapY; 80ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private boolean mInScale; 81ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 82676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // The minimum and maximum scale we allow. 83676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang private float mScaleMin, mScaleMax = SCALE_LIMIT; 84676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 85b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // This is used by the fling animation 86b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private FlingScroller mScroller; 87b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 88b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // The bound of the stable region, see the comments above 89b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // calculateStableBound() for details. 90b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private int mBoundLeft, mBoundRight, mBoundTop, mBoundBottom; 91b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 92676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // Assume the image size is the same as view size before we know the actual 93676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // size of image. 94676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang private boolean mUseViewSize = true; 95ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 96ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private RectF mTempRect = new RectF(); 97ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private float[] mTempPoints = new float[8]; 98ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 99532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang public PositionController(PhotoView viewer, Context context, 100532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang EdgeView edgeView) { 101ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mViewer = viewer; 102532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang mEdgeView = edgeView; 103b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mScroller = new FlingScroller(); 104ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 105ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 106ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public void setImageSize(int width, int height) { 107ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 108ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang // If no image available, use view size. 109ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang if (width == 0 || height == 0) { 110ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mUseViewSize = true; 111ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mImageW = mViewW; 112ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mImageH = mViewH; 113ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mCurrentX = mImageW / 2; 114ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mCurrentY = mImageH / 2; 115ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mCurrentScale = 1; 116ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mScaleMin = 1; 117ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mViewer.setPosition(mCurrentX, mCurrentY, mCurrentScale); 118ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang return; 119ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 120ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 121ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mUseViewSize = false; 122ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 123ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang float ratio = Math.min( 124ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang (float) mImageW / width, (float) mImageH / height); 125ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 126676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // See the comment above translate() for details. 127ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mCurrentX = translate(mCurrentX, mImageW, width, ratio); 128ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mCurrentY = translate(mCurrentY, mImageH, height, ratio); 129ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mCurrentScale = mCurrentScale * ratio; 130ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 131ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mFromX = translate(mFromX, mImageW, width, ratio); 132ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mFromY = translate(mFromY, mImageH, height, ratio); 133ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mFromScale = mFromScale * ratio; 134ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 135ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mToX = translate(mToX, mImageW, width, ratio); 136ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mToY = translate(mToY, mImageH, height, ratio); 137ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mToScale = mToScale * ratio; 138ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 139b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mFocusBitmapX = translate(mFocusBitmapX, mImageW, width, ratio); 140b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mFocusBitmapY = translate(mFocusBitmapY, mImageH, height, ratio); 141b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 142ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mImageW = width; 143ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mImageH = height; 144ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 145676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang mScaleMin = getMinimalScale(mImageW, mImageH); 146ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 147676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // Start animation from the saved position if we have one. 148676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang Position position = mViewer.retrieveSavedPosition(); 149ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang if (position != null) { 150676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // The animation starts from 240 pixels and centers at the image 151676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // at the saved position. 152ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang float scale = 240f / Math.min(width, height); 153ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mCurrentX = Math.round((mViewW / 2f - position.x) / scale) + mImageW / 2; 154ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mCurrentY = Math.round((mViewH / 2f - position.y) / scale) + mImageH / 2; 155ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mCurrentScale = scale; 156ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mViewer.openAnimationStarted(); 157ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang startSnapback(); 158ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } else if (mAnimationStartTime == NO_ANIMATION) { 159ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mCurrentScale = Utils.clamp(mCurrentScale, mScaleMin, mScaleMax); 160ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 161ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mViewer.setPosition(mCurrentX, mCurrentY, mCurrentScale); 162ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 163ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 164ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public void zoomIn(float tapX, float tapY, float targetScale) { 165ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang if (targetScale > mScaleMax) targetScale = mScaleMax; 166676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 167676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // Convert the tap position to image coordinate 168b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang int tempX = Math.round((tapX - mViewW / 2) / mCurrentScale + mCurrentX); 169b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang int tempY = Math.round((tapY - mViewH / 2) / mCurrentScale + mCurrentY); 170ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 171b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang calculateStableBound(targetScale); 172b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang int targetX = Utils.clamp(tempX, mBoundLeft, mBoundRight); 173b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang int targetY = Utils.clamp(tempY, mBoundTop, mBoundBottom); 174ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 175ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang startAnimation(targetX, targetY, targetScale, ANIM_KIND_ZOOM); 176ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 177ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 178ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public void resetToFullView() { 179ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang startAnimation(mImageW / 2, mImageH / 2, mScaleMin, ANIM_KIND_ZOOM); 180ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 181ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 182676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang public float getMinimalScale(int w, int h) { 183676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang return Math.min(SCALE_LIMIT, 184676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang Math.min((float) mViewW / w, (float) mViewH / h)); 185ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 186ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 187b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // Translate a coordinate on bitmap if the bitmap size changes. 188b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // If the aspect ratio doesn't change, it's easy: 189b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // 190b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // r = w / w' (= h / h') 191b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // x' = x / r 192b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // y' = y / r 193b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // 194b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // However the aspect ratio may change. That happens when the user slides 195b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // a image before it's loaded, we don't know the actual aspect ratio, so 196b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // we will assume one. When we receive the actual bitmap size, we need to 197b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // translate the coordinate from the old bitmap into the new bitmap. 198b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // 199b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // What we want to do is center the bitmap at the original position. 200b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // 201b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // ...+--+... 202b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // . | | . 203b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // . | | . 204b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // ...+--+... 205676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // 206b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // First we scale down the new bitmap by a factor r = min(w/w', h/h'). 207b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // Overlay it onto the original bitmap. Now (0, 0) of the old bitmap maps 208b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // to (-(w-w'*r)/2 / r, -(h-h'*r)/2 / r) in the new bitmap. So (x, y) of 209b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // the old bitmap maps to (x', y') in the new bitmap, where 210b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // x' = (x-(w-w'*r)/2) / r = w'/2 + (x-w/2)/r 211b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // y' = (y-(h-h'*r)/2) / r = h'/2 + (y-h/2)/r 212676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang private static int translate(int value, int size, int newSize, float ratio) { 213676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang return Math.round(newSize / 2f + (value - size / 2f) / ratio); 214ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 215ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 216ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public void setViewSize(int viewW, int viewH) { 217ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang boolean needLayout = mViewW == 0 || mViewH == 0; 218ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 219ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mViewW = viewW; 220ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mViewH = viewH; 221ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 222ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang if (mUseViewSize) { 223ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mImageW = viewW; 224ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mImageH = viewH; 225ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mCurrentX = mImageW / 2; 226ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mCurrentY = mImageH / 2; 227ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mCurrentScale = 1; 228676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang mScaleMin = 1; 229676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang mViewer.setPosition(mCurrentX, mCurrentY, mCurrentScale); 230676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang return; 231676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang } 232676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 233676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // In most cases we want to keep the scaling factor intact when the 234676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // view size changes. The cases we want to reset the scaling factor 235676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // (to fit the view if possible) are (1) the scaling factor is too 236676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // small for the new view size (2) the scaling factor has not been 237676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // changed by the user. 238676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang boolean wasMinScale = (mCurrentScale == mScaleMin); 239676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang mScaleMin = getMinimalScale(mImageW, mImageH); 240676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 241676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang if (needLayout || mCurrentScale < mScaleMin || wasMinScale) { 242676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang mCurrentX = mImageW / 2; 243676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang mCurrentY = mImageH / 2; 244676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang mCurrentScale = mScaleMin; 245ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mViewer.setPosition(mCurrentX, mCurrentY, mCurrentScale); 246ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 247ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 248ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 249ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public void stopAnimation() { 250ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mAnimationStartTime = NO_ANIMATION; 251ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 252ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 253ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public void skipAnimation() { 254ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang if (mAnimationStartTime == NO_ANIMATION) return; 255ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mAnimationStartTime = NO_ANIMATION; 256ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mCurrentX = mToX; 257ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mCurrentY = mToY; 258ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mCurrentScale = mToScale; 259ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 260ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 261ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public void beginScale(float focusX, float focusY) { 262ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mInScale = true; 263b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mFocusBitmapX = Math.round(mCurrentX + 264b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang (focusX - mViewW / 2f) / mCurrentScale); 265b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mFocusBitmapY = Math.round(mCurrentY + 266b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang (focusY - mViewH / 2f) / mCurrentScale); 267ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 268ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 269ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public void scaleBy(float s, float focusX, float focusY) { 270ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 271676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // We want to keep the focus point (on the bitmap) the same as when 272676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // we begin the scale guesture, that is, 273676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // 274676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // mCurrentX' + (focusX - mViewW / 2f) / scale = mFocusBitmapX 275676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // 276676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang s *= getTargetScale(); 277676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang int x = Math.round(mFocusBitmapX - (focusX - mViewW / 2f) / s); 278676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang int y = Math.round(mFocusBitmapY - (focusY - mViewH / 2f) / s); 279676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 280676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang startAnimation(x, y, s, ANIM_KIND_SCALE); 281ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 282ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 283ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public void endScale() { 284ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mInScale = false; 285ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang startSnapbackIfNeeded(); 286ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 287ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 288ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public float getCurrentScale() { 289ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang return mCurrentScale; 290ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 291ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 292ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public boolean isAtMinimalScale() { 293ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang return isAlmostEquals(mCurrentScale, mScaleMin); 294ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 295ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 296ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private static boolean isAlmostEquals(float a, float b) { 297ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang float diff = a - b; 298ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang return (diff < 0 ? -diff : diff) < 0.02f; 299ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 300ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 301ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public void up() { 302ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang startSnapback(); 303ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 304ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 305676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // |<--| (1/2) * mImageW 306676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // +-------+-------+-------+ 307676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // | | | | 308676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // | | o | | 309676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // | | | | 310676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // +-------+-------+-------+ 311676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // |<----------| (3/2) * mImageW 312676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // Slide in the image from left or right. 313676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // Precondition: mCurrentScale = 1 (mView{W|H} == mImage{W|H}). 314676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // Sliding from left: mCurrentX = (1/2) * mImageW 315676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // right: mCurrentX = (3/2) * mImageW 316ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public void startSlideInAnimation(int direction) { 317ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang int fromX = (direction == PhotoView.TRANS_SLIDE_IN_LEFT) ? 318676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang mImageW / 2 : 3 * mImageW / 2; 319676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang mFromX = Math.round(fromX); 320ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mFromY = Math.round(mImageH / 2f); 321ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mCurrentX = mFromX; 322ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mCurrentY = mFromY; 323676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang startAnimation( 324676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang mImageW / 2, mImageH / 2, mCurrentScale, ANIM_KIND_SLIDE); 325ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 326ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 327ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public void startHorizontalSlide(int distance) { 328ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang scrollBy(distance, 0, ANIM_KIND_SLIDE); 329ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 330ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 331676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang private void scrollBy(float dx, float dy, int type) { 332676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang startAnimation(getTargetX() + Math.round(dx / mCurrentScale), 333676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang getTargetY() + Math.round(dy / mCurrentScale), 334676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang mCurrentScale, type); 335676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang } 336676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang 337532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang public void startScroll(float dx, float dy, boolean hasNext, 338532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang boolean hasPrev) { 339532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang int x = getTargetX() + Math.round(dx / mCurrentScale); 340532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang int y = getTargetY() + Math.round(dy / mCurrentScale); 341532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 342532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang calculateStableBound(mCurrentScale); 343532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 344532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang // Vertical direction: If we have space to move in the vertical 345532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang // direction, we show the edge effect when scrolling reaches the edge. 346532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang if (mBoundTop != mBoundBottom) { 347532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang if (y < mBoundTop) { 348532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang mEdgeView.onPull(mBoundTop - y, EdgeView.TOP); 349532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } else if (y > mBoundBottom) { 350532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang mEdgeView.onPull(y - mBoundBottom, EdgeView.BOTTOM); 351532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } 352532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } 353532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 354532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang y = Utils.clamp(y, mBoundTop, mBoundBottom); 355532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 356532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang // Horizontal direction: we show the edge effect when the scrolling 357532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang // tries to go left of the first image or go right of the last image. 358532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang if (!hasPrev && x < mBoundLeft) { 359532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang int pixels = Math.round((mBoundLeft - x) * mCurrentScale); 360532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang mEdgeView.onPull(pixels, EdgeView.LEFT); 361532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang x = mBoundLeft; 362532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } else if (!hasNext && x > mBoundRight) { 363532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang int pixels = Math.round((x - mBoundRight) * mCurrentScale); 364532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang mEdgeView.onPull(pixels, EdgeView.RIGHT); 365532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang x = mBoundRight; 366532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } 367532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 368532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang startAnimation(x, y, mCurrentScale, ANIM_KIND_SCROLL); 369532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } 370532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 371b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang public boolean fling(float velocityX, float velocityY) { 372b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // We only want to do fling when the picture is zoomed-in. 373b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang if (mImageW * mCurrentScale <= mViewW && 374b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mImageH * mCurrentScale <= mViewH) { 375b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang return false; 376b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 377b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 378b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang calculateStableBound(mCurrentScale); 379b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mScroller.fling(mCurrentX, mCurrentY, 380b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang Math.round(-velocityX / mCurrentScale), 381b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang Math.round(-velocityY / mCurrentScale), 382b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mBoundLeft, mBoundRight, mBoundTop, mBoundBottom); 383b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang int targetX = mScroller.getFinalX(); 384b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang int targetY = mScroller.getFinalY(); 385b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mAnimationDuration = mScroller.getDuration(); 386b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang startAnimation(targetX, targetY, mCurrentScale, ANIM_KIND_FLING); 387b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang return true; 388b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 389b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 390ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private void startAnimation( 391b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang int targetX, int targetY, float scale, int kind) { 392b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang if (targetX == mCurrentX && targetY == mCurrentY 393ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang && scale == mCurrentScale) return; 394ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 395ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mFromX = mCurrentX; 396ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mFromY = mCurrentY; 397ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mFromScale = mCurrentScale; 398ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 399b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mToX = targetX; 400b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mToY = targetY; 401ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mToScale = Utils.clamp(scale, 0.6f * mScaleMin, 1.4f * mScaleMax); 402ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 403b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // If the scaled height is smaller than the view height, 404ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang // force it to be in the center. 405b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // (We do for height only, not width, because the user may 406b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // want to scroll to the previous/next image.) 407ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang if (Math.floor(mImageH * mToScale) <= mViewH) { 408ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mToY = mImageH / 2; 409ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 410ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 411ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mAnimationStartTime = SystemClock.uptimeMillis(); 412ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mAnimationKind = kind; 413b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang if (mAnimationKind != ANIM_KIND_FLING) { 414b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mAnimationDuration = ANIM_TIME[mAnimationKind]; 415b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 416ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang if (advanceAnimation()) mViewer.invalidate(); 417ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 418ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 419ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang // Returns true if redraw is needed. 420ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public boolean advanceAnimation() { 421ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang if (mAnimationStartTime == NO_ANIMATION) { 422ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang return false; 423ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } else if (mAnimationStartTime == LAST_ANIMATION) { 424ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mAnimationStartTime = NO_ANIMATION; 425ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang if (mViewer.isInTransition()) { 426ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mViewer.notifyTransitionComplete(); 427ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang return false; 428ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } else { 429ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang return startSnapbackIfNeeded(); 430ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 431ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 432ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 433b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang long now = SystemClock.uptimeMillis(); 434ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang float progress; 435b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang if (mAnimationDuration == 0) { 436ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang progress = 1; 437ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } else { 438b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang progress = (now - mAnimationStartTime) / mAnimationDuration; 439ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 440ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 441ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang if (progress >= 1) { 442ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang progress = 1; 443ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mCurrentX = mToX; 444ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mCurrentY = mToY; 445ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mCurrentScale = mToScale; 446ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mAnimationStartTime = LAST_ANIMATION; 447ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } else { 448ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang float f = 1 - progress; 449676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang switch (mAnimationKind) { 450676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang case ANIM_KIND_SCROLL: 451b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang case ANIM_KIND_FLING: 452676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang progress = 1 - f; // linear 453676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang break; 454676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang case ANIM_KIND_SCALE: 455676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang progress = 1 - f * f; // quadratic 456676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang break; 457676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang case ANIM_KIND_SNAPBACK: 458676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang case ANIM_KIND_ZOOM: 459676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang case ANIM_KIND_SLIDE: 460676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang progress = 1 - f * f * f * f * f; // x^5 461676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang break; 462ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 463b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang if (mAnimationKind == ANIM_KIND_FLING) { 464b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang flingInterpolate(progress); 465b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } else { 466b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang linearInterpolate(progress); 467b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 468ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 469ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mViewer.setPosition(mCurrentX, mCurrentY, mCurrentScale); 470ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang return true; 471ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 472ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 473b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private void flingInterpolate(float progress) { 474b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mScroller.computeScrollOffset(progress); 475532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang int oldX = mCurrentX; 476532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang int oldY = mCurrentY; 477b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mCurrentX = mScroller.getCurrX(); 478b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mCurrentY = mScroller.getCurrY(); 479532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 480532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang // Check if we hit the edges; show edge effects if we do. 481532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang if (oldX > mBoundLeft && mCurrentX == mBoundLeft) { 482532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang int v = Math.round(-mScroller.getCurrVelocityX() * mCurrentScale); 483532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang mEdgeView.onAbsorb(v, EdgeView.LEFT); 484532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } else if (oldX < mBoundRight && mCurrentX == mBoundRight) { 485532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang int v = Math.round(mScroller.getCurrVelocityX() * mCurrentScale); 486532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang mEdgeView.onAbsorb(v, EdgeView.RIGHT); 487532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } 488532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 489532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang if (oldY > mBoundTop && mCurrentY == mBoundTop) { 490532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang int v = Math.round(-mScroller.getCurrVelocityY() * mCurrentScale); 491532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang mEdgeView.onAbsorb(v, EdgeView.TOP); 492532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } else if (oldY < mBoundBottom && mCurrentY == mBoundBottom) { 493532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang int v = Math.round(mScroller.getCurrVelocityY() * mCurrentScale); 494532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang mEdgeView.onAbsorb(v, EdgeView.BOTTOM); 495532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } 496b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 497b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 498676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // Interpolates mCurrent{X,Y,Scale} given the progress in [0, 1]. 499ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private void linearInterpolate(float progress) { 500676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // To linearly interpolate the position on view coordinates, we do the 501676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // following steps: 502676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // (1) convert a bitmap position (x, y) to view coordinates: 503676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // from: (x - mFromX) * mFromScale + mViewW / 2 504676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // to: (x - mToX) * mToScale + mViewW / 2 505676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // (2) interpolate between the "from" and "to" coordinates: 506676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // (x - mFromX) * mFromScale * (1 - p) + (x - mToX) * mToScale * p 507676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // + mViewW / 2 508676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // should be equal to 509676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // (x - mCurrentX) * mCurrentScale + mViewW / 2 510676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // (3) The x-related terms in the above equation can be removed because 511676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // mFromScale * (1 - p) + ToScale * p = mCurrentScale 512676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // (4) Solve for mCurrentX, we have mCurrentX = 513676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang // (mFromX * mFromScale * (1 - p) + mToX * mToScale * p) / mCurrentScale 514676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang float fromX = mFromX * mFromScale; 515676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang float toX = mToX * mToScale; 516ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang float currentX = fromX + progress * (toX - fromX); 517ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 518676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang float fromY = mFromY * mFromScale; 519676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang float toY = mToY * mToScale; 520ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang float currentY = fromY + progress * (toY - fromY); 521ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 522ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang mCurrentScale = mFromScale + progress * (mToScale - mFromScale); 523676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang mCurrentX = Math.round(currentX / mCurrentScale); 524676170e619ad59ea97d04e0edcd62b1500304845Chih-Chung Chang mCurrentY = Math.round(currentY / mCurrentScale); 525ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 526ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 527ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang // Returns true if redraw is needed. 528ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private boolean startSnapbackIfNeeded() { 529ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang if (mAnimationStartTime != NO_ANIMATION) return false; 530ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang if (mInScale) return false; 531ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang if (mAnimationKind == ANIM_KIND_SCROLL && mViewer.isDown()) { 532ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang return false; 533ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 534ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang return startSnapback(); 535ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 536ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 537ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public boolean startSnapback() { 538ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang boolean needAnimation = false; 539ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang float scale = mCurrentScale; 540ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 541ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang if (mCurrentScale < mScaleMin || mCurrentScale > mScaleMax) { 542ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang needAnimation = true; 543ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang scale = Utils.clamp(mCurrentScale, mScaleMin, mScaleMax); 544ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 545ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 546b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang calculateStableBound(scale); 547b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang int x = Utils.clamp(mCurrentX, mBoundLeft, mBoundRight); 548b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang int y = Utils.clamp(mCurrentY, mBoundTop, mBoundBottom); 549ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 550b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang if (mCurrentX != x || mCurrentY != y || mCurrentScale != scale) { 551ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang needAnimation = true; 552ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 553ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 554ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang if (needAnimation) { 555ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang startAnimation(x, y, scale, ANIM_KIND_SNAPBACK); 556ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 557ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 558ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang return needAnimation; 559ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 560ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 561b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // Calculates the stable region of mCurrent{X/Y}, where "stable" means 562b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // 563b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // (1) If the dimension of scaled image >= view dimension, we will not 564b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // see black region outside the image (at that dimension). 565b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // (2) If the dimension of scaled image < view dimension, we will center 566b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // the scaled image. 567b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // 568b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // We might temporarily go out of this stable during user interaction, 569b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // but will "snap back" after user stops interaction. 570b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // 571b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // The results are stored in mBound{Left/Right/Top/Bottom}. 572b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // 573b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private void calculateStableBound(float scale) { 574b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // The number of pixels between the center of the view 575b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // and the edge when the edge is aligned. 576b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mBoundLeft = (int) Math.ceil(mViewW / (2 * scale)); 577b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mBoundRight = mImageW - mBoundLeft; 578b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mBoundTop = (int) Math.ceil(mViewH / (2 * scale)); 579b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mBoundBottom = mImageH - mBoundTop; 580b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 581b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // If the scaled height is smaller than the view height, 582b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // force it to be in the center. 583b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang if (Math.floor(mImageH * scale) <= mViewH) { 584b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mBoundTop = mBoundBottom = mImageH / 2; 585b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 586b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 587b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // Same for width 588b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang if (Math.floor(mImageW * scale) <= mViewW) { 589b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mBoundLeft = mBoundRight = mImageW / 2; 590b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 591b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 592b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 593b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private boolean useCurrentValueAsTarget() { 594b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang return mAnimationStartTime == NO_ANIMATION || 595b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mAnimationKind == ANIM_KIND_SNAPBACK || 596b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mAnimationKind == ANIM_KIND_FLING; 597b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 598b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 599ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private float getTargetScale() { 600b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang return useCurrentValueAsTarget() ? mCurrentScale : mToScale; 601ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 602ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 603ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private int getTargetX() { 604b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang return useCurrentValueAsTarget() ? mCurrentX : mToX; 605ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 606ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 607ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang private int getTargetY() { 608b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang return useCurrentValueAsTarget() ? mCurrentY : mToY; 609ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 610ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 611ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public RectF getImageBounds() { 612ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang float points[] = mTempPoints; 613ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 614ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang /* 615ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * (p0,p1)----------(p2,p3) 616ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * | | 617ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * | | 618ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang * (p4,p5)----------(p6,p7) 619ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang */ 620ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang points[0] = points[4] = -mCurrentX; 621ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang points[1] = points[3] = -mCurrentY; 622ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang points[2] = points[6] = mImageW - mCurrentX; 623ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang points[5] = points[7] = mImageH - mCurrentY; 624ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 625ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang RectF rect = mTempRect; 626ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang rect.set(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, 627ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY); 628ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 629ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang float scale = mCurrentScale; 630ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang float offsetX = mViewW / 2; 631ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang float offsetY = mViewH / 2; 632ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang for (int i = 0; i < 4; ++i) { 633ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang float x = points[i + i] * scale + offsetX; 634ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang float y = points[i + i + 1] * scale + offsetY; 635ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang if (x < rect.left) rect.left = x; 636ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang if (x > rect.right) rect.right = x; 637ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang if (y < rect.top) rect.top = y; 638ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang if (y > rect.bottom) rect.bottom = y; 639ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 640ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang return rect; 641ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 642ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 643ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public int getImageWidth() { 644ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang return mImageW; 645ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 646ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang 647ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang public int getImageHeight() { 648ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang return mImageH; 649ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang } 650532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 651532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang public boolean isAtLeftEdge() { 652532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang calculateStableBound(mCurrentScale); 653532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang return mCurrentX <= mBoundLeft; 654532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } 655532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 656532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang public boolean isAtRightEdge() { 657532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang calculateStableBound(mCurrentScale); 658532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang return mCurrentX >= mBoundRight; 659532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } 660ec4125492f17130f65e49160a17bd437e01128a7Chih-Chung Chang} 661