1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.gallery3d.filtershow.imageshow; 18 19 import android.content.Context; 20 import android.graphics.Bitmap; 21 import android.graphics.Canvas; 22 import android.graphics.Color; 23 import android.graphics.Matrix; 24 import android.graphics.Paint; 25 import android.graphics.Point; 26 import android.graphics.Rect; 27 import android.graphics.RectF; 28 import android.net.Uri; 29 import android.os.Handler; 30 import android.util.AttributeSet; 31 import android.view.GestureDetector; 32 import android.view.GestureDetector.OnDoubleTapListener; 33 import android.view.GestureDetector.OnGestureListener; 34 import android.view.MotionEvent; 35 import android.view.ScaleGestureDetector; 36 import android.view.View; 37 import android.widget.LinearLayout; 38 39 import com.android.gallery3d.filtershow.FilterShowActivity; 40 import com.android.gallery3d.filtershow.cache.ImageLoader; 41 import com.android.gallery3d.filtershow.filters.ImageFilter; 42 import com.android.gallery3d.filtershow.presets.ImagePreset; 43 44 import java.io.File; 45 46 public class ImageShow extends View implements OnGestureListener, 47 ScaleGestureDetector.OnScaleGestureListener, 48 OnDoubleTapListener { 49 50 private static final String LOGTAG = "ImageShow"; 51 private static final boolean ENABLE_ZOOMED_COMPARISON = false; 52 53 protected Paint mPaint = new Paint(); 54 protected static int mTextSize = 24; 55 protected static int mTextPadding = 20; 56 57 protected ImageLoader mImageLoader = null; 58 private boolean mDirtyGeometry = false; 59 60 private Bitmap mBackgroundImage = null; 61 private final boolean USE_BACKGROUND_IMAGE = false; 62 private static int mBackgroundColor = Color.RED; 63 64 private GestureDetector mGestureDetector = null; 65 private ScaleGestureDetector mScaleGestureDetector = null; 66 67 protected Rect mImageBounds = new Rect(); 68 private boolean mOriginalDisabled = false; 69 private boolean mTouchShowOriginal = false; 70 private long mTouchShowOriginalDate = 0; 71 private final long mTouchShowOriginalDelayMin = 200; // 200ms 72 private final long mTouchShowOriginalDelayMax = 300; // 300ms 73 private int mShowOriginalDirection = 0; 74 private static int UNVEIL_HORIZONTAL = 1; 75 private static int UNVEIL_VERTICAL = 2; 76 77 private Point mTouchDown = new Point(); 78 private Point mTouch = new Point(); 79 private boolean mFinishedScalingOperation = false; 80 81 private static int mOriginalTextMargin = 8; 82 private static int mOriginalTextSize = 26; 83 private static String mOriginalText = "Original"; 84 private boolean mZoomIn = false; 85 Point mOriginalTranslation = new Point(); 86 float mOriginalScale; 87 float mStartFocusX, mStartFocusY; 88 private enum InteractionMode { 89 NONE, 90 SCALE, 91 MOVE 92 } 93 private String mToast = null; 94 private boolean mShowToast = false; 95 private boolean mImportantToast = false; 96 InteractionMode mInteractionMode = InteractionMode.NONE; 97 98 protected GeometryMetadata getGeometry() { 99 return new GeometryMetadata(getImagePreset().mGeoData); 100 } 101 102 private FilterShowActivity mActivity = null; 103 104 public static void setDefaultBackgroundColor(int value) { 105 mBackgroundColor = value; 106 } 107 108 public FilterShowActivity getActivity() { 109 return mActivity; 110 } 111 112 public int getDefaultBackgroundColor() { 113 return mBackgroundColor; 114 } 115 116 public static void setTextSize(int value) { 117 mTextSize = value; 118 } 119 120 public static void setTextPadding(int value) { 121 mTextPadding = value; 122 } 123 124 public static void setOriginalTextMargin(int value) { 125 mOriginalTextMargin = value; 126 } 127 128 public static void setOriginalTextSize(int value) { 129 mOriginalTextSize = value; 130 } 131 132 public static void setOriginalText(String text) { 133 mOriginalText = text; 134 } 135 136 private final Handler mHandler = new Handler(); 137 138 public void select() { 139 } 140 141 public void unselect() { 142 } 143 144 public boolean hasModifications() { 145 if (getImagePreset() == null) { 146 return false; 147 } 148 return getImagePreset().hasModifications(); 149 } 150 151 public void resetParameter() { 152 // TODO: implement reset 153 } 154 155 public void onNewValue(int parameter) { 156 invalidate(); 157 mActivity.enableSave(hasModifications()); 158 } 159 160 public Point getTouchPoint() { 161 return mTouch; 162 } 163 164 public ImageShow(Context context, AttributeSet attrs) { 165 super(context, attrs); 166 167 setupGestureDetector(context); 168 mActivity = (FilterShowActivity) context; 169 MasterImage.getImage().addObserver(this); 170 } 171 172 public ImageShow(Context context) { 173 super(context); 174 175 setupGestureDetector(context); 176 mActivity = (FilterShowActivity) context; 177 MasterImage.getImage().addObserver(this); 178 } 179 180 public void setupGestureDetector(Context context) { 181 mGestureDetector = new GestureDetector(context, this); 182 mScaleGestureDetector = new ScaleGestureDetector(context, this); 183 } 184 185 @Override 186 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 187 int parentWidth = MeasureSpec.getSize(widthMeasureSpec); 188 int parentHeight = MeasureSpec.getSize(heightMeasureSpec); 189 setMeasuredDimension(parentWidth, parentHeight); 190 } 191 192 public ImageFilter getCurrentFilter() { 193 return MasterImage.getImage().getCurrentFilter(); 194 } 195 196 public void showToast(String text) { 197 showToast(text, false); 198 } 199 200 public void showToast(String text, boolean important) { 201 mToast = text; 202 mShowToast = true; 203 mImportantToast = important; 204 invalidate(); 205 206 mHandler.postDelayed(new Runnable() { 207 @Override 208 public void run() { 209 mShowToast = false; 210 invalidate(); 211 } 212 }, 400); 213 } 214 215 public Rect getImageBounds() { 216 Rect dst = new Rect(); 217 getImagePreset().mGeoData.getPhotoBounds().roundOut(dst); 218 return dst; 219 } 220 221 public Rect getImageCropBounds() { 222 return GeometryMath.roundNearest(getImagePreset().mGeoData.getPreviewCropBounds()); 223 } 224 225 /* consider moving the following 2 methods into a subclass */ 226 /** 227 * This function calculates a Image to Screen Transformation matrix 228 * 229 * @param reflectRotation set true if you want the rotation encoded 230 * @return Image to Screen transformation matrix 231 */ 232 protected Matrix getImageToScreenMatrix(boolean reflectRotation) { 233 GeometryMetadata geo = getImagePreset().mGeoData; 234 if (geo == null || mImageLoader == null 235 || mImageLoader.getOriginalBounds() == null) { 236 return new Matrix(); 237 } 238 Matrix m = geo.getOriginalToScreen(reflectRotation, 239 mImageLoader.getOriginalBounds().width(), 240 mImageLoader.getOriginalBounds().height(), getWidth(), getHeight()); 241 Point translate = MasterImage.getImage().getTranslation(); 242 float scaleFactor = MasterImage.getImage().getScaleFactor(); 243 m.postTranslate(translate.x, translate.y); 244 m.postScale(scaleFactor, scaleFactor, getWidth() / 2.0f, getHeight() / 2.0f); 245 return m; 246 } 247 248 /** 249 * This function calculates a to Screen Image Transformation matrix 250 * 251 * @param reflectRotation set true if you want the rotation encoded 252 * @return Screen to Image transformation matrix 253 */ 254 protected Matrix getScreenToImageMatrix(boolean reflectRotation) { 255 Matrix m = getImageToScreenMatrix(reflectRotation); 256 Matrix invert = new Matrix(); 257 m.invert(invert); 258 return invert; 259 } 260 261 public Rect getDisplayedImageBounds() { 262 return mImageBounds; 263 } 264 265 public ImagePreset getImagePreset() { 266 return MasterImage.getImage().getPreset(); 267 } 268 269 public void drawToast(Canvas canvas) { 270 if (mShowToast && mToast != null) { 271 Paint paint = new Paint(); 272 paint.setTextSize(128); 273 float textWidth = paint.measureText(mToast); 274 int toastX = (int) ((getWidth() - textWidth) / 2.0f); 275 int toastY = (int) (getHeight() / 3.0f); 276 277 paint.setARGB(255, 0, 0, 0); 278 canvas.drawText(mToast, toastX - 2, toastY - 2, paint); 279 canvas.drawText(mToast, toastX - 2, toastY, paint); 280 canvas.drawText(mToast, toastX, toastY - 2, paint); 281 canvas.drawText(mToast, toastX + 2, toastY + 2, paint); 282 canvas.drawText(mToast, toastX + 2, toastY, paint); 283 canvas.drawText(mToast, toastX, toastY + 2, paint); 284 if (mImportantToast) { 285 paint.setARGB(255, 200, 0, 0); 286 } else { 287 paint.setARGB(255, 255, 255, 255); 288 } 289 canvas.drawText(mToast, toastX, toastY, paint); 290 } 291 } 292 293 @Override 294 public void onDraw(Canvas canvas) { 295 MasterImage.getImage().setImageShowSize(getWidth(), getHeight()); 296 297 float cx = canvas.getWidth()/2.0f; 298 float cy = canvas.getHeight()/2.0f; 299 float scaleFactor = MasterImage.getImage().getScaleFactor(); 300 Point translation = MasterImage.getImage().getTranslation(); 301 302 Matrix scalingMatrix = new Matrix(); 303 scalingMatrix.postScale(scaleFactor, scaleFactor, cx, cy); 304 scalingMatrix.preTranslate(translation.x, translation.y); 305 306 RectF unscaledClipRect = new RectF(mImageBounds); 307 scalingMatrix.mapRect(unscaledClipRect, unscaledClipRect); 308 309 canvas.save(); 310 311 boolean enablePartialRendering = false; 312 313 // For now, partial rendering is disabled for all filters, 314 // so no need to clip. 315 if (enablePartialRendering && !unscaledClipRect.isEmpty()) { 316 canvas.clipRect(unscaledClipRect); 317 } 318 319 canvas.save(); 320 // TODO: center scale on gesture 321 canvas.scale(scaleFactor, scaleFactor, cx, cy); 322 canvas.translate(translation.x, translation.y); 323 drawBackground(canvas); 324 drawImage(canvas, getFilteredImage(), true); 325 Bitmap highresPreview = MasterImage.getImage().getHighresImage(); 326 if (highresPreview != null) { 327 drawImage(canvas, highresPreview, false); 328 } 329 canvas.restore(); 330 331 if (showTitle() && getImagePreset() != null) { 332 mPaint.setARGB(200, 0, 0, 0); 333 mPaint.setTextSize(mTextSize); 334 335 Rect textRect = new Rect(0, 0, getWidth(), mTextSize + mTextPadding); 336 canvas.drawRect(textRect, mPaint); 337 mPaint.setARGB(255, 200, 200, 200); 338 canvas.drawText(getImagePreset().name(), mTextPadding, 339 1.5f * mTextPadding, mPaint); 340 } 341 342 Bitmap partialPreview = MasterImage.getImage().getPartialImage(); 343 if (partialPreview != null) { 344 Rect src = new Rect(0, 0, partialPreview.getWidth(), partialPreview.getHeight()); 345 Rect dest = new Rect(0, 0, getWidth(), getHeight()); 346 canvas.drawBitmap(partialPreview, src, dest, mPaint); 347 } 348 349 canvas.save(); 350 canvas.scale(scaleFactor, scaleFactor, cx, cy); 351 canvas.translate(translation.x, translation.y); 352 drawPartialImage(canvas, getGeometryOnlyImage()); 353 canvas.restore(); 354 355 canvas.restore(); 356 357 drawToast(canvas); 358 } 359 360 public void resetImageCaches(ImageShow caller) { 361 if (mImageLoader == null) { 362 return; 363 } 364 MasterImage.getImage().updatePresets(true); 365 } 366 367 public Bitmap getFiltersOnlyImage() { 368 return MasterImage.getImage().getFiltersOnlyImage(); 369 } 370 371 public Bitmap getGeometryOnlyImage() { 372 return MasterImage.getImage().getGeometryOnlyImage(); 373 } 374 375 public Bitmap getFilteredImage() { 376 return MasterImage.getImage().getFilteredImage(); 377 } 378 379 public void drawImage(Canvas canvas, Bitmap image, boolean updateBounds) { 380 if (image != null) { 381 Rect s = new Rect(0, 0, image.getWidth(), 382 image.getHeight()); 383 384 float scale = GeometryMath.scale(image.getWidth(), image.getHeight(), getWidth(), 385 getHeight()); 386 387 float w = image.getWidth() * scale; 388 float h = image.getHeight() * scale; 389 float ty = (getHeight() - h) / 2.0f; 390 float tx = (getWidth() - w) / 2.0f; 391 392 Rect d = new Rect((int) tx, (int) ty, (int) (w + tx), 393 (int) (h + ty)); 394 if (updateBounds) { 395 mImageBounds = d; 396 } 397 canvas.drawBitmap(image, s, d, mPaint); 398 } 399 } 400 401 public void drawPartialImage(Canvas canvas, Bitmap image) { 402 boolean showsOriginal = MasterImage.getImage().showsOriginal(); 403 if (!showsOriginal && !mTouchShowOriginal) 404 return; 405 canvas.save(); 406 if (image != null) { 407 if (mShowOriginalDirection == 0) { 408 if (Math.abs(mTouch.y - mTouchDown.y) > Math.abs(mTouch.x - mTouchDown.x)) { 409 mShowOriginalDirection = UNVEIL_VERTICAL; 410 } else { 411 mShowOriginalDirection = UNVEIL_HORIZONTAL; 412 } 413 } 414 415 int px = 0; 416 int py = 0; 417 if (mShowOriginalDirection == UNVEIL_VERTICAL) { 418 px = mImageBounds.width(); 419 py = (int) (mTouch.y - mImageBounds.top); 420 } else { 421 px = (int) (mTouch.x - mImageBounds.left); 422 py = mImageBounds.height(); 423 if (showsOriginal) { 424 px = mImageBounds.width(); 425 } 426 } 427 428 Rect d = new Rect(mImageBounds.left, mImageBounds.top, 429 mImageBounds.left + px, mImageBounds.top + py); 430 canvas.clipRect(d); 431 drawImage(canvas, image, false); 432 Paint paint = new Paint(); 433 paint.setColor(Color.BLACK); 434 paint.setStrokeWidth(3); 435 436 if (mShowOriginalDirection == UNVEIL_VERTICAL) { 437 canvas.drawLine(mImageBounds.left, mTouch.y, 438 mImageBounds.right, mTouch.y, paint); 439 } else { 440 canvas.drawLine(mTouch.x, mImageBounds.top, 441 mTouch.x, mImageBounds.bottom, paint); 442 } 443 444 Rect bounds = new Rect(); 445 paint.setAntiAlias(true); 446 paint.setTextSize(mOriginalTextSize); 447 paint.getTextBounds(mOriginalText, 0, mOriginalText.length(), bounds); 448 paint.setColor(Color.BLACK); 449 paint.setStyle(Paint.Style.STROKE); 450 paint.setStrokeWidth(3); 451 canvas.drawText(mOriginalText, mImageBounds.left + mOriginalTextMargin, 452 mImageBounds.top + bounds.height() + mOriginalTextMargin, paint); 453 paint.setStyle(Paint.Style.FILL); 454 paint.setStrokeWidth(1); 455 paint.setColor(Color.WHITE); 456 canvas.drawText(mOriginalText, mImageBounds.left + mOriginalTextMargin, 457 mImageBounds.top + bounds.height() + mOriginalTextMargin, paint); 458 } 459 canvas.restore(); 460 } 461 462 public void drawBackground(Canvas canvas) { 463 if (USE_BACKGROUND_IMAGE) { 464 if (mBackgroundImage == null) { 465 mBackgroundImage = mImageLoader.getBackgroundBitmap(getResources()); 466 } 467 if (mBackgroundImage != null) { 468 Rect s = new Rect(0, 0, mBackgroundImage.getWidth(), 469 mBackgroundImage.getHeight()); 470 Rect d = new Rect(0, 0, getWidth(), getHeight()); 471 canvas.drawBitmap(mBackgroundImage, s, d, mPaint); 472 } 473 } else { 474 canvas.drawARGB(0, 0, 0, 0); 475 } 476 } 477 478 public boolean showTitle() { 479 return false; 480 } 481 482 public void setImageLoader(ImageLoader loader) { 483 mImageLoader = loader; 484 if (mImageLoader != null) { 485 mImageLoader.addListener(this); 486 MasterImage.getImage().setImageLoader(mImageLoader); 487 } 488 } 489 490 private void setDirtyGeometryFlag() { 491 mDirtyGeometry = true; 492 } 493 494 protected void clearDirtyGeometryFlag() { 495 mDirtyGeometry = false; 496 } 497 498 protected boolean getDirtyGeometryFlag() { 499 return mDirtyGeometry; 500 } 501 502 private void imageSizeChanged(Bitmap image) { 503 if (image == null || getImagePreset() == null) 504 return; 505 float w = image.getWidth(); 506 float h = image.getHeight(); 507 GeometryMetadata geo = getImagePreset().mGeoData; 508 RectF pb = geo.getPhotoBounds(); 509 if (w == pb.width() && h == pb.height()) { 510 return; 511 } 512 RectF r = new RectF(0, 0, w, h); 513 getImagePreset().mGeoData.setPhotoBounds(r); 514 getImagePreset().mGeoData.setCropBounds(r); 515 516 } 517 518 public boolean updateGeometryFlags() { 519 return true; 520 } 521 522 public void updateImage() { 523 invalidate(); 524 if (!updateGeometryFlags()) { 525 return; 526 } 527 Bitmap bitmap = mImageLoader.getOriginalBitmapLarge(); 528 if (bitmap != null) { 529 imageSizeChanged(bitmap); 530 } 531 } 532 533 public void imageLoaded() { 534 updateImage(); 535 invalidate(); 536 } 537 538 public void saveImage(FilterShowActivity filterShowActivity, File file) { 539 mImageLoader.saveImage(getImagePreset(), filterShowActivity, file); 540 } 541 542 public void saveToUri(Bitmap f, Uri u, String m, FilterShowActivity filterShowActivity) { 543 mImageLoader.saveToUri(f, u, m, filterShowActivity); 544 } 545 546 public void returnFilteredResult(FilterShowActivity filterShowActivity) { 547 mImageLoader.returnFilteredResult(getImagePreset(), filterShowActivity); 548 } 549 550 public boolean scaleInProgress() { 551 return mScaleGestureDetector.isInProgress(); 552 } 553 554 protected boolean isOriginalDisabled() { 555 return mOriginalDisabled; 556 } 557 558 protected void setOriginalDisabled(boolean originalDisabled) { 559 mOriginalDisabled = originalDisabled; 560 } 561 562 @Override 563 public boolean onTouchEvent(MotionEvent event) { 564 super.onTouchEvent(event); 565 int action = event.getAction(); 566 action = action & MotionEvent.ACTION_MASK; 567 568 mGestureDetector.onTouchEvent(event); 569 boolean scaleInProgress = scaleInProgress(); 570 mScaleGestureDetector.onTouchEvent(event); 571 if (mInteractionMode == InteractionMode.SCALE) { 572 return true; 573 } 574 if (!scaleInProgress() && scaleInProgress) { 575 // If we were scaling, the scale will stop but we will 576 // still issue an ACTION_UP. Let the subclasses know. 577 mFinishedScalingOperation = true; 578 } 579 580 int ex = (int) event.getX(); 581 int ey = (int) event.getY(); 582 if (action == MotionEvent.ACTION_DOWN) { 583 mInteractionMode = InteractionMode.MOVE; 584 mTouchDown.x = ex; 585 mTouchDown.y = ey; 586 mTouchShowOriginalDate = System.currentTimeMillis(); 587 mShowOriginalDirection = 0; 588 MasterImage.getImage().setOriginalTranslation(MasterImage.getImage().getTranslation()); 589 } 590 591 if (action == MotionEvent.ACTION_MOVE && mInteractionMode == InteractionMode.MOVE) { 592 mTouch.x = ex; 593 mTouch.y = ey; 594 595 float scaleFactor = MasterImage.getImage().getScaleFactor(); 596 if (scaleFactor > 1 && (!ENABLE_ZOOMED_COMPARISON || event.getPointerCount() == 2)) { 597 float translateX = (mTouch.x - mTouchDown.x) / scaleFactor; 598 float translateY = (mTouch.y - mTouchDown.y) / scaleFactor; 599 Point originalTranslation = MasterImage.getImage().getOriginalTranslation(); 600 Point translation = MasterImage.getImage().getTranslation(); 601 translation.x = (int) (originalTranslation.x + translateX); 602 translation.y = (int) (originalTranslation.y + translateY); 603 MasterImage.getImage().setTranslation(translation); 604 mTouchShowOriginal = false; 605 } else if (enableComparison() && !mOriginalDisabled 606 && (System.currentTimeMillis() - mTouchShowOriginalDate 607 > mTouchShowOriginalDelayMin) 608 && event.getPointerCount() == 1) { 609 mTouchShowOriginal = true; 610 } 611 } 612 613 if (action == MotionEvent.ACTION_UP) { 614 mInteractionMode = InteractionMode.NONE; 615 mTouchShowOriginal = false; 616 mTouchDown.x = 0; 617 mTouchDown.y = 0; 618 mTouch.x = 0; 619 mTouch.y = 0; 620 if (MasterImage.getImage().getScaleFactor() <= 1) { 621 MasterImage.getImage().setScaleFactor(1); 622 MasterImage.getImage().resetTranslation(); 623 } 624 } 625 invalidate(); 626 return true; 627 } 628 629 protected boolean enableComparison() { 630 return true; 631 } 632 633 // listview stuff 634 public void showOriginal(boolean show) { 635 invalidate(); 636 } 637 638 @Override 639 public boolean onDoubleTap(MotionEvent arg0) { 640 mZoomIn = !mZoomIn; 641 float scale = 1.0f; 642 if (mZoomIn) { 643 scale = MasterImage.getImage().getMaxScaleFactor(); 644 } 645 if (scale != MasterImage.getImage().getScaleFactor()) { 646 MasterImage.getImage().setScaleFactor(scale); 647 invalidate(); 648 } 649 return true; 650 } 651 652 @Override 653 public boolean onDoubleTapEvent(MotionEvent arg0) { 654 // TODO Auto-generated method stub 655 return false; 656 } 657 658 @Override 659 public boolean onSingleTapConfirmed(MotionEvent arg0) { 660 // TODO Auto-generated method stub 661 return false; 662 } 663 664 @Override 665 public boolean onDown(MotionEvent arg0) { 666 // TODO Auto-generated method stub 667 return false; 668 } 669 670 @Override 671 public boolean onFling(MotionEvent startEvent, MotionEvent endEvent, float arg2, float arg3) { 672 if (mActivity == null) { 673 return false; 674 } 675 if (endEvent.getPointerCount() == 2) { 676 return false; 677 } 678 return true; 679 } 680 681 @Override 682 public void onLongPress(MotionEvent arg0) { 683 // TODO Auto-generated method stub 684 } 685 686 @Override 687 public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) { 688 // TODO Auto-generated method stub 689 return false; 690 } 691 692 @Override 693 public void onShowPress(MotionEvent arg0) { 694 // TODO Auto-generated method stub 695 } 696 697 @Override 698 public boolean onSingleTapUp(MotionEvent arg0) { 699 // TODO Auto-generated method stub 700 return false; 701 } 702 703 public boolean useUtilityPanel() { 704 return false; 705 } 706 707 public void openUtilityPanel(final LinearLayout accessoryViewList) { 708 // TODO Auto-generated method stub 709 } 710 711 @Override 712 public boolean onScale(ScaleGestureDetector detector) { 713 MasterImage img = MasterImage.getImage(); 714 float scaleFactor = img.getScaleFactor(); 715 Point pos = img.getTranslation(); 716 717 scaleFactor = scaleFactor * detector.getScaleFactor(); 718 if (scaleFactor > MasterImage.getImage().getMaxScaleFactor()) { 719 scaleFactor = MasterImage.getImage().getMaxScaleFactor(); 720 } 721 if (scaleFactor < 0.5) { 722 scaleFactor = 0.5f; 723 } 724 MasterImage.getImage().setScaleFactor(scaleFactor); 725 scaleFactor = img.getScaleFactor(); 726 pos = img.getTranslation(); 727 float focusx = detector.getFocusX(); 728 float focusy = detector.getFocusY(); 729 float translateX = (focusx - mStartFocusX) / scaleFactor; 730 float translateY = (focusy - mStartFocusY) / scaleFactor; 731 Point translation = MasterImage.getImage().getTranslation(); 732 translation.x = (int) (mOriginalTranslation.x + translateX); 733 translation.y = (int) (mOriginalTranslation.y + translateY); 734 MasterImage.getImage().setTranslation(translation); 735 736 invalidate(); 737 return true; 738 } 739 740 @Override 741 public boolean onScaleBegin(ScaleGestureDetector detector) { 742 Point pos = MasterImage.getImage().getTranslation(); 743 mOriginalTranslation.x = pos.x; 744 mOriginalTranslation.y = pos.y; 745 mOriginalScale = MasterImage.getImage().getScaleFactor(); 746 mStartFocusX = detector.getFocusX(); 747 mStartFocusY = detector.getFocusY(); 748 mInteractionMode = InteractionMode.SCALE; 749 return true; 750 } 751 752 @Override 753 public void onScaleEnd(ScaleGestureDetector detector) { 754 mInteractionMode = InteractionMode.NONE; 755 if (MasterImage.getImage().getScaleFactor() < 1) { 756 MasterImage.getImage().setScaleFactor(1); 757 invalidate(); 758 } 759 } 760 761 public boolean didFinishScalingOperation() { 762 if (mFinishedScalingOperation) { 763 mFinishedScalingOperation = false; 764 return true; 765 } 766 return false; 767 } 768 769 } 770