• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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.photoeditor;
18 
19 import android.content.Context;
20 import android.graphics.Canvas;
21 import android.graphics.Matrix;
22 import android.graphics.Paint;
23 import android.graphics.Path;
24 import android.graphics.PointF;
25 import android.graphics.RectF;
26 import android.util.AttributeSet;
27 import android.view.View;
28 import android.view.animation.Animation;
29 
30 import com.android.photoeditor.animation.AnimationPair;
31 
32 /**
33  * Displays photo in the view. All its methods should be called from UI thread.
34  */
35 public class PhotoView extends View {
36 
37     private final Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
38     private final Matrix displayMatrix = new Matrix();
39     private Photo photo;
40     private RectF clipBounds;
41     private AnimationPair transitions;
42 
PhotoView(Context context, AttributeSet attrs)43     public PhotoView(Context context, AttributeSet attrs) {
44         super(context, attrs);
45     }
46 
47     @Override
onDraw(Canvas canvas)48     protected void onDraw(Canvas canvas) {
49         super.onDraw(canvas);
50 
51         if (photo != null) {
52             canvas.save();
53             if (clipBounds != null) {
54                 canvas.clipRect(clipBounds);
55             }
56             canvas.concat(displayMatrix);
57             canvas.drawBitmap(photo.bitmap(), 0, 0, paint);
58             canvas.restore();
59         }
60     }
61 
62     /**
63      * Maps x and y to a percentage position relative to displayed photo.
64      */
mapPhotoPoint(float x, float y)65     public PointF mapPhotoPoint(float x, float y) {
66         if ((photo == null) || (photo.width() == 0) || (photo.height() == 0)) {
67             return new PointF();
68         }
69         float[] point = new float[] {x, y};
70         Matrix matrix = new Matrix();
71         displayMatrix.invert(matrix);
72         matrix.mapPoints(point);
73         return new PointF(point[0] / photo.width(), point[1] / photo.height());
74     }
75 
mapPhotoPath(Path src, Path dst)76     public void mapPhotoPath(Path src, Path dst) {
77         // TODO: Use percentages representing paths for saving photo larger than previewed photo.
78         Matrix matrix = new Matrix();
79         displayMatrix.invert(matrix);
80         src.transform(matrix, dst);
81     }
82 
getPhotoDisplayBounds()83     public RectF getPhotoDisplayBounds() {
84         RectF bounds = getPhotoBounds();
85         displayMatrix.mapRect(bounds);
86         return bounds;
87     }
88 
getPhotoBounds()89     public RectF getPhotoBounds() {
90         return (photo != null) ? new RectF(0, 0, photo.width(), photo.height()) : new RectF();
91     }
92 
93     /**
94      * Transforms display by replacing the display matrix of photo-view with the given matrix.
95      */
transformDisplay(Matrix matrix)96     public void transformDisplay(Matrix matrix) {
97         RectF bounds = getPhotoBounds();
98         matrix.mapRect(bounds);
99         displayMatrix.set(matrix);
100         RectUtils.postCenterMatrix(bounds, this, displayMatrix);
101         invalidate();
102     }
103 
clipPhoto(RectF bounds)104     public void clipPhoto(RectF bounds) {
105         clipBounds = bounds;
106         invalidate();
107     }
108 
109     /**
110      * Updates the photo with animations (if any) and also updates photo display-matrix.
111      */
update(final Photo photo)112     public void update(final Photo photo) {
113         if (transitions == null) {
114             setPhoto(photo);
115             invalidate();
116         } else if (getAnimation() != null) {
117             // Clear old running transitions.
118             clearTransitionAnimations();
119             setPhoto(photo);
120             invalidate();
121         } else {
122             // TODO: Use AnimationSet to chain two animations.
123             transitions.first().setAnimationListener(new Animation.AnimationListener() {
124 
125                 @Override
126                 public void onAnimationStart(Animation animation) {
127                 }
128 
129                 @Override
130                 public void onAnimationRepeat(Animation animation) {
131                 }
132 
133                 @Override
134                 public void onAnimationEnd(final Animation animation) {
135                     post(new Runnable() {
136 
137                         @Override
138                         public void run() {
139                             if ((transitions != null) && (animation == transitions.first())) {
140                                 startAnimation(transitions.second());
141                             }
142                         }
143                     });
144                 }
145             });
146             transitions.second().setAnimationListener(new Animation.AnimationListener() {
147 
148                 @Override
149                 public void onAnimationStart(Animation animation) {
150                     setPhoto(photo);
151                 }
152 
153                 @Override
154                 public void onAnimationRepeat(Animation animation) {
155                 }
156 
157                 @Override
158                 public void onAnimationEnd(Animation animation) {
159                     post(new Runnable() {
160 
161                         @Override
162                         public void run() {
163                             clearTransitionAnimations();
164                         }
165                     });
166                 }
167             });
168             startAnimation(transitions.first());
169         }
170     }
171 
setPhoto(Photo photo)172     private void setPhoto(Photo photo) {
173         if (this.photo != null) {
174             this.photo.clear();
175             this.photo = null;
176         }
177         this.photo = photo;
178 
179         // Scale-down (if necessary) and center the photo for display.
180         displayMatrix.reset();
181         RectF bounds = getPhotoBounds();
182         float scale = RectUtils.getDisplayScale(bounds, this);
183         displayMatrix.setScale(scale, scale);
184         displayMatrix.mapRect(bounds);
185         RectUtils.postCenterMatrix(bounds, this, displayMatrix);
186     }
187 
clearTransitionAnimations()188     private void clearTransitionAnimations() {
189         if (transitions != null) {
190             transitions.first().setAnimationListener(null);
191             transitions.second().setAnimationListener(null);
192             transitions = null;
193             clearAnimation();
194         }
195     }
196 
197     /**
198      * Sets transition animations in the next update() to transit out the current bitmap and
199      * transit in the new replacing one. Transition animations will be cleared once done.
200      */
setTransitionAnimations(AnimationPair transitions)201     public void setTransitionAnimations(AnimationPair transitions) {
202         clearTransitionAnimations();
203         this.transitions = transitions;
204     }
205 }
206