blob: 8c7fb10dba7e5d06e7c0accbebbe21393b7d7cf1 [file] [log] [blame]
Chih-Chung Changec412542011-09-26 17:34:06 +08001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.gallery3d.ui;
18
19import com.android.gallery3d.R;
20import com.android.gallery3d.app.GalleryActivity;
21import com.android.gallery3d.common.Utils;
22import com.android.gallery3d.data.Path;
23import com.android.gallery3d.ui.PositionRepository.Position;
24
25import android.content.Context;
26import android.graphics.Bitmap;
27import android.graphics.Color;
28import android.graphics.RectF;
29import android.os.Message;
30import android.os.SystemClock;
31import android.view.GestureDetector;
32import android.view.MotionEvent;
33import android.view.ScaleGestureDetector;
34
35class PositionController {
36 private long mAnimationStartTime = NO_ANIMATION;
37 private static final long NO_ANIMATION = -1;
38 private static final long LAST_ANIMATION = -2;
39
Chih-Chung Changec412542011-09-26 17:34:06 +080040 private int mAnimationKind;
41 private final static int ANIM_KIND_SCROLL = 0;
42 private final static int ANIM_KIND_SCALE = 1;
43 private final static int ANIM_KIND_SNAPBACK = 2;
44 private final static int ANIM_KIND_SLIDE = 3;
45 private final static int ANIM_KIND_ZOOM = 4;
46
Chih-Chung Chang676170e2011-09-30 18:33:17 +080047 // Animation time in milliseconds. The order must match ANIM_KIND_* above.
48 private final static int ANIM_TIME[] = {
49 0, // ANIM_KIND_SCROLL
50 50, // ANIM_KIND_SCALE
51 600, // ANIM_KIND_SNAPBACK
52 400, // ANIM_KIND_SLIDE
53 300, // ANIM_KIND_ZOOM
54 };
55
Chih-Chung Changec412542011-09-26 17:34:06 +080056 // We try to scale up the image to fill the screen. But in order not to
57 // scale too much for small icons, we limit the max up-scaling factor here.
58 private static final float SCALE_LIMIT = 4;
59
60 private PhotoView mViewer;
61 private int mImageW, mImageH;
62 private int mViewW, mViewH;
63
64 // The X, Y are the coordinate on bitmap which shows on the center of
Chih-Chung Chang676170e2011-09-30 18:33:17 +080065 // the view. We always keep the mCurrent{X,Y,Scale} sync with the actual
Chih-Chung Changec412542011-09-26 17:34:06 +080066 // values used currently.
67 private int mCurrentX, mFromX, mToX;
68 private int mCurrentY, mFromY, mToY;
69 private float mCurrentScale, mFromScale, mToScale;
70
Chih-Chung Chang676170e2011-09-30 18:33:17 +080071 // The focus point of the scaling gesture (in bitmap coordinates).
72 private float mFocusBitmapX;
73 private float mFocusBitmapY;
Chih-Chung Changec412542011-09-26 17:34:06 +080074 private boolean mInScale;
Chih-Chung Changec412542011-09-26 17:34:06 +080075
Chih-Chung Chang676170e2011-09-30 18:33:17 +080076 // The minimum and maximum scale we allow.
77 private float mScaleMin, mScaleMax = SCALE_LIMIT;
78
79 // Assume the image size is the same as view size before we know the actual
80 // size of image.
81 private boolean mUseViewSize = true;
Chih-Chung Changec412542011-09-26 17:34:06 +080082
83 private RectF mTempRect = new RectF();
84 private float[] mTempPoints = new float[8];
85
Chih-Chung Chang676170e2011-09-30 18:33:17 +080086 public PositionController(PhotoView viewer) {
Chih-Chung Changec412542011-09-26 17:34:06 +080087 mViewer = viewer;
88 }
89
90 public void setImageSize(int width, int height) {
91
92 // If no image available, use view size.
93 if (width == 0 || height == 0) {
94 mUseViewSize = true;
95 mImageW = mViewW;
96 mImageH = mViewH;
97 mCurrentX = mImageW / 2;
98 mCurrentY = mImageH / 2;
99 mCurrentScale = 1;
100 mScaleMin = 1;
101 mViewer.setPosition(mCurrentX, mCurrentY, mCurrentScale);
102 return;
103 }
104
105 mUseViewSize = false;
106
107 float ratio = Math.min(
108 (float) mImageW / width, (float) mImageH / height);
109
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800110 // See the comment above translate() for details.
Chih-Chung Changec412542011-09-26 17:34:06 +0800111 mCurrentX = translate(mCurrentX, mImageW, width, ratio);
112 mCurrentY = translate(mCurrentY, mImageH, height, ratio);
113 mCurrentScale = mCurrentScale * ratio;
114
115 mFromX = translate(mFromX, mImageW, width, ratio);
116 mFromY = translate(mFromY, mImageH, height, ratio);
117 mFromScale = mFromScale * ratio;
118
119 mToX = translate(mToX, mImageW, width, ratio);
120 mToY = translate(mToY, mImageH, height, ratio);
121 mToScale = mToScale * ratio;
122
123 mImageW = width;
124 mImageH = height;
125
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800126 mScaleMin = getMinimalScale(mImageW, mImageH);
Chih-Chung Changec412542011-09-26 17:34:06 +0800127
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800128 // Start animation from the saved position if we have one.
129 Position position = mViewer.retrieveSavedPosition();
Chih-Chung Changec412542011-09-26 17:34:06 +0800130 if (position != null) {
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800131 // The animation starts from 240 pixels and centers at the image
132 // at the saved position.
Chih-Chung Changec412542011-09-26 17:34:06 +0800133 float scale = 240f / Math.min(width, height);
134 mCurrentX = Math.round((mViewW / 2f - position.x) / scale) + mImageW / 2;
135 mCurrentY = Math.round((mViewH / 2f - position.y) / scale) + mImageH / 2;
136 mCurrentScale = scale;
137 mViewer.openAnimationStarted();
138 startSnapback();
139 } else if (mAnimationStartTime == NO_ANIMATION) {
140 mCurrentScale = Utils.clamp(mCurrentScale, mScaleMin, mScaleMax);
141 }
142 mViewer.setPosition(mCurrentX, mCurrentY, mCurrentScale);
143 }
144
145 public void zoomIn(float tapX, float tapY, float targetScale) {
146 if (targetScale > mScaleMax) targetScale = mScaleMax;
147 float scale = mCurrentScale;
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800148
149 // Convert the tap position to image coordinate
Chih-Chung Changec412542011-09-26 17:34:06 +0800150 float tempX = (tapX - mViewW / 2) / mCurrentScale + mCurrentX;
151 float tempY = (tapY - mViewH / 2) / mCurrentScale + mCurrentY;
152
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800153 // We want to make sure that after zoom-in, we don't see black regions
154 // because we zoom too close to the border. The conditions are:
155 //
156 // (mViewW / 2) / targetScale + mCurrentX < mImageW
157 // -(mViewW / 2) / targetScale + mCurrentX > 0
Chih-Chung Changec412542011-09-26 17:34:06 +0800158 float min = mViewW / 2.0f / targetScale;
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800159 float max = mImageW - mViewW / 2.0f / targetScale;
Chih-Chung Changec412542011-09-26 17:34:06 +0800160 int targetX = (int) Utils.clamp(tempX, min, max);
161
162 min = mViewH / 2.0f / targetScale;
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800163 max = mImageH - mViewH / 2.0f / targetScale;
164 int targetY = (int) Utils.clamp(tempY, min, max);
Chih-Chung Changec412542011-09-26 17:34:06 +0800165
166 // If the width of the image is less then the view, center the image
167 if (mImageW * targetScale < mViewW) targetX = mImageW / 2;
168 if (mImageH * targetScale < mViewH) targetY = mImageH / 2;
169
170 startAnimation(targetX, targetY, targetScale, ANIM_KIND_ZOOM);
171 }
172
173 public void resetToFullView() {
174 startAnimation(mImageW / 2, mImageH / 2, mScaleMin, ANIM_KIND_ZOOM);
175 }
176
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800177 public float getMinimalScale(int w, int h) {
178 return Math.min(SCALE_LIMIT,
179 Math.min((float) mViewW / w, (float) mViewH / h));
Chih-Chung Changec412542011-09-26 17:34:06 +0800180 }
181
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800182 // Translate the coordinate if the aspect ratio of the image changes.
183 // When the user slides a image before it's loaded, we don't know the
184 // actual aspect ratio, so we will assume one. When we receive the actual
185 // aspect ratio, we need to translate the coordinate from the old (assumed)
186 // bitmap into the new (actual) bitmap.
187 //
188 // +-------------------------+ "o" is where center of the view
189 // | +--------+ | is. mCurrent{X,Y} is the coordinate of
190 // | | | | "o" relative to the old bitmap. Assume
191 // | | o | | the old bitmap size is (w, h). The new
192 // | +--------+ | bitmap size is (w', h'). First we adjust
193 // | | mCurrentScale by factor r = min(w/w',
194 // +-------------------------+ h/h'), so one of the sides matches the old
195 // | bitmap (w'*r == w or h'*r == h).
196 // v
197 // +-------------------------+ Then we put the new scaled bitmap to the
198 // | +--+ .......... | center of the original bitmap's bounding
199 // | | | . . | box. The center of the old bitmap and the
200 // | | | . o . | new bitmap must match in view coordinate:
201 // | +--+ .......... |
202 // | | (w/2 - mCurrentX) * mCurrentScale =
203 // +-------------------------+ (w'/2 - mCurrentX') * mCurrentScale * r
204 // |
205 // v Solve for mCurrentX' we have:
206 // +-------------------------+
207 // | ...+--+... | mCurrentX' = w'/2 + (mCurrentX - w/2) / r
208 // | . | | . |
209 // | . o| | . |
210 // | ...+--+... |
211 // | |
212 // +-------------------------+
213 private static int translate(int value, int size, int newSize, float ratio) {
214 return Math.round(newSize / 2f + (value - size / 2f) / ratio);
Chih-Chung Changec412542011-09-26 17:34:06 +0800215 }
216
217 public void setViewSize(int viewW, int viewH) {
218 boolean needLayout = mViewW == 0 || mViewH == 0;
219
220 mViewW = viewW;
221 mViewH = viewH;
222
223 if (mUseViewSize) {
224 mImageW = viewW;
225 mImageH = viewH;
226 mCurrentX = mImageW / 2;
227 mCurrentY = mImageH / 2;
228 mCurrentScale = 1;
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800229 mScaleMin = 1;
Chih-Chung Changec412542011-09-26 17:34:06 +0800230 mViewer.setPosition(mCurrentX, mCurrentY, mCurrentScale);
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800231 return;
232 }
233
234 // In most cases we want to keep the scaling factor intact when the
235 // view size changes. The cases we want to reset the scaling factor
236 // (to fit the view if possible) are (1) the scaling factor is too
237 // small for the new view size (2) the scaling factor has not been
238 // changed by the user.
239 boolean wasMinScale = (mCurrentScale == mScaleMin);
240 mScaleMin = getMinimalScale(mImageW, mImageH);
241
242 if (needLayout || mCurrentScale < mScaleMin || wasMinScale) {
243 mCurrentX = mImageW / 2;
244 mCurrentY = mImageH / 2;
245 mCurrentScale = mScaleMin;
246 mViewer.setPosition(mCurrentX, mCurrentY, mCurrentScale);
Chih-Chung Changec412542011-09-26 17:34:06 +0800247 }
248 }
249
250 public void stopAnimation() {
251 mAnimationStartTime = NO_ANIMATION;
252 }
253
254 public void skipAnimation() {
255 if (mAnimationStartTime == NO_ANIMATION) return;
256 mAnimationStartTime = NO_ANIMATION;
257 mCurrentX = mToX;
258 mCurrentY = mToY;
259 mCurrentScale = mToScale;
260 }
261
Chih-Chung Changec412542011-09-26 17:34:06 +0800262 public void beginScale(float focusX, float focusY) {
263 mInScale = true;
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800264 mFocusBitmapX = mCurrentX + (focusX - mViewW / 2f) / mCurrentScale;
265 mFocusBitmapY = mCurrentY + (focusY - mViewH / 2f) / mCurrentScale;
Chih-Chung Changec412542011-09-26 17:34:06 +0800266 }
267
268 public void scaleBy(float s, float focusX, float focusY) {
269
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800270 // We want to keep the focus point (on the bitmap) the same as when
271 // we begin the scale guesture, that is,
272 //
273 // mCurrentX' + (focusX - mViewW / 2f) / scale = mFocusBitmapX
274 //
275 s *= getTargetScale();
276 int x = Math.round(mFocusBitmapX - (focusX - mViewW / 2f) / s);
277 int y = Math.round(mFocusBitmapY - (focusY - mViewH / 2f) / s);
Chih-Chung Changec412542011-09-26 17:34:06 +0800278
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800279 startAnimation(x, y, s, ANIM_KIND_SCALE);
Chih-Chung Changec412542011-09-26 17:34:06 +0800280 }
281
282 public void endScale() {
283 mInScale = false;
284 startSnapbackIfNeeded();
285 }
286
287 public float getCurrentScale() {
288 return mCurrentScale;
289 }
290
291 public boolean isAtMinimalScale() {
292 return isAlmostEquals(mCurrentScale, mScaleMin);
293 }
294
295 private static boolean isAlmostEquals(float a, float b) {
296 float diff = a - b;
297 return (diff < 0 ? -diff : diff) < 0.02f;
298 }
299
300 public void up() {
301 startSnapback();
302 }
303
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800304 // |<--| (1/2) * mImageW
305 // +-------+-------+-------+
306 // | | | |
307 // | | o | |
308 // | | | |
309 // +-------+-------+-------+
310 // |<----------| (3/2) * mImageW
311 // Slide in the image from left or right.
312 // Precondition: mCurrentScale = 1 (mView{W|H} == mImage{W|H}).
313 // Sliding from left: mCurrentX = (1/2) * mImageW
314 // right: mCurrentX = (3/2) * mImageW
Chih-Chung Changec412542011-09-26 17:34:06 +0800315 public void startSlideInAnimation(int direction) {
316 int fromX = (direction == PhotoView.TRANS_SLIDE_IN_LEFT) ?
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800317 mImageW / 2 : 3 * mImageW / 2;
318 mFromX = Math.round(fromX);
Chih-Chung Changec412542011-09-26 17:34:06 +0800319 mFromY = Math.round(mImageH / 2f);
320 mCurrentX = mFromX;
321 mCurrentY = mFromY;
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800322 startAnimation(
323 mImageW / 2, mImageH / 2, mCurrentScale, ANIM_KIND_SLIDE);
Chih-Chung Changec412542011-09-26 17:34:06 +0800324 }
325
326 public void startHorizontalSlide(int distance) {
327 scrollBy(distance, 0, ANIM_KIND_SLIDE);
328 }
329
330 public void startScroll(float dx, float dy) {
331 scrollBy(dx, dy, ANIM_KIND_SCROLL);
332 }
333
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800334 private void scrollBy(float dx, float dy, int type) {
335 startAnimation(getTargetX() + Math.round(dx / mCurrentScale),
336 getTargetY() + Math.round(dy / mCurrentScale),
337 mCurrentScale, type);
338 }
339
Chih-Chung Changec412542011-09-26 17:34:06 +0800340 private void startAnimation(
341 int centerX, int centerY, float scale, int kind) {
342 if (centerX == mCurrentX && centerY == mCurrentY
343 && scale == mCurrentScale) return;
344
345 mFromX = mCurrentX;
346 mFromY = mCurrentY;
347 mFromScale = mCurrentScale;
348
349 mToX = centerX;
350 mToY = centerY;
351 mToScale = Utils.clamp(scale, 0.6f * mScaleMin, 1.4f * mScaleMax);
352
353 // If the scaled dimension is smaller than the view,
354 // force it to be in the center.
355 if (Math.floor(mImageH * mToScale) <= mViewH) {
356 mToY = mImageH / 2;
357 }
358
359 mAnimationStartTime = SystemClock.uptimeMillis();
360 mAnimationKind = kind;
361 if (advanceAnimation()) mViewer.invalidate();
362 }
363
364 // Returns true if redraw is needed.
365 public boolean advanceAnimation() {
366 if (mAnimationStartTime == NO_ANIMATION) {
367 return false;
368 } else if (mAnimationStartTime == LAST_ANIMATION) {
369 mAnimationStartTime = NO_ANIMATION;
370 if (mViewer.isInTransition()) {
371 mViewer.notifyTransitionComplete();
372 return false;
373 } else {
374 return startSnapbackIfNeeded();
375 }
376 }
377
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800378 float animationTime = ANIM_TIME[mAnimationKind];
Chih-Chung Changec412542011-09-26 17:34:06 +0800379 float progress;
380 if (animationTime == 0) {
381 progress = 1;
382 } else {
383 long now = SystemClock.uptimeMillis();
384 progress = (now - mAnimationStartTime) / animationTime;
385 }
386
387 if (progress >= 1) {
388 progress = 1;
389 mCurrentX = mToX;
390 mCurrentY = mToY;
391 mCurrentScale = mToScale;
392 mAnimationStartTime = LAST_ANIMATION;
393 } else {
394 float f = 1 - progress;
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800395 switch (mAnimationKind) {
396 case ANIM_KIND_SCROLL:
397 progress = 1 - f; // linear
398 break;
399 case ANIM_KIND_SCALE:
400 progress = 1 - f * f; // quadratic
401 break;
402 case ANIM_KIND_SNAPBACK:
403 case ANIM_KIND_ZOOM:
404 case ANIM_KIND_SLIDE:
405 progress = 1 - f * f * f * f * f; // x^5
406 break;
Chih-Chung Changec412542011-09-26 17:34:06 +0800407 }
408 linearInterpolate(progress);
409 }
410 mViewer.setPosition(mCurrentX, mCurrentY, mCurrentScale);
411 return true;
412 }
413
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800414 // Interpolates mCurrent{X,Y,Scale} given the progress in [0, 1].
Chih-Chung Changec412542011-09-26 17:34:06 +0800415 private void linearInterpolate(float progress) {
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800416 // To linearly interpolate the position on view coordinates, we do the
417 // following steps:
418 // (1) convert a bitmap position (x, y) to view coordinates:
419 // from: (x - mFromX) * mFromScale + mViewW / 2
420 // to: (x - mToX) * mToScale + mViewW / 2
421 // (2) interpolate between the "from" and "to" coordinates:
422 // (x - mFromX) * mFromScale * (1 - p) + (x - mToX) * mToScale * p
423 // + mViewW / 2
424 // should be equal to
425 // (x - mCurrentX) * mCurrentScale + mViewW / 2
426 // (3) The x-related terms in the above equation can be removed because
427 // mFromScale * (1 - p) + ToScale * p = mCurrentScale
428 // (4) Solve for mCurrentX, we have mCurrentX =
429 // (mFromX * mFromScale * (1 - p) + mToX * mToScale * p) / mCurrentScale
430 float fromX = mFromX * mFromScale;
431 float toX = mToX * mToScale;
Chih-Chung Changec412542011-09-26 17:34:06 +0800432 float currentX = fromX + progress * (toX - fromX);
433
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800434 float fromY = mFromY * mFromScale;
435 float toY = mToY * mToScale;
Chih-Chung Changec412542011-09-26 17:34:06 +0800436 float currentY = fromY + progress * (toY - fromY);
437
438 mCurrentScale = mFromScale + progress * (mToScale - mFromScale);
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800439 mCurrentX = Math.round(currentX / mCurrentScale);
440 mCurrentY = Math.round(currentY / mCurrentScale);
Chih-Chung Changec412542011-09-26 17:34:06 +0800441 }
442
443 // Returns true if redraw is needed.
444 private boolean startSnapbackIfNeeded() {
445 if (mAnimationStartTime != NO_ANIMATION) return false;
446 if (mInScale) return false;
447 if (mAnimationKind == ANIM_KIND_SCROLL && mViewer.isDown()) {
448 return false;
449 }
450 return startSnapback();
451 }
452
453 public boolean startSnapback() {
454 boolean needAnimation = false;
455 int x = mCurrentX;
456 int y = mCurrentY;
457 float scale = mCurrentScale;
458
459 if (mCurrentScale < mScaleMin || mCurrentScale > mScaleMax) {
460 needAnimation = true;
461 scale = Utils.clamp(mCurrentScale, mScaleMin, mScaleMax);
462 }
463
Chih-Chung Chang676170e2011-09-30 18:33:17 +0800464 // The number of pixels between the center of the view
465 // and the edge when the edge is aligned.
Chih-Chung Changec412542011-09-26 17:34:06 +0800466 int left = (int) Math.ceil(mViewW / (2 * scale));
467 int right = mImageW - left;
468 int top = (int) Math.ceil(mViewH / (2 * scale));
469 int bottom = mImageH - top;
470
471 if (mImageW * scale > mViewW) {
472 if (mCurrentX < left) {
473 needAnimation = true;
474 x = left;
475 } else if (mCurrentX > right) {
476 needAnimation = true;
477 x = right;
478 }
479 } else if (mCurrentX != mImageW / 2) {
480 needAnimation = true;
481 x = mImageW / 2;
482 }
483
484 if (mImageH * scale > mViewH) {
485 if (mCurrentY < top) {
486 needAnimation = true;
487 y = top;
488 } else if (mCurrentY > bottom) {
489 needAnimation = true;
490 y = bottom;
491 }
492 } else if (mCurrentY != mImageH / 2) {
493 needAnimation = true;
494 y = mImageH / 2;
495 }
496
497 if (needAnimation) {
498 startAnimation(x, y, scale, ANIM_KIND_SNAPBACK);
499 }
500
501 return needAnimation;
502 }
503
504 private float getTargetScale() {
505 if (mAnimationStartTime == NO_ANIMATION
506 || mAnimationKind == ANIM_KIND_SNAPBACK) return mCurrentScale;
507 return mToScale;
508 }
509
510 private int getTargetX() {
511 if (mAnimationStartTime == NO_ANIMATION
512 || mAnimationKind == ANIM_KIND_SNAPBACK) return mCurrentX;
513 return mToX;
514 }
515
516 private int getTargetY() {
517 if (mAnimationStartTime == NO_ANIMATION
518 || mAnimationKind == ANIM_KIND_SNAPBACK) return mCurrentY;
519 return mToY;
520 }
521
522 public RectF getImageBounds() {
523 float points[] = mTempPoints;
524
525 /*
526 * (p0,p1)----------(p2,p3)
527 * | |
528 * | |
529 * (p4,p5)----------(p6,p7)
530 */
531 points[0] = points[4] = -mCurrentX;
532 points[1] = points[3] = -mCurrentY;
533 points[2] = points[6] = mImageW - mCurrentX;
534 points[5] = points[7] = mImageH - mCurrentY;
535
536 RectF rect = mTempRect;
537 rect.set(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY,
538 Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
539
540 float scale = mCurrentScale;
541 float offsetX = mViewW / 2;
542 float offsetY = mViewH / 2;
543 for (int i = 0; i < 4; ++i) {
544 float x = points[i + i] * scale + offsetX;
545 float y = points[i + i + 1] * scale + offsetY;
546 if (x < rect.left) rect.left = x;
547 if (x > rect.right) rect.right = x;
548 if (y < rect.top) rect.top = y;
549 if (y > rect.bottom) rect.bottom = y;
550 }
551 return rect;
552 }
553
554 public int getImageWidth() {
555 return mImageW;
556 }
557
558 public int getImageHeight() {
559 return mImageH;
560 }
561}