OSDN Git Service

Fix rotation anims on slow device (svelte)
[android-x86/packages-apps-Gallery2.git] / src / com / android / gallery3d / filtershow / imageshow / ImageShow.java
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.content.res.Resources;
21 import android.graphics.Bitmap;
22 import android.graphics.BitmapFactory;
23 import android.graphics.BitmapShader;
24 import android.graphics.Canvas;
25 import android.graphics.Color;
26 import android.graphics.Matrix;
27 import android.graphics.Paint;
28 import android.graphics.Point;
29 import android.graphics.Rect;
30 import android.graphics.Shader;
31 import android.graphics.drawable.NinePatchDrawable;
32 import android.util.AttributeSet;
33 import android.view.GestureDetector;
34 import android.view.GestureDetector.OnDoubleTapListener;
35 import android.view.GestureDetector.OnGestureListener;
36 import android.view.MotionEvent;
37 import android.view.ScaleGestureDetector;
38 import android.view.View;
39 import android.widget.LinearLayout;
40
41 import com.android.gallery3d.R;
42 import com.android.gallery3d.filtershow.FilterShowActivity;
43 import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation;
44 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
45 import com.android.gallery3d.filtershow.filters.ImageFilter;
46 import com.android.gallery3d.filtershow.pipeline.ImagePreset;
47 import com.android.gallery3d.filtershow.tools.SaveImage;
48
49 import java.io.File;
50 import java.util.ArrayList;
51 import java.util.Collection;
52
53 public class ImageShow extends View implements OnGestureListener,
54         ScaleGestureDetector.OnScaleGestureListener,
55         OnDoubleTapListener {
56
57     private static final String LOGTAG = "ImageShow";
58     private static final boolean ENABLE_ZOOMED_COMPARISON = false;
59
60     protected Paint mPaint = new Paint();
61     protected int mTextSize;
62     protected int mTextPadding;
63
64     protected int mBackgroundColor;
65
66     private GestureDetector mGestureDetector = null;
67     private ScaleGestureDetector mScaleGestureDetector = null;
68
69     protected Rect mImageBounds = new Rect();
70     private boolean mOriginalDisabled = false;
71     private boolean mTouchShowOriginal = false;
72     private long mTouchShowOriginalDate = 0;
73     private final long mTouchShowOriginalDelayMin = 200; // 200ms
74     private int mShowOriginalDirection = 0;
75     private static int UNVEIL_HORIZONTAL = 1;
76     private static int UNVEIL_VERTICAL = 2;
77
78     private NinePatchDrawable mShadow = null;
79     private Rect mShadowBounds = new Rect();
80     private int mShadowMargin = 15; // not scaled, fixed in the asset
81     private boolean mShadowDrawn = false;
82
83     private Point mTouchDown = new Point();
84     private Point mTouch = new Point();
85     private boolean mFinishedScalingOperation = false;
86
87     private int mOriginalTextMargin;
88     private int mOriginalTextSize;
89     private String mOriginalText;
90     private boolean mZoomIn = false;
91     Point mOriginalTranslation = new Point();
92     float mOriginalScale;
93     float mStartFocusX, mStartFocusY;
94     private enum InteractionMode {
95         NONE,
96         SCALE,
97         MOVE
98     }
99     InteractionMode mInteractionMode = InteractionMode.NONE;
100
101     private static Bitmap sMask;
102     private Paint mMaskPaint = new Paint();
103     private Matrix mShaderMatrix = new Matrix();
104     private boolean mDidStartAnimation = false;
105
106     private static Bitmap convertToAlphaMask(Bitmap b) {
107         Bitmap a = Bitmap.createBitmap(b.getWidth(), b.getHeight(), Bitmap.Config.ALPHA_8);
108         Canvas c = new Canvas(a);
109         c.drawBitmap(b, 0.0f, 0.0f, null);
110         return a;
111     }
112
113     private static Shader createShader(Bitmap b) {
114         return new BitmapShader(b, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
115     }
116
117     private FilterShowActivity mActivity = null;
118
119     public FilterShowActivity getActivity() {
120         return mActivity;
121     }
122
123     public boolean hasModifications() {
124         return MasterImage.getImage().hasModifications();
125     }
126
127     public void resetParameter() {
128         // TODO: implement reset
129     }
130
131     public void onNewValue(int parameter) {
132         invalidate();
133     }
134
135     public ImageShow(Context context, AttributeSet attrs, int defStyle) {
136         super(context, attrs, defStyle);
137         setupImageShow(context);
138     }
139
140     public ImageShow(Context context, AttributeSet attrs) {
141         super(context, attrs);
142         setupImageShow(context);
143
144     }
145
146     public ImageShow(Context context) {
147         super(context);
148         setupImageShow(context);
149     }
150
151     private void setupImageShow(Context context) {
152         Resources res = context.getResources();
153         mTextSize = res.getDimensionPixelSize(R.dimen.photoeditor_text_size);
154         mTextPadding = res.getDimensionPixelSize(R.dimen.photoeditor_text_padding);
155         mOriginalTextMargin = res.getDimensionPixelSize(R.dimen.photoeditor_original_text_margin);
156         mOriginalTextSize = res.getDimensionPixelSize(R.dimen.photoeditor_original_text_size);
157         mBackgroundColor = res.getColor(R.color.background_screen);
158         mOriginalText = res.getString(R.string.original_picture_text);
159         mShadow = (NinePatchDrawable) res.getDrawable(R.drawable.geometry_shadow);
160         setupGestureDetector(context);
161         mActivity = (FilterShowActivity) context;
162         if (sMask == null) {
163             Bitmap mask = BitmapFactory.decodeResource(res, R.drawable.spot_mask);
164             sMask = convertToAlphaMask(mask);
165         }
166     }
167
168     public void attach() {
169         MasterImage.getImage().addObserver(this);
170         bindAsImageLoadListener();
171     }
172
173     public void detach() {
174         MasterImage.getImage().removeObserver(this);
175         mMaskPaint.reset();
176     }
177
178     public void setupGestureDetector(Context context) {
179         mGestureDetector = new GestureDetector(context, this);
180         mScaleGestureDetector = new ScaleGestureDetector(context, this);
181     }
182
183     @Override
184     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
185         int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
186         int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
187         setMeasuredDimension(parentWidth, parentHeight);
188     }
189
190     public ImageFilter getCurrentFilter() {
191         return MasterImage.getImage().getCurrentFilter();
192     }
193
194     /* consider moving the following 2 methods into a subclass */
195     /**
196      * This function calculates a Image to Screen Transformation matrix
197      *
198      * @param reflectRotation set true if you want the rotation encoded
199      * @return Image to Screen transformation matrix
200      */
201     protected Matrix getImageToScreenMatrix(boolean reflectRotation) {
202         MasterImage master = MasterImage.getImage();
203         if (master.getOriginalBounds() == null) {
204             return new Matrix();
205         }
206         Matrix m = GeometryMathUtils.getImageToScreenMatrix(master.getPreset().getGeometryFilters(),
207                 reflectRotation, master.getOriginalBounds(), getWidth(), getHeight());
208         Point translate = master.getTranslation();
209         float scaleFactor = master.getScaleFactor();
210         m.postTranslate(translate.x, translate.y);
211         m.postScale(scaleFactor, scaleFactor, getWidth() / 2.0f, getHeight() / 2.0f);
212         return m;
213     }
214
215     /**
216      * This function calculates a to Screen Image Transformation matrix
217      *
218      * @param reflectRotation set true if you want the rotation encoded
219      * @return Screen to Image transformation matrix
220      */
221     protected Matrix getScreenToImageMatrix(boolean reflectRotation) {
222         Matrix m = getImageToScreenMatrix(reflectRotation);
223         Matrix invert = new Matrix();
224         m.invert(invert);
225         return invert;
226     }
227
228     public ImagePreset getImagePreset() {
229         return MasterImage.getImage().getPreset();
230     }
231
232     @Override
233     public void onDraw(Canvas canvas) {
234         MasterImage.getImage().setImageShowSize(
235                 getWidth() - 2*mShadowMargin,
236                 getHeight() - 2*mShadowMargin);
237
238         float cx = canvas.getWidth()/2.0f;
239         float cy = canvas.getHeight()/2.0f;
240         float scaleFactor = MasterImage.getImage().getScaleFactor();
241         Point translation = MasterImage.getImage().getTranslation();
242
243         canvas.save();
244
245         mShadowDrawn = false;
246
247         canvas.save();
248         // TODO: center scale on gesture
249         canvas.scale(scaleFactor, scaleFactor, cx, cy);
250         canvas.translate(translation.x, translation.y);
251         Bitmap highresPreview = MasterImage.getImage().getHighresImage();
252
253         boolean isDoingNewLookAnimation = MasterImage.getImage().onGoingNewLookAnimation();
254
255         if (!isDoingNewLookAnimation && highresPreview != null) {
256             drawImage(canvas, highresPreview, true);
257         } else {
258             drawImage(canvas, getFilteredImage(), true);
259         }
260         canvas.restore();
261
262         Bitmap partialPreview = MasterImage.getImage().getPartialImage();
263         if (!isDoingNewLookAnimation && partialPreview != null) {
264             canvas.save();
265             Rect originalBounds = MasterImage.getImage().getOriginalBounds();
266             Collection<FilterRepresentation> geo = MasterImage.getImage().getPreset()
267                     .getGeometryFilters();
268
269             Matrix compensation = GeometryMathUtils.getPartialToScreenMatrix(geo,
270                     originalBounds, getWidth(), getHeight(),
271                     partialPreview.getWidth(), partialPreview.getHeight());
272             canvas.drawBitmap(partialPreview, compensation, null);
273             canvas.restore();
274         }
275
276         canvas.save();
277         canvas.scale(scaleFactor, scaleFactor, cx, cy);
278         canvas.translate(translation.x, translation.y);
279         drawPartialImage(canvas, getGeometryOnlyImage());
280         canvas.restore();
281
282         canvas.restore();
283     }
284
285     public void resetImageCaches(ImageShow caller) {
286         MasterImage.getImage().invalidatePreview();
287     }
288
289     public Bitmap getFiltersOnlyImage() {
290         return MasterImage.getImage().getFiltersOnlyImage();
291     }
292
293     public Bitmap getGeometryOnlyImage() {
294         return MasterImage.getImage().getGeometryOnlyImage();
295     }
296
297     public Bitmap getFilteredImage() {
298         return MasterImage.getImage().getFilteredImage();
299     }
300
301     public void drawImage(Canvas canvas, Bitmap image, boolean updateBounds) {
302         if (image == null) {
303             return;
304         }
305
306         Rect d = computeImageBounds(image.getWidth(), image.getHeight());
307
308         if (updateBounds) {
309             mImageBounds = d;
310         }
311
312         float centerX = mShadowMargin + (getWidth() - 2 * mShadowMargin) / 2;
313         float centerY = mShadowMargin + (getHeight() - 2 * mShadowMargin) / 2;
314
315         MasterImage master = MasterImage.getImage();
316         canvas.save();
317         if (master.onGoingNewLookAnimation()
318                 || mDidStartAnimation) {
319             mDidStartAnimation = true;
320             if (master.getCurrentLookAnimation()
321                     == MasterImage.CIRCLE_ANIMATION
322                     && MasterImage.getImage().getPreviousImage() != null) {
323                 float maskScale = MasterImage.getImage().getMaskScale();
324                 if (maskScale > 0.0f) {
325                     float maskW = sMask.getWidth() / 2.0f;
326                     float maskH = sMask.getHeight() / 2.0f;
327                     float x = centerX - maskW * maskScale;
328                     float y = centerY - maskH * maskScale;
329
330                     // Prepare the shader
331                     mShaderMatrix.reset();
332                     mShaderMatrix.setScale(1.0f / maskScale, 1.0f / maskScale);
333                     mShaderMatrix.preTranslate(-x + d.left, -y + d.top);
334                     float scaleImageX = d.width() / (float) image.getWidth();
335                     float scaleImageY = d.height() / (float) image.getHeight();
336                     mShaderMatrix.preScale(scaleImageX, scaleImageY);
337                     mMaskPaint.reset();
338                     mMaskPaint.setShader(createShader(image));
339                     mMaskPaint.getShader().setLocalMatrix(mShaderMatrix);
340
341                     drawImage(canvas, MasterImage.getImage().getPreviousImage());
342                     canvas.translate(x, y);
343                     canvas.scale(maskScale, maskScale);
344                     canvas.drawBitmap(sMask, 0, 0, mMaskPaint);
345                 } else {
346                     drawImage(canvas, image);
347                 }
348             } else if (master.getCurrentLookAnimation()
349                     == MasterImage.ROTATE_ANIMATION) {
350                 Rect d1 = computeImageBounds(master.getPreviousImage().getHeight(),
351                         master.getPreviousImage().getWidth());
352                 Rect d2 = computeImageBounds(master.getPreviousImage().getWidth(),
353                         master.getPreviousImage().getHeight());
354                 float finalScale = d1.width() / (float) d2.height();
355                 finalScale = (1.0f * (1.0f - master.getAnimFraction()))
356                         + (finalScale * master.getAnimFraction());
357                 canvas.rotate(master.getAnimRotationValue(), centerX, centerY);
358                 canvas.scale(finalScale, finalScale, centerX, centerY);
359                 drawImage(canvas, master.getPreviousImage());
360             } else if (master.getCurrentLookAnimation()
361                     == MasterImage.MIRROR_ANIMATION) {
362                 if (master.getCurrentFilterRepresentation()
363                         instanceof FilterMirrorRepresentation) {
364                     FilterMirrorRepresentation rep =
365                             (FilterMirrorRepresentation) master.getCurrentFilterRepresentation();
366
367                     ImagePreset preset = master.getPreset();
368                     ArrayList<FilterRepresentation> geometry =
369                             (ArrayList<FilterRepresentation>) preset.getGeometryFilters();
370                     GeometryMathUtils.GeometryHolder holder = null;
371                     holder = GeometryMathUtils.unpackGeometry(geometry);
372
373                     if (holder.rotation.value() == 90 || holder.rotation.value() == 270) {
374                         if (rep.isHorizontal() && !rep.isVertical()) {
375                             canvas.scale(1, master.getAnimRotationValue(), centerX, centerY);
376                         } else if (rep.isVertical() && !rep.isHorizontal()) {
377                             canvas.scale(1, master.getAnimRotationValue(), centerX, centerY);
378                         } else if (rep.isHorizontal() && rep.isVertical()) {
379                             canvas.scale(master.getAnimRotationValue(), 1, centerX, centerY);
380                         } else {
381                             canvas.scale(master.getAnimRotationValue(), 1, centerX, centerY);
382                         }
383                     } else {
384                         if (rep.isHorizontal() && !rep.isVertical()) {
385                             canvas.scale(master.getAnimRotationValue(), 1, centerX, centerY);
386                         } else if (rep.isVertical() && !rep.isHorizontal()) {
387                             canvas.scale(master.getAnimRotationValue(), 1, centerX, centerY);
388                         } else  if (rep.isHorizontal() && rep.isVertical()) {
389                             canvas.scale(1, master.getAnimRotationValue(), centerX, centerY);
390                         } else {
391                             canvas.scale(1, master.getAnimRotationValue(), centerX, centerY);
392                         }
393                     }
394                 }
395                 drawImage(canvas, master.getPreviousImage());
396             }
397         } else {
398             drawImage(canvas, image);
399         }
400
401         if (!master.onGoingNewLookAnimation()
402                 && mDidStartAnimation
403                 && !master.getPreviousPreset().equals(master.getCurrentPreset())) {
404             mDidStartAnimation = false;
405             MasterImage.getImage().resetAnimBitmap();
406         }
407         canvas.restore();
408     }
409
410     private void drawImage(Canvas canvas, Bitmap image) {
411         Rect d = computeImageBounds(image.getWidth(), image.getHeight());
412         float scaleImageX = d.width() / (float) image.getWidth();
413         float scaleImageY = d.height() / (float) image.getHeight();
414         Matrix imageMatrix = new Matrix();
415         imageMatrix.postScale(scaleImageX, scaleImageY);
416         imageMatrix.postTranslate(d.left, d.top);
417         drawShadow(canvas, d);
418         canvas.clipRect(d);
419         canvas.drawBitmap(image, imageMatrix, mPaint);
420     }
421
422     private Rect computeImageBounds(int imageWidth, int imageHeight) {
423         float scale = GeometryMathUtils.scale(imageWidth, imageHeight,
424                 getWidth(), getHeight());
425
426         float w = imageWidth * scale;
427         float h = imageHeight * scale;
428         float ty = (getHeight() - h) / 2.0f;
429         float tx = (getWidth() - w) / 2.0f;
430         return new Rect((int) tx + mShadowMargin,
431                 (int) ty + mShadowMargin,
432                 (int) (w + tx) - mShadowMargin,
433                 (int) (h + ty) - mShadowMargin);
434     }
435
436     private void drawShadow(Canvas canvas, Rect d) {
437         if (!mShadowDrawn) {
438             mShadowBounds.set(d.left - mShadowMargin, d.top - mShadowMargin,
439                     d.right + mShadowMargin, d.bottom + mShadowMargin);
440             mShadow.setBounds(mShadowBounds);
441             mShadow.draw(canvas);
442             mShadowDrawn = true;
443         }
444     }
445
446     public void drawPartialImage(Canvas canvas, Bitmap image) {
447         boolean showsOriginal = MasterImage.getImage().showsOriginal();
448         if (!showsOriginal && !mTouchShowOriginal)
449             return;
450         canvas.save();
451         if (image != null) {
452             if (mShowOriginalDirection == 0) {
453                 if (Math.abs(mTouch.y - mTouchDown.y) > Math.abs(mTouch.x - mTouchDown.x)) {
454                     mShowOriginalDirection = UNVEIL_VERTICAL;
455                 } else {
456                     mShowOriginalDirection = UNVEIL_HORIZONTAL;
457                 }
458             }
459
460             int px = 0;
461             int py = 0;
462             if (mShowOriginalDirection == UNVEIL_VERTICAL) {
463                 px = mImageBounds.width();
464                 py = mTouch.y - mImageBounds.top;
465             } else {
466                 px = mTouch.x - mImageBounds.left;
467                 py = mImageBounds.height();
468                 if (showsOriginal) {
469                     px = mImageBounds.width();
470                 }
471             }
472
473             Rect d = new Rect(mImageBounds.left, mImageBounds.top,
474                     mImageBounds.left + px, mImageBounds.top + py);
475             canvas.clipRect(d);
476             drawImage(canvas, image, false);
477             Paint paint = new Paint();
478             paint.setColor(Color.BLACK);
479             paint.setStrokeWidth(3);
480
481             if (mShowOriginalDirection == UNVEIL_VERTICAL) {
482                 canvas.drawLine(mImageBounds.left, mTouch.y,
483                         mImageBounds.right, mTouch.y, paint);
484             } else {
485                 canvas.drawLine(mTouch.x, mImageBounds.top,
486                         mTouch.x, mImageBounds.bottom, paint);
487             }
488
489             Rect bounds = new Rect();
490             paint.setAntiAlias(true);
491             paint.setTextSize(mOriginalTextSize);
492             paint.getTextBounds(mOriginalText, 0, mOriginalText.length(), bounds);
493             paint.setColor(Color.BLACK);
494             paint.setStyle(Paint.Style.STROKE);
495             paint.setStrokeWidth(3);
496             canvas.drawText(mOriginalText, mImageBounds.left + mOriginalTextMargin,
497                     mImageBounds.top + bounds.height() + mOriginalTextMargin, paint);
498             paint.setStyle(Paint.Style.FILL);
499             paint.setStrokeWidth(1);
500             paint.setColor(Color.WHITE);
501             canvas.drawText(mOriginalText, mImageBounds.left + mOriginalTextMargin,
502                     mImageBounds.top + bounds.height() + mOriginalTextMargin, paint);
503         }
504         canvas.restore();
505     }
506
507     public void bindAsImageLoadListener() {
508         MasterImage.getImage().addListener(this);
509     }
510
511     public void updateImage() {
512         invalidate();
513     }
514
515     public void imageLoaded() {
516         updateImage();
517     }
518
519     public void saveImage(FilterShowActivity filterShowActivity, File file) {
520         SaveImage.saveImage(getImagePreset(), filterShowActivity, file);
521     }
522
523
524     public boolean scaleInProgress() {
525         return mScaleGestureDetector.isInProgress();
526     }
527
528     @Override
529     public boolean onTouchEvent(MotionEvent event) {
530         super.onTouchEvent(event);
531         int action = event.getAction();
532         action = action & MotionEvent.ACTION_MASK;
533
534         mGestureDetector.onTouchEvent(event);
535         boolean scaleInProgress = scaleInProgress();
536         mScaleGestureDetector.onTouchEvent(event);
537         if (mInteractionMode == InteractionMode.SCALE) {
538             return true;
539         }
540         if (!scaleInProgress() && scaleInProgress) {
541             // If we were scaling, the scale will stop but we will
542             // still issue an ACTION_UP. Let the subclasses know.
543             mFinishedScalingOperation = true;
544         }
545
546         int ex = (int) event.getX();
547         int ey = (int) event.getY();
548         if (action == MotionEvent.ACTION_DOWN) {
549             mInteractionMode = InteractionMode.MOVE;
550             mTouchDown.x = ex;
551             mTouchDown.y = ey;
552             mTouchShowOriginalDate = System.currentTimeMillis();
553             mShowOriginalDirection = 0;
554             MasterImage.getImage().setOriginalTranslation(MasterImage.getImage().getTranslation());
555         }
556
557         if (action == MotionEvent.ACTION_MOVE && mInteractionMode == InteractionMode.MOVE) {
558             mTouch.x = ex;
559             mTouch.y = ey;
560
561             float scaleFactor = MasterImage.getImage().getScaleFactor();
562             if (scaleFactor > 1 && (!ENABLE_ZOOMED_COMPARISON || event.getPointerCount() == 2)) {
563                 float translateX = (mTouch.x - mTouchDown.x) / scaleFactor;
564                 float translateY = (mTouch.y - mTouchDown.y) / scaleFactor;
565                 Point originalTranslation = MasterImage.getImage().getOriginalTranslation();
566                 Point translation = MasterImage.getImage().getTranslation();
567                 translation.x = (int) (originalTranslation.x + translateX);
568                 translation.y = (int) (originalTranslation.y + translateY);
569                 constrainTranslation(translation, scaleFactor);
570                 MasterImage.getImage().setTranslation(translation);
571                 mTouchShowOriginal = false;
572             } else if (enableComparison() && !mOriginalDisabled
573                     && (System.currentTimeMillis() - mTouchShowOriginalDate
574                             > mTouchShowOriginalDelayMin)
575                     && event.getPointerCount() == 1) {
576                 mTouchShowOriginal = true;
577             }
578         }
579
580         if (action == MotionEvent.ACTION_UP) {
581             mInteractionMode = InteractionMode.NONE;
582             mTouchShowOriginal = false;
583             mTouchDown.x = 0;
584             mTouchDown.y = 0;
585             mTouch.x = 0;
586             mTouch.y = 0;
587             if (MasterImage.getImage().getScaleFactor() <= 1) {
588                 MasterImage.getImage().setScaleFactor(1);
589                 MasterImage.getImage().resetTranslation();
590             }
591         }
592         invalidate();
593         return true;
594     }
595
596     protected boolean enableComparison() {
597         return true;
598     }
599
600     @Override
601     public boolean onDoubleTap(MotionEvent arg0) {
602         mZoomIn = !mZoomIn;
603         float scale = 1.0f;
604         if (mZoomIn) {
605             scale = MasterImage.getImage().getMaxScaleFactor();
606         }
607         if (scale != MasterImage.getImage().getScaleFactor()) {
608             MasterImage.getImage().setScaleFactor(scale);
609             float translateX = (getWidth() / 2 - arg0.getX());
610             float translateY = (getHeight() / 2 - arg0.getY());
611             Point translation = MasterImage.getImage().getTranslation();
612             translation.x = (int) (mOriginalTranslation.x + translateX);
613             translation.y = (int) (mOriginalTranslation.y + translateY);
614             constrainTranslation(translation, scale);
615             MasterImage.getImage().setTranslation(translation);
616             invalidate();
617         }
618         return true;
619     }
620
621     private void constrainTranslation(Point translation, float scale) {
622         float maxTranslationX = getWidth() / scale;
623         float maxTranslationY = getHeight() / scale;
624         if (Math.abs(translation.x) > maxTranslationX) {
625             translation.x = (int) (Math.signum(translation.x) *
626                     maxTranslationX);
627             if (Math.abs(translation.y) > maxTranslationY) {
628                 translation.y = (int) (Math.signum(translation.y) *
629                         maxTranslationY);
630             }
631
632         }
633     }
634
635     @Override
636     public boolean onDoubleTapEvent(MotionEvent arg0) {
637         return false;
638     }
639
640     @Override
641     public boolean onSingleTapConfirmed(MotionEvent arg0) {
642         return false;
643     }
644
645     @Override
646     public boolean onDown(MotionEvent arg0) {
647         return false;
648     }
649
650     @Override
651     public boolean onFling(MotionEvent startEvent, MotionEvent endEvent, float arg2, float arg3) {
652         if (mActivity == null) {
653             return false;
654         }
655         if (endEvent.getPointerCount() == 2) {
656             return false;
657         }
658         return true;
659     }
660
661     @Override
662     public void onLongPress(MotionEvent arg0) {
663     }
664
665     @Override
666     public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) {
667         return false;
668     }
669
670     @Override
671     public void onShowPress(MotionEvent arg0) {
672     }
673
674     @Override
675     public boolean onSingleTapUp(MotionEvent arg0) {
676         return false;
677     }
678
679     public boolean useUtilityPanel() {
680         return false;
681     }
682
683     public void openUtilityPanel(final LinearLayout accessoryViewList) {
684     }
685
686     @Override
687     public boolean onScale(ScaleGestureDetector detector) {
688         MasterImage img = MasterImage.getImage();
689         float scaleFactor = img.getScaleFactor();
690
691         scaleFactor = scaleFactor * detector.getScaleFactor();
692         if (scaleFactor > MasterImage.getImage().getMaxScaleFactor()) {
693             scaleFactor = MasterImage.getImage().getMaxScaleFactor();
694         }
695         if (scaleFactor < 0.5) {
696             scaleFactor = 0.5f;
697         }
698         MasterImage.getImage().setScaleFactor(scaleFactor);
699         scaleFactor = img.getScaleFactor();
700         float focusx = detector.getFocusX();
701         float focusy = detector.getFocusY();
702         float translateX = (focusx - mStartFocusX) / scaleFactor;
703         float translateY = (focusy - mStartFocusY) / scaleFactor;
704         Point translation = MasterImage.getImage().getTranslation();
705         translation.x = (int) (mOriginalTranslation.x + translateX);
706         translation.y = (int) (mOriginalTranslation.y + translateY);
707         constrainTranslation(translation, scaleFactor);
708         MasterImage.getImage().setTranslation(translation);
709
710         invalidate();
711         return true;
712     }
713
714     @Override
715     public boolean onScaleBegin(ScaleGestureDetector detector) {
716         Point pos = MasterImage.getImage().getTranslation();
717         mOriginalTranslation.x = pos.x;
718         mOriginalTranslation.y = pos.y;
719         mOriginalScale = MasterImage.getImage().getScaleFactor();
720         mStartFocusX = detector.getFocusX();
721         mStartFocusY = detector.getFocusY();
722         mInteractionMode = InteractionMode.SCALE;
723         return true;
724     }
725
726     @Override
727     public void onScaleEnd(ScaleGestureDetector detector) {
728         mInteractionMode = InteractionMode.NONE;
729         if (MasterImage.getImage().getScaleFactor() < 1) {
730             MasterImage.getImage().setScaleFactor(1);
731             invalidate();
732         }
733     }
734
735     public boolean didFinishScalingOperation() {
736         if (mFinishedScalingOperation) {
737             mFinishedScalingOperation = false;
738             return true;
739         }
740         return false;
741     }
742
743 }