blob: 408beab804fa8542a3b9059bae2bd4e4c6082988 [file] [log] [blame]
The Android Open Source Project1d4c7502008-10-21 07:00:00 -07001/*
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
17package com.android.camera;
18
19import android.graphics.Canvas;
20import android.graphics.Matrix;
21import android.graphics.Paint;
22import android.graphics.Path;
23import android.graphics.Rect;
24import android.graphics.RectF;
25import android.graphics.Region;
26import android.graphics.drawable.Drawable;
27import android.util.Config;
28import android.util.Log;
29import android.view.KeyEvent;
30import android.view.View;
31
32
33public class HighlightView
34{
35 private static final String TAG = "CropImage";
36 View mContext;
37 Path mPath;
38 Rect mViewDrawingRect = new Rect();
39
40 int mMotionMode;
41
42 public static final int GROW_NONE = (1 << 0);
43 public static final int GROW_LEFT_EDGE = (1 << 1);
44 public static final int GROW_RIGHT_EDGE = (1 << 2);
45 public static final int GROW_TOP_EDGE = (1 << 3);
46 public static final int GROW_BOTTOM_EDGE = (1 << 4);
47 public static final int MOVE = (1 << 5);
48
49 public HighlightView(View ctx)
50 {
51 super();
52 mContext = ctx;
53 mPath = new Path();
54 }
55
56 private void initHighlightView() {
57 android.content.res.Resources resources = mContext.getResources();
58 mResizeDrawableWidth = resources.getDrawable(R.drawable.camera_crop_width);
59 mResizeDrawableHeight = resources.getDrawable(R.drawable.camera_crop_height);
60 mResizeDrawableDiagonal = resources.getDrawable(R.drawable.indicator_autocrop);
61 }
62
63 boolean mIsFocused;
64 boolean mHidden;
65
66 public boolean hasFocus() {
67 return mIsFocused;
68 }
69
70 public void setFocus(boolean f) {
71 mIsFocused = f;
72 }
73
74 public void setHidden(boolean hidden) {
75 mHidden = hidden;
76 }
77
78 protected void draw(Canvas canvas) {
79 if (mHidden)
80 return;
81
82 canvas.save();
83 mPath.reset();
84 if (!hasFocus()) {
85 mOutlinePaint.setColor(0xFF000000);
86 canvas.drawRect(mDrawRect, mOutlinePaint);
87 } else {
88 mContext.getDrawingRect(mViewDrawingRect);
89 if (mCircle) {
90 float width = mDrawRect.width() - (getPaddingLeft() + getPaddingRight());
91 float height = mDrawRect.height() - (getPaddingTop() + getPaddingBottom());
92 mPath.addCircle(
93 mDrawRect.left + getPaddingLeft() + (width / 2),
94 mDrawRect.top + getPaddingTop() + (height / 2),
95 width / 2,
96 Path.Direction.CW);
97 mOutlinePaint.setColor(0xFFEF04D6);
98 } else {
99 mPath.addRect(new RectF(mDrawRect), Path.Direction.CW);
100 mOutlinePaint.setColor(0xFFFF8A00);
101 }
102 canvas.clipPath(mPath, Region.Op.DIFFERENCE);
103 canvas.drawRect(mViewDrawingRect, hasFocus() ? mFocusPaint : mNoFocusPaint);
104
105 canvas.restore();
106 canvas.drawPath(mPath, mOutlinePaint);
107
108 if (mMode == ModifyMode.Grow) {
109 if (mCircle) {
110 int width = mResizeDrawableDiagonal.getIntrinsicWidth();
111 int height = mResizeDrawableDiagonal.getIntrinsicHeight();
112
113 int d = (int) Math.round(Math.cos(/*45deg*/Math.PI/4D) * (mDrawRect.width() / 2D));
114 int x = mDrawRect.left + (mDrawRect.width() / 2) + d - width/2;
115 int y = mDrawRect.top + (mDrawRect.height() / 2) - d - height/2;
116 mResizeDrawableDiagonal.setBounds(x, y, x + mResizeDrawableDiagonal.getIntrinsicWidth(), y + mResizeDrawableDiagonal.getIntrinsicHeight());
117 mResizeDrawableDiagonal.draw(canvas);
118 } else {
119 int left = mDrawRect.left + 1;
120 int right = mDrawRect.right + 1;
121 int top = mDrawRect.top + 4;
122 int bottom = mDrawRect.bottom + 3;
123
124 int widthWidth = mResizeDrawableWidth.getIntrinsicWidth() / 2;
125 int widthHeight = mResizeDrawableWidth.getIntrinsicHeight()/ 2;
126 int heightHeight = mResizeDrawableHeight.getIntrinsicHeight()/ 2;
127 int heightWidth = mResizeDrawableHeight.getIntrinsicWidth()/ 2;
128
129 int xMiddle = mDrawRect.left + ((mDrawRect.right - mDrawRect.left) / 2);
130 int yMiddle = mDrawRect.top + ((mDrawRect.bottom - mDrawRect.top ) / 2);
131
132 mResizeDrawableWidth.setBounds(left-widthWidth, yMiddle-widthHeight, left+widthWidth, yMiddle+widthHeight);
133 mResizeDrawableWidth.draw(canvas);
134
135 mResizeDrawableWidth.setBounds(right-widthWidth, yMiddle-widthHeight, right+widthWidth, yMiddle+widthHeight);
136 mResizeDrawableWidth.draw(canvas);
137
138 mResizeDrawableHeight.setBounds(xMiddle-heightWidth, top-heightHeight, xMiddle+heightWidth, top+heightHeight);
139 mResizeDrawableHeight.draw(canvas);
140
141 mResizeDrawableHeight.setBounds(xMiddle-heightWidth, bottom-heightHeight, xMiddle+heightWidth, bottom+heightHeight);
142 mResizeDrawableHeight.draw(canvas);
143 }
144 }
145 }
146
147 }
148
149 float getPaddingTop() { return 0F; }
150 float getPaddingBottom() { return 0F; }
151 float getPaddingLeft() { return 0F; }
152 float getPaddingRight() { return 0F; }
153
154 public ModifyMode getMode() {
155 return mMode;
156 }
157
158 public void setMode(ModifyMode mode)
159 {
160 if (mode != mMode) {
161 mMode = mode;
162 mContext.invalidate();
163 }
164 }
165
166 public int getHit(float x, float y) {
167 Rect r = computeLayout();
168 final float hysteresis = 20F;
169 int retval = GROW_NONE;
170
171 if (mCircle) {
172 float distX = x - r.centerX();
173 float distY = y - r.centerY();
174 int distanceFromCenter = (int) Math.sqrt(distX*distX + distY*distY);
175 int radius = (int) (mDrawRect.width() - getPaddingLeft()) / 2;
176 int delta = distanceFromCenter - radius;
177 if (Math.abs(delta) <= hysteresis) {
178 if (Math.abs(distY) > Math.abs(distX)) {
179 if (distY < 0)
180 retval = GROW_TOP_EDGE;
181 else
182 retval = GROW_BOTTOM_EDGE;
183 } else {
184 if (distX < 0)
185 retval = GROW_LEFT_EDGE;
186 else
187 retval = GROW_RIGHT_EDGE;
188 }
189 } else if (distanceFromCenter < radius) {
190 retval = MOVE;
191 } else {
192 retval = GROW_NONE;
193 }
194// Log.v(TAG, "radius: " + radius + "; touchRadius: " + distanceFromCenter + "; distX: " + distX + "; distY: " + distY + "; retval: " + retval);
195 } else {
196 boolean verticalCheck = (y >= r.top - hysteresis) && (y < r.bottom + hysteresis);
197 boolean horizCheck = (x >= r.left - hysteresis) && (x < r.right + hysteresis);
198
199 if ((Math.abs(r.left - x) < hysteresis) && verticalCheck)
200 retval |= GROW_LEFT_EDGE;
201 if ((Math.abs(r.right - x) < hysteresis) && verticalCheck)
202 retval |= GROW_RIGHT_EDGE;
203 if ((Math.abs(r.top - y) < hysteresis) && horizCheck)
204 retval |= GROW_TOP_EDGE;
205 if ((Math.abs(r.bottom - y) < hysteresis) && horizCheck)
206 retval |= GROW_BOTTOM_EDGE;
207
208 if (retval == GROW_NONE && r.contains((int)x, (int)y))
209 retval = MOVE;
210 }
211 return retval;
212 }
213
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 moveBy(dx * (mCropRect.width() / r.width()),
220 dy * (mCropRect.height() / r.height()));
221 } else {
222 if (((GROW_LEFT_EDGE | GROW_RIGHT_EDGE) & edge) == 0)
223 dx = 0;
224
225 if (((GROW_TOP_EDGE | GROW_BOTTOM_EDGE) & edge) == 0)
226 dy = 0;
227
228 float xDelta = dx * (mCropRect.width() / r.width());
229 float yDelta = dy * (mCropRect.height() / r.height());
230 growBy((((edge & GROW_LEFT_EDGE) != 0) ? -1 : 1) * xDelta,
231 (((edge & GROW_TOP_EDGE) != 0) ? -1 : 1) * yDelta);
232
233 }
234// Log.v(TAG, "ratio is now " + this.mCropRect.width() / this.mCropRect.height());
235 }
236
237 void moveBy(float dx, float dy) {
238 Rect invalRect = new Rect(mDrawRect);
239
240 mCropRect.offset(dx, dy);
241 mCropRect.offset(
242 Math.max(0, mImageRect.left - mCropRect.left),
243 Math.max(0, mImageRect.top - mCropRect.top));
244
245 mCropRect.offset(
246 Math.min(0, mImageRect.right - mCropRect.right),
247 Math.min(0, mImageRect.bottom - mCropRect.bottom));
248
249 mDrawRect = computeLayout();
250 invalRect.union(mDrawRect);
251 invalRect.inset(-10, -10);
252 mContext.invalidate(invalRect);
253 }
254
255 private void shift(RectF r, float dx, float dy) {
256 r.left += dx;
257 r.right += dx;
258 r.top += dy;
259 r.bottom += dy;
260 }
261
262 void growBy(float dx, float dy) {
263// Log.v(TAG, "growBy: " + dx + " " + dy + "; rect w/h is " + mCropRect.width() + " / " + mCropRect.height());
264 if (mMaintainAspectRatio) {
265 if (dx != 0) {
266 dy = dx / mInitialAspectRatio;
267 } else if (dy != 0) {
268 dx = dy * mInitialAspectRatio;
269 }
270 }
271
272 RectF r = new RectF(mCropRect);
273 if (dx > 0F && r.width() + 2 * dx > mImageRect.width()) {
274 float adjustment = (mImageRect.width() - r.width()) / 2F;
275 dx = adjustment;
276 if (mMaintainAspectRatio)
277 dy = dx / mInitialAspectRatio;
278 }
279 if (dy > 0F && r.height() + 2 * dy > mImageRect.height()) {
280 float adjustment = (mImageRect.height() - r.height()) / 2F;
281 dy = adjustment;
282 if (mMaintainAspectRatio)
283 dx = dy * mInitialAspectRatio;
284 }
285
286 r.inset(-dx, -dy);
287
288 float widthCap = 25F;
289 if (r.width() < 25) {
290 r.inset(-(25F-r.width())/2F, 0F);
291 }
292 float heightCap = mMaintainAspectRatio ? (widthCap / mInitialAspectRatio) : widthCap;
293 if (r.height() < heightCap) {
294 r.inset(0F, -(heightCap-r.height())/2F);
295 }
296
297 if (r.left < mImageRect.left) {
298 shift(r, mImageRect.left - r.left, 0F);
299 } else if (r.right > mImageRect.right) {
300 shift(r, -(r.right - mImageRect.right), 0);
301 }
302 if (r.top < mImageRect.top) {
303 shift(r, 0F, mImageRect.top - r.top);
304 } else if (r.bottom > mImageRect.bottom) {
305 shift(r, 0F, -(r.bottom - mImageRect.bottom));
306 }
307/*
308 RectF rCandidate = new RectF(r);
309 r.intersect(mImageRect);
310 if (mMaintainAspectRatio) {
311 if (r.left != rCandidate.left) {
312 Log.v(TAG, "bail 1");
313 return;
314 }
315 if (r.right != rCandidate.right) {
316 Log.v(TAG, "bail 2");
317 return;
318 }
319 if (r.top != rCandidate.top) {
320 Log.v(TAG, "bail 3");
321 return;
322 }
323 if (r.bottom != rCandidate.bottom) {
324 Log.v(TAG, "bail 4");
325 return;
326 }
327 }
328*/
329 mCropRect.set(r);
330 mDrawRect = computeLayout();
331 mContext.invalidate();
332 }
333
334 public Rect getCropRect() {
335 return new Rect((int)mCropRect.left, (int)mCropRect.top, (int)mCropRect.right, (int)mCropRect.bottom);
336 }
337
338 private Rect computeLayout() {
339 RectF r = new RectF(mCropRect.left, mCropRect.top, mCropRect.right, mCropRect.bottom);
340 mMatrix.mapRect(r);
341 return new Rect(Math.round(r.left), Math.round(r.top), Math.round(r.right), Math.round(r.bottom));
342 }
343
344 public void invalidate() {
345 mDrawRect = computeLayout();
346 }
347
348 public void setup(Matrix m, Rect imageRect, RectF cropRect, boolean circle, boolean maintainAspectRatio) {
349 if (Config.LOGV) Log.v(TAG, "setup... " + imageRect + "; " + cropRect + "; maintain " + maintainAspectRatio + "; circle " + circle);
350 if (circle)
351 maintainAspectRatio = true;
352 mMatrix = new Matrix(m);
353
354 mCropRect = cropRect;
355 mImageRect = new RectF(imageRect);
356 mMaintainAspectRatio = maintainAspectRatio;
357 mCircle = circle;
358
359 mInitialAspectRatio = mCropRect.width() / mCropRect.height();
360 mDrawRect = computeLayout();
361
362 mFocusPaint.setARGB(125, 50, 50, 50);
363 mNoFocusPaint.setARGB(125, 50, 50, 50);
364 mOutlinePaint.setStrokeWidth(3F);
365 mOutlinePaint.setStyle(Paint.Style.STROKE);
366 mOutlinePaint.setAntiAlias(true);
367
368 mMode = ModifyMode.None;
369 initHighlightView();
370 }
371
372 public void modify(int keyCode, long repeatCount)
373 {
374 float factor = Math.max(.01F, Math.min(.1F, repeatCount * .01F));
375 float widthUnits = factor * (float)mContext.getWidth();
376 float heightUnits = widthUnits;
377
378 switch (keyCode)
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800379 {
380 case KeyEvent.KEYCODE_DPAD_LEFT:
381 if (mMode == ModifyMode.Move)
382 moveBy(-widthUnits, 0);
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700383 else if (mMode == ModifyMode.Grow)
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800384 growBy(-widthUnits, 0);
385 break;
386
387 case KeyEvent.KEYCODE_DPAD_RIGHT:
388 if (mMode == ModifyMode.Move)
389 moveBy(widthUnits, 0);
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700390 else if (mMode == ModifyMode.Grow)
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800391 growBy(widthUnits, 0);
392 break;
393
394 case KeyEvent.KEYCODE_DPAD_UP:
395 if (mMode == ModifyMode.Move)
396 moveBy(0, -heightUnits);
397 else if (mMode == ModifyMode.Grow)
398 growBy(0, -heightUnits);
399 break;
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700400
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800401 case KeyEvent.KEYCODE_DPAD_DOWN:
402 if (mMode == ModifyMode.Move)
403 moveBy(0, heightUnits);
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700404 else if (mMode == ModifyMode.Grow)
The Android Open Source Project0eaec582008-12-17 18:05:54 -0800405 growBy(0, heightUnits);
406 break;
407 }
The Android Open Source Project1d4c7502008-10-21 07:00:00 -0700408 }
409
410 enum ModifyMode { None, Move,Grow };
411
412 ModifyMode mMode = ModifyMode.None;
413
414 Rect mDrawRect;
415 RectF mImageRect;
416 RectF mCropRect;
417 Matrix mMatrix;
418
419 boolean mMaintainAspectRatio = false;
420 float mInitialAspectRatio;
421 boolean mCircle = false;
422
423 Drawable mResizeDrawableWidth, mResizeDrawableHeight, mResizeDrawableDiagonal;
424
425 Paint mFocusPaint = new Paint();
426 Paint mNoFocusPaint = new Paint();
427 Paint mOutlinePaint = new Paint();
428}