OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / packages / apps / Gallery3D / src / com / cooliris / media / HighlightView.java
1 /*
2  * Copyright (C) 2007 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.cooliris.media;
18
19 import android.graphics.Canvas;
20 import android.graphics.Matrix;
21 import android.graphics.Paint;
22 import android.graphics.Path;
23 import android.graphics.Rect;
24 import android.graphics.RectF;
25 import android.graphics.Region;
26 import android.graphics.drawable.Drawable;
27 import android.view.View;
28
29 import com.cooliris.app.Res;
30
31 // This class is used by CropImage to display a highlighted cropping rectangle
32 // overlayed with the image. There are two coordinate spaces in use. One is
33 // image, another is screen. computeLayout() uses mMatrix to map from image
34 // space to screen space.
35 class HighlightView {
36
37     @SuppressWarnings("unused")
38     private static final String TAG = "HighlightView";
39     View mContext; // The View displaying the image.
40
41     public static final int GROW_NONE = (1 << 0);
42     public static final int GROW_LEFT_EDGE = (1 << 1);
43     public static final int GROW_RIGHT_EDGE = (1 << 2);
44     public static final int GROW_TOP_EDGE = (1 << 3);
45     public static final int GROW_BOTTOM_EDGE = (1 << 4);
46     public static final int MOVE = (1 << 5);
47
48     public HighlightView(View ctx) {
49         mContext = ctx;
50     }
51
52     private void init() {
53         android.content.res.Resources resources = mContext.getResources();
54         mResizeDrawableWidth = resources.getDrawable(Res.drawable.camera_crop_width);
55         mResizeDrawableHeight = resources.getDrawable(Res.drawable.camera_crop_height);
56         mResizeDrawableDiagonal = resources.getDrawable(Res.drawable.indicator_autocrop);
57     }
58
59     boolean mIsFocused;
60     boolean mHidden;
61
62     public boolean hasFocus() {
63         return mIsFocused;
64     }
65
66     public void setFocus(boolean f) {
67         mIsFocused = f;
68     }
69
70     public void setHidden(boolean hidden) {
71         mHidden = hidden;
72     }
73
74     protected void draw(Canvas canvas) {
75         if (mHidden) {
76             return;
77         }
78         canvas.save();
79         Path path = new Path();
80         if (!hasFocus()) {
81             mOutlinePaint.setColor(0xFF000000);
82             canvas.drawRect(mDrawRect, mOutlinePaint);
83         } else {
84             Rect viewDrawingRect = new Rect();
85             mContext.getDrawingRect(viewDrawingRect);
86             if (mCircle) {
87                 float width = mDrawRect.width();
88                 float height = mDrawRect.height();
89                 path.addCircle(mDrawRect.left + (width / 2), mDrawRect.top + (height / 2), width / 2, Path.Direction.CW);
90                 mOutlinePaint.setColor(0xFFEF04D6);
91             } else {
92                 path.addRect(new RectF(mDrawRect), Path.Direction.CW);
93                 mOutlinePaint.setColor(0xFFFF8A00);
94             }
95             canvas.clipPath(path, Region.Op.DIFFERENCE);
96             canvas.drawRect(viewDrawingRect, hasFocus() ? mFocusPaint : mNoFocusPaint);
97
98             canvas.restore();
99             canvas.drawPath(path, mOutlinePaint);
100
101             if (mMode == ModifyMode.Grow) {
102                 if (mCircle) {
103                     int width = mResizeDrawableDiagonal.getIntrinsicWidth();
104                     int height = mResizeDrawableDiagonal.getIntrinsicHeight();
105
106                     int d = (int) Math.round(Math.cos(/* 45deg */Math.PI / 4D) * (mDrawRect.width() / 2D));
107                     int x = mDrawRect.left + (mDrawRect.width() / 2) + d - width / 2;
108                     int y = mDrawRect.top + (mDrawRect.height() / 2) - d - height / 2;
109                     mResizeDrawableDiagonal.setBounds(x, y, x + mResizeDrawableDiagonal.getIntrinsicWidth(), y
110                             + mResizeDrawableDiagonal.getIntrinsicHeight());
111                     mResizeDrawableDiagonal.draw(canvas);
112                 } else {
113                     int left = mDrawRect.left + 1;
114                     int right = mDrawRect.right + 1;
115                     int top = mDrawRect.top + 4;
116                     int bottom = mDrawRect.bottom + 3;
117
118                     int widthWidth = mResizeDrawableWidth.getIntrinsicWidth() / 2;
119                     int widthHeight = mResizeDrawableWidth.getIntrinsicHeight() / 2;
120                     int heightHeight = mResizeDrawableHeight.getIntrinsicHeight() / 2;
121                     int heightWidth = mResizeDrawableHeight.getIntrinsicWidth() / 2;
122
123                     int xMiddle = mDrawRect.left + ((mDrawRect.right - mDrawRect.left) / 2);
124                     int yMiddle = mDrawRect.top + ((mDrawRect.bottom - mDrawRect.top) / 2);
125
126                     mResizeDrawableWidth.setBounds(left - widthWidth, yMiddle - widthHeight, left + widthWidth, yMiddle
127                             + widthHeight);
128                     mResizeDrawableWidth.draw(canvas);
129
130                     mResizeDrawableWidth.setBounds(right - widthWidth, yMiddle - widthHeight, right + widthWidth, yMiddle
131                             + widthHeight);
132                     mResizeDrawableWidth.draw(canvas);
133
134                     mResizeDrawableHeight.setBounds(xMiddle - heightWidth, top - heightHeight, xMiddle + heightWidth, top
135                             + heightHeight);
136                     mResizeDrawableHeight.draw(canvas);
137
138                     mResizeDrawableHeight.setBounds(xMiddle - heightWidth, bottom - heightHeight, xMiddle + heightWidth, bottom
139                             + heightHeight);
140                     mResizeDrawableHeight.draw(canvas);
141                 }
142             }
143         }
144     }
145
146     public void setMode(ModifyMode mode) {
147         if (mode != mMode) {
148             mMode = mode;
149             mContext.invalidate();
150         }
151     }
152
153     // Determines which edges are hit by touching at (x, y).
154     public int getHit(float x, float y) {
155         Rect r = computeLayout();
156         final float hysteresis = 20F;
157         int retval = GROW_NONE;
158
159         if (mCircle) {
160             float distX = x - r.centerX();
161             float distY = y - r.centerY();
162             int distanceFromCenter = (int) Math.sqrt(distX * distX + distY * distY);
163             int radius = mDrawRect.width() / 2;
164             int delta = distanceFromCenter - radius;
165             if (Math.abs(delta) <= hysteresis) {
166                 if (Math.abs(distY) > Math.abs(distX)) {
167                     if (distY < 0) {
168                         retval = GROW_TOP_EDGE;
169                     } else {
170                         retval = GROW_BOTTOM_EDGE;
171                     }
172                 } else {
173                     if (distX < 0) {
174                         retval = GROW_LEFT_EDGE;
175                     } else {
176                         retval = GROW_RIGHT_EDGE;
177                     }
178                 }
179             } else if (distanceFromCenter < radius) {
180                 retval = MOVE;
181             } else {
182                 retval = GROW_NONE;
183             }
184         } else {
185             // verticalCheck makes sure the position is between the top and
186             // the bottom edge (with some tolerance). Similar for horizCheck.
187             boolean verticalCheck = (y >= r.top - hysteresis) && (y < r.bottom + hysteresis);
188             boolean horizCheck = (x >= r.left - hysteresis) && (x < r.right + hysteresis);
189
190             // Check whether the position is near some edge(s).
191             if ((Math.abs(r.left - x) < hysteresis) && verticalCheck) {
192                 retval |= GROW_LEFT_EDGE;
193             }
194             if ((Math.abs(r.right - x) < hysteresis) && verticalCheck) {
195                 retval |= GROW_RIGHT_EDGE;
196             }
197             if ((Math.abs(r.top - y) < hysteresis) && horizCheck) {
198                 retval |= GROW_TOP_EDGE;
199             }
200             if ((Math.abs(r.bottom - y) < hysteresis) && horizCheck) {
201                 retval |= GROW_BOTTOM_EDGE;
202             }
203
204             // Not near any edge but inside the rectangle: move.
205             if (retval == GROW_NONE && r.contains((int) x, (int) y)) {
206                 retval = MOVE;
207             }
208         }
209         return retval;
210     }
211
212     // Handles motion (dx, dy) in screen space.
213     // The "edge" parameter specifies which edges the user is dragging.
214     void handleMotion(int edge, float dx, float dy) {
215         Rect r = computeLayout();
216         if (edge == GROW_NONE) {
217             return;
218         } else if (edge == MOVE) {
219             // Convert to image space before sending to moveBy().
220             moveBy(dx * (mCropRect.width() / r.width()), dy * (mCropRect.height() / r.height()));
221         } else {
222             if (((GROW_LEFT_EDGE | GROW_RIGHT_EDGE) & edge) == 0) {
223                 dx = 0;
224             }
225
226             if (((GROW_TOP_EDGE | GROW_BOTTOM_EDGE) & edge) == 0) {
227                 dy = 0;
228             }
229
230             // Convert to image space before sending to growBy().
231             float xDelta = dx * (mCropRect.width() / r.width());
232             float yDelta = dy * (mCropRect.height() / r.height());
233             growBy((((edge & GROW_LEFT_EDGE) != 0) ? -1 : 1) * xDelta, (((edge & GROW_TOP_EDGE) != 0) ? -1 : 1) * yDelta);
234         }
235     }
236
237     // Grows the cropping rectange by (dx, dy) in image space.
238     void moveBy(float dx, float dy) {
239         Rect invalRect = new Rect(mDrawRect);
240
241         mCropRect.offset(dx, dy);
242
243         // Put the cropping rectangle inside image rectangle.
244         mCropRect.offset(Math.max(0, mImageRect.left - mCropRect.left), Math.max(0, mImageRect.top - mCropRect.top));
245
246         mCropRect.offset(Math.min(0, mImageRect.right - mCropRect.right), Math.min(0, mImageRect.bottom - mCropRect.bottom));
247
248         mDrawRect = computeLayout();
249         invalRect.union(mDrawRect);
250         invalRect.inset(-10, -10);
251         mContext.invalidate(invalRect);
252     }
253
254     // Grows the cropping rectange by (dx, dy) in image space.
255     void growBy(float dx, float dy) {
256         if (mMaintainAspectRatio) {
257             if (dx != 0) {
258                 dy = dx / mInitialAspectRatio;
259             } else if (dy != 0) {
260                 dx = dy * mInitialAspectRatio;
261             }
262         }
263
264         // Don't let the cropping rectangle grow too fast.
265         // Grow at most half of the difference between the image rectangle and
266         // the cropping rectangle.
267         RectF r = new RectF(mCropRect);
268         if (dx > 0F && r.width() + 2 * dx > mImageRect.width()) {
269             float adjustment = (mImageRect.width() - r.width()) / 2F;
270             dx = adjustment;
271             if (mMaintainAspectRatio) {
272                 dy = dx / mInitialAspectRatio;
273             }
274         }
275         if (dy > 0F && r.height() + 2 * dy > mImageRect.height()) {
276             float adjustment = (mImageRect.height() - r.height()) / 2F;
277             dy = adjustment;
278             if (mMaintainAspectRatio) {
279                 dx = dy * mInitialAspectRatio;
280             }
281         }
282
283         r.inset(-dx, -dy);
284
285         // Don't let the cropping rectangle shrink too fast.
286         final float widthCap = 25F;
287         if (r.width() < widthCap) {
288             r.inset(-(widthCap - r.width()) / 2F, 0F);
289         }
290         float heightCap = mMaintainAspectRatio ? (widthCap / mInitialAspectRatio) : widthCap;
291         if (r.height() < heightCap) {
292             r.inset(0F, -(heightCap - r.height()) / 2F);
293         }
294
295         // Put the cropping rectangle inside the image rectangle.
296         if (r.left < mImageRect.left) {
297             r.offset(mImageRect.left - r.left, 0F);
298         } else if (r.right > mImageRect.right) {
299             r.offset(-(r.right - mImageRect.right), 0);
300         }
301         if (r.top < mImageRect.top) {
302             r.offset(0F, mImageRect.top - r.top);
303         } else if (r.bottom > mImageRect.bottom) {
304             r.offset(0F, -(r.bottom - mImageRect.bottom));
305         }
306
307         mCropRect.set(r);
308         mDrawRect = computeLayout();
309         mContext.invalidate();
310     }
311
312     // Returns the cropping rectangle in image space.
313     public Rect getCropRect() {
314         return new Rect((int) mCropRect.left, (int) mCropRect.top, (int) mCropRect.right, (int) mCropRect.bottom);
315     }
316
317     // Maps the cropping rectangle from image space to screen space.
318     private Rect computeLayout() {
319         RectF r = new RectF(mCropRect.left, mCropRect.top, mCropRect.right, mCropRect.bottom);
320         mMatrix.mapRect(r);
321         return new Rect(Math.round(r.left), Math.round(r.top), Math.round(r.right), Math.round(r.bottom));
322     }
323
324     public void invalidate() {
325         mDrawRect = computeLayout();
326     }
327
328     public void setup(Matrix m, Rect imageRect, RectF cropRect, boolean circle, boolean maintainAspectRatio) {
329         if (circle) {
330             maintainAspectRatio = true;
331         }
332         mMatrix = new Matrix(m);
333
334         mCropRect = cropRect;
335         mImageRect = new RectF(imageRect);
336         mMaintainAspectRatio = maintainAspectRatio;
337         mCircle = circle;
338
339         mInitialAspectRatio = mCropRect.width() / mCropRect.height();
340         mDrawRect = computeLayout();
341
342         mFocusPaint.setARGB(125, 50, 50, 50);
343         mNoFocusPaint.setARGB(125, 50, 50, 50);
344         mOutlinePaint.setStrokeWidth(3F);
345         mOutlinePaint.setStyle(Paint.Style.STROKE);
346         mOutlinePaint.setAntiAlias(true);
347
348         mMode = ModifyMode.None;
349         init();
350     }
351
352     enum ModifyMode {
353         None, Move, Grow
354     }
355
356     private ModifyMode mMode = ModifyMode.None;
357
358     Rect mDrawRect; // in screen space
359     private RectF mImageRect; // in image space
360     RectF mCropRect; // in image space
361     Matrix mMatrix;
362
363     private boolean mMaintainAspectRatio = false;
364     private float mInitialAspectRatio;
365     private boolean mCircle = false;
366
367     private Drawable mResizeDrawableWidth;
368     private Drawable mResizeDrawableHeight;
369     private Drawable mResizeDrawableDiagonal;
370
371     private final Paint mFocusPaint = new Paint();
372     private final Paint mNoFocusPaint = new Paint();
373     private final Paint mOutlinePaint = new Paint();
374 }