1666ea1b28a76aeba74744148b15099254d918671Owen Lin/*
2666ea1b28a76aeba74744148b15099254d918671Owen Lin * Copyright (C) 2007 The Android Open Source Project
3666ea1b28a76aeba74744148b15099254d918671Owen Lin *
4666ea1b28a76aeba74744148b15099254d918671Owen Lin * Licensed under the Apache License, Version 2.0 (the "License");
5666ea1b28a76aeba74744148b15099254d918671Owen Lin * you may not use this file except in compliance with the License.
6666ea1b28a76aeba74744148b15099254d918671Owen Lin * You may obtain a copy of the License at
7666ea1b28a76aeba74744148b15099254d918671Owen Lin *
8666ea1b28a76aeba74744148b15099254d918671Owen Lin *      http://www.apache.org/licenses/LICENSE-2.0
9666ea1b28a76aeba74744148b15099254d918671Owen Lin *
10666ea1b28a76aeba74744148b15099254d918671Owen Lin * Unless required by applicable law or agreed to in writing, software
11666ea1b28a76aeba74744148b15099254d918671Owen Lin * distributed under the License is distributed on an "AS IS" BASIS,
12666ea1b28a76aeba74744148b15099254d918671Owen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13666ea1b28a76aeba74744148b15099254d918671Owen Lin * See the License for the specific language governing permissions and
14666ea1b28a76aeba74744148b15099254d918671Owen Lin * limitations under the License.
15666ea1b28a76aeba74744148b15099254d918671Owen Lin */
16666ea1b28a76aeba74744148b15099254d918671Owen Lin
17666ea1b28a76aeba74744148b15099254d918671Owen Linpackage com.android.camera;
18666ea1b28a76aeba74744148b15099254d918671Owen Lin
19d6c2fb7a38fcdb58742fcfffd67a4594487ec71cOwen Linimport com.android.gallery.R;
20d6c2fb7a38fcdb58742fcfffd67a4594487ec71cOwen Lin
21666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.Canvas;
22666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.Matrix;
23666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.Paint;
24666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.Path;
25666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.Rect;
26666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.RectF;
27666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.Region;
28666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.drawable.Drawable;
29666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.view.View;
30666ea1b28a76aeba74744148b15099254d918671Owen Lin
31666ea1b28a76aeba74744148b15099254d918671Owen Lin// This class is used by CropImage to display a highlighted cropping rectangle
32666ea1b28a76aeba74744148b15099254d918671Owen Lin// overlayed with the image. There are two coordinate spaces in use. One is
33666ea1b28a76aeba74744148b15099254d918671Owen Lin// image, another is screen. computeLayout() uses mMatrix to map from image
34666ea1b28a76aeba74744148b15099254d918671Owen Lin// space to screen space.
35666ea1b28a76aeba74744148b15099254d918671Owen Linclass HighlightView {
36666ea1b28a76aeba74744148b15099254d918671Owen Lin
37666ea1b28a76aeba74744148b15099254d918671Owen Lin    @SuppressWarnings("unused")
38666ea1b28a76aeba74744148b15099254d918671Owen Lin    private static final String TAG = "HighlightView";
39666ea1b28a76aeba74744148b15099254d918671Owen Lin    View mContext;  // The View displaying the image.
40666ea1b28a76aeba74744148b15099254d918671Owen Lin
41666ea1b28a76aeba74744148b15099254d918671Owen Lin    public static final int GROW_NONE        = (1 << 0);
42666ea1b28a76aeba74744148b15099254d918671Owen Lin    public static final int GROW_LEFT_EDGE   = (1 << 1);
43666ea1b28a76aeba74744148b15099254d918671Owen Lin    public static final int GROW_RIGHT_EDGE  = (1 << 2);
44666ea1b28a76aeba74744148b15099254d918671Owen Lin    public static final int GROW_TOP_EDGE    = (1 << 3);
45666ea1b28a76aeba74744148b15099254d918671Owen Lin    public static final int GROW_BOTTOM_EDGE = (1 << 4);
46666ea1b28a76aeba74744148b15099254d918671Owen Lin    public static final int MOVE             = (1 << 5);
47666ea1b28a76aeba74744148b15099254d918671Owen Lin
48666ea1b28a76aeba74744148b15099254d918671Owen Lin    public HighlightView(View ctx) {
49666ea1b28a76aeba74744148b15099254d918671Owen Lin        mContext = ctx;
50666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
51666ea1b28a76aeba74744148b15099254d918671Owen Lin
52666ea1b28a76aeba74744148b15099254d918671Owen Lin    private void init() {
53666ea1b28a76aeba74744148b15099254d918671Owen Lin        android.content.res.Resources resources = mContext.getResources();
54666ea1b28a76aeba74744148b15099254d918671Owen Lin        mResizeDrawableWidth =
55666ea1b28a76aeba74744148b15099254d918671Owen Lin                resources.getDrawable(R.drawable.camera_crop_width);
56666ea1b28a76aeba74744148b15099254d918671Owen Lin        mResizeDrawableHeight =
57666ea1b28a76aeba74744148b15099254d918671Owen Lin                resources.getDrawable(R.drawable.camera_crop_height);
58666ea1b28a76aeba74744148b15099254d918671Owen Lin        mResizeDrawableDiagonal =
59666ea1b28a76aeba74744148b15099254d918671Owen Lin                resources.getDrawable(R.drawable.indicator_autocrop);
60666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
61666ea1b28a76aeba74744148b15099254d918671Owen Lin
62666ea1b28a76aeba74744148b15099254d918671Owen Lin    boolean mIsFocused;
63666ea1b28a76aeba74744148b15099254d918671Owen Lin    boolean mHidden;
64666ea1b28a76aeba74744148b15099254d918671Owen Lin
65666ea1b28a76aeba74744148b15099254d918671Owen Lin    public boolean hasFocus() {
66666ea1b28a76aeba74744148b15099254d918671Owen Lin        return mIsFocused;
67666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
68666ea1b28a76aeba74744148b15099254d918671Owen Lin
69666ea1b28a76aeba74744148b15099254d918671Owen Lin    public void setFocus(boolean f) {
70666ea1b28a76aeba74744148b15099254d918671Owen Lin        mIsFocused = f;
71666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
72666ea1b28a76aeba74744148b15099254d918671Owen Lin
73666ea1b28a76aeba74744148b15099254d918671Owen Lin    public void setHidden(boolean hidden) {
74666ea1b28a76aeba74744148b15099254d918671Owen Lin        mHidden = hidden;
75666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
76666ea1b28a76aeba74744148b15099254d918671Owen Lin
77666ea1b28a76aeba74744148b15099254d918671Owen Lin    protected void draw(Canvas canvas) {
78666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (mHidden) {
79666ea1b28a76aeba74744148b15099254d918671Owen Lin            return;
80666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
81666ea1b28a76aeba74744148b15099254d918671Owen Lin        canvas.save();
82666ea1b28a76aeba74744148b15099254d918671Owen Lin        Path path = new Path();
83666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (!hasFocus()) {
84666ea1b28a76aeba74744148b15099254d918671Owen Lin            mOutlinePaint.setColor(0xFF000000);
85666ea1b28a76aeba74744148b15099254d918671Owen Lin            canvas.drawRect(mDrawRect, mOutlinePaint);
86666ea1b28a76aeba74744148b15099254d918671Owen Lin        } else {
87666ea1b28a76aeba74744148b15099254d918671Owen Lin            Rect viewDrawingRect = new Rect();
88666ea1b28a76aeba74744148b15099254d918671Owen Lin            mContext.getDrawingRect(viewDrawingRect);
89666ea1b28a76aeba74744148b15099254d918671Owen Lin            if (mCircle) {
90666ea1b28a76aeba74744148b15099254d918671Owen Lin                float width  = mDrawRect.width();
91666ea1b28a76aeba74744148b15099254d918671Owen Lin                float height = mDrawRect.height();
92666ea1b28a76aeba74744148b15099254d918671Owen Lin                path.addCircle(mDrawRect.left + (width  / 2),
93666ea1b28a76aeba74744148b15099254d918671Owen Lin                               mDrawRect.top + (height / 2),
94666ea1b28a76aeba74744148b15099254d918671Owen Lin                               width / 2,
95666ea1b28a76aeba74744148b15099254d918671Owen Lin                               Path.Direction.CW);
96666ea1b28a76aeba74744148b15099254d918671Owen Lin                mOutlinePaint.setColor(0xFFEF04D6);
97666ea1b28a76aeba74744148b15099254d918671Owen Lin            } else {
98666ea1b28a76aeba74744148b15099254d918671Owen Lin                path.addRect(new RectF(mDrawRect), Path.Direction.CW);
99666ea1b28a76aeba74744148b15099254d918671Owen Lin                mOutlinePaint.setColor(0xFFFF8A00);
100666ea1b28a76aeba74744148b15099254d918671Owen Lin            }
101666ea1b28a76aeba74744148b15099254d918671Owen Lin            canvas.clipPath(path, Region.Op.DIFFERENCE);
102666ea1b28a76aeba74744148b15099254d918671Owen Lin            canvas.drawRect(viewDrawingRect,
103666ea1b28a76aeba74744148b15099254d918671Owen Lin                    hasFocus() ? mFocusPaint : mNoFocusPaint);
104666ea1b28a76aeba74744148b15099254d918671Owen Lin
105666ea1b28a76aeba74744148b15099254d918671Owen Lin            canvas.restore();
106666ea1b28a76aeba74744148b15099254d918671Owen Lin            canvas.drawPath(path, mOutlinePaint);
107666ea1b28a76aeba74744148b15099254d918671Owen Lin
108666ea1b28a76aeba74744148b15099254d918671Owen Lin            if (mMode == ModifyMode.Grow) {
109666ea1b28a76aeba74744148b15099254d918671Owen Lin                if (mCircle) {
110666ea1b28a76aeba74744148b15099254d918671Owen Lin                    int width  = mResizeDrawableDiagonal.getIntrinsicWidth();
111666ea1b28a76aeba74744148b15099254d918671Owen Lin                    int height = mResizeDrawableDiagonal.getIntrinsicHeight();
112666ea1b28a76aeba74744148b15099254d918671Owen Lin
113666ea1b28a76aeba74744148b15099254d918671Owen Lin                    int d  = (int) Math.round(Math.cos(/*45deg*/Math.PI / 4D)
114666ea1b28a76aeba74744148b15099254d918671Owen Lin                            * (mDrawRect.width() / 2D));
115666ea1b28a76aeba74744148b15099254d918671Owen Lin                    int x  = mDrawRect.left
116666ea1b28a76aeba74744148b15099254d918671Owen Lin                            + (mDrawRect.width() / 2) + d - width / 2;
117666ea1b28a76aeba74744148b15099254d918671Owen Lin                    int y  = mDrawRect.top
118666ea1b28a76aeba74744148b15099254d918671Owen Lin                            + (mDrawRect.height() / 2) - d - height / 2;
119666ea1b28a76aeba74744148b15099254d918671Owen Lin                    mResizeDrawableDiagonal.setBounds(x, y,
120666ea1b28a76aeba74744148b15099254d918671Owen Lin                            x + mResizeDrawableDiagonal.getIntrinsicWidth(),
121666ea1b28a76aeba74744148b15099254d918671Owen Lin                            y + mResizeDrawableDiagonal.getIntrinsicHeight());
122666ea1b28a76aeba74744148b15099254d918671Owen Lin                    mResizeDrawableDiagonal.draw(canvas);
123666ea1b28a76aeba74744148b15099254d918671Owen Lin                } else {
124666ea1b28a76aeba74744148b15099254d918671Owen Lin                    int left    = mDrawRect.left   + 1;
125666ea1b28a76aeba74744148b15099254d918671Owen Lin                    int right   = mDrawRect.right  + 1;
126666ea1b28a76aeba74744148b15099254d918671Owen Lin                    int top     = mDrawRect.top    + 4;
127666ea1b28a76aeba74744148b15099254d918671Owen Lin                    int bottom  = mDrawRect.bottom + 3;
128666ea1b28a76aeba74744148b15099254d918671Owen Lin
129666ea1b28a76aeba74744148b15099254d918671Owen Lin                    int widthWidth   =
130666ea1b28a76aeba74744148b15099254d918671Owen Lin                            mResizeDrawableWidth.getIntrinsicWidth() / 2;
131666ea1b28a76aeba74744148b15099254d918671Owen Lin                    int widthHeight  =
132666ea1b28a76aeba74744148b15099254d918671Owen Lin                            mResizeDrawableWidth.getIntrinsicHeight() / 2;
133666ea1b28a76aeba74744148b15099254d918671Owen Lin                    int heightHeight =
134666ea1b28a76aeba74744148b15099254d918671Owen Lin                            mResizeDrawableHeight.getIntrinsicHeight() / 2;
135666ea1b28a76aeba74744148b15099254d918671Owen Lin                    int heightWidth  =
136666ea1b28a76aeba74744148b15099254d918671Owen Lin                            mResizeDrawableHeight.getIntrinsicWidth() / 2;
137666ea1b28a76aeba74744148b15099254d918671Owen Lin
138666ea1b28a76aeba74744148b15099254d918671Owen Lin                    int xMiddle = mDrawRect.left
139666ea1b28a76aeba74744148b15099254d918671Owen Lin                            + ((mDrawRect.right  - mDrawRect.left) / 2);
140666ea1b28a76aeba74744148b15099254d918671Owen Lin                    int yMiddle = mDrawRect.top
141666ea1b28a76aeba74744148b15099254d918671Owen Lin                            + ((mDrawRect.bottom - mDrawRect.top) / 2);
142666ea1b28a76aeba74744148b15099254d918671Owen Lin
143666ea1b28a76aeba74744148b15099254d918671Owen Lin                    mResizeDrawableWidth.setBounds(left - widthWidth,
144666ea1b28a76aeba74744148b15099254d918671Owen Lin                                                   yMiddle - widthHeight,
145666ea1b28a76aeba74744148b15099254d918671Owen Lin                                                   left + widthWidth,
146666ea1b28a76aeba74744148b15099254d918671Owen Lin                                                   yMiddle + widthHeight);
147666ea1b28a76aeba74744148b15099254d918671Owen Lin                    mResizeDrawableWidth.draw(canvas);
148666ea1b28a76aeba74744148b15099254d918671Owen Lin
149666ea1b28a76aeba74744148b15099254d918671Owen Lin                    mResizeDrawableWidth.setBounds(right - widthWidth,
150666ea1b28a76aeba74744148b15099254d918671Owen Lin                                                   yMiddle - widthHeight,
151666ea1b28a76aeba74744148b15099254d918671Owen Lin                                                   right + widthWidth,
152666ea1b28a76aeba74744148b15099254d918671Owen Lin                                                   yMiddle + widthHeight);
153666ea1b28a76aeba74744148b15099254d918671Owen Lin                    mResizeDrawableWidth.draw(canvas);
154666ea1b28a76aeba74744148b15099254d918671Owen Lin
155666ea1b28a76aeba74744148b15099254d918671Owen Lin                    mResizeDrawableHeight.setBounds(xMiddle - heightWidth,
156666ea1b28a76aeba74744148b15099254d918671Owen Lin                                                    top - heightHeight,
157666ea1b28a76aeba74744148b15099254d918671Owen Lin                                                    xMiddle + heightWidth,
158666ea1b28a76aeba74744148b15099254d918671Owen Lin                                                    top + heightHeight);
159666ea1b28a76aeba74744148b15099254d918671Owen Lin                    mResizeDrawableHeight.draw(canvas);
160666ea1b28a76aeba74744148b15099254d918671Owen Lin
161666ea1b28a76aeba74744148b15099254d918671Owen Lin                    mResizeDrawableHeight.setBounds(xMiddle - heightWidth,
162666ea1b28a76aeba74744148b15099254d918671Owen Lin                                                    bottom - heightHeight,
163666ea1b28a76aeba74744148b15099254d918671Owen Lin                                                    xMiddle + heightWidth,
164666ea1b28a76aeba74744148b15099254d918671Owen Lin                                                    bottom + heightHeight);
165666ea1b28a76aeba74744148b15099254d918671Owen Lin                    mResizeDrawableHeight.draw(canvas);
166666ea1b28a76aeba74744148b15099254d918671Owen Lin                }
167666ea1b28a76aeba74744148b15099254d918671Owen Lin            }
168666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
169666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
170666ea1b28a76aeba74744148b15099254d918671Owen Lin
171666ea1b28a76aeba74744148b15099254d918671Owen Lin    public void setMode(ModifyMode mode) {
172666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (mode != mMode) {
173666ea1b28a76aeba74744148b15099254d918671Owen Lin            mMode = mode;
174666ea1b28a76aeba74744148b15099254d918671Owen Lin            mContext.invalidate();
175666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
176666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
177666ea1b28a76aeba74744148b15099254d918671Owen Lin
178666ea1b28a76aeba74744148b15099254d918671Owen Lin    // Determines which edges are hit by touching at (x, y).
179666ea1b28a76aeba74744148b15099254d918671Owen Lin    public int getHit(float x, float y) {
180666ea1b28a76aeba74744148b15099254d918671Owen Lin        Rect r = computeLayout();
181666ea1b28a76aeba74744148b15099254d918671Owen Lin        final float hysteresis = 20F;
182666ea1b28a76aeba74744148b15099254d918671Owen Lin        int retval = GROW_NONE;
183666ea1b28a76aeba74744148b15099254d918671Owen Lin
184666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (mCircle) {
185666ea1b28a76aeba74744148b15099254d918671Owen Lin            float distX = x - r.centerX();
186666ea1b28a76aeba74744148b15099254d918671Owen Lin            float distY = y - r.centerY();
187666ea1b28a76aeba74744148b15099254d918671Owen Lin            int distanceFromCenter =
188666ea1b28a76aeba74744148b15099254d918671Owen Lin                    (int) Math.sqrt(distX * distX + distY * distY);
189666ea1b28a76aeba74744148b15099254d918671Owen Lin            int radius  = mDrawRect.width() / 2;
190666ea1b28a76aeba74744148b15099254d918671Owen Lin            int delta = distanceFromCenter - radius;
191666ea1b28a76aeba74744148b15099254d918671Owen Lin            if (Math.abs(delta) <= hysteresis) {
192666ea1b28a76aeba74744148b15099254d918671Owen Lin                if (Math.abs(distY) > Math.abs(distX)) {
193666ea1b28a76aeba74744148b15099254d918671Owen Lin                    if (distY < 0) {
194666ea1b28a76aeba74744148b15099254d918671Owen Lin                        retval = GROW_TOP_EDGE;
195666ea1b28a76aeba74744148b15099254d918671Owen Lin                    } else {
196666ea1b28a76aeba74744148b15099254d918671Owen Lin                        retval = GROW_BOTTOM_EDGE;
197666ea1b28a76aeba74744148b15099254d918671Owen Lin                    }
198666ea1b28a76aeba74744148b15099254d918671Owen Lin                } else {
199666ea1b28a76aeba74744148b15099254d918671Owen Lin                    if (distX < 0) {
200666ea1b28a76aeba74744148b15099254d918671Owen Lin                        retval = GROW_LEFT_EDGE;
201666ea1b28a76aeba74744148b15099254d918671Owen Lin                    } else {
202666ea1b28a76aeba74744148b15099254d918671Owen Lin                        retval = GROW_RIGHT_EDGE;
203666ea1b28a76aeba74744148b15099254d918671Owen Lin                    }
204666ea1b28a76aeba74744148b15099254d918671Owen Lin                }
205666ea1b28a76aeba74744148b15099254d918671Owen Lin            } else if (distanceFromCenter < radius) {
206666ea1b28a76aeba74744148b15099254d918671Owen Lin                retval = MOVE;
207666ea1b28a76aeba74744148b15099254d918671Owen Lin            } else {
208666ea1b28a76aeba74744148b15099254d918671Owen Lin                retval = GROW_NONE;
209666ea1b28a76aeba74744148b15099254d918671Owen Lin            }
210666ea1b28a76aeba74744148b15099254d918671Owen Lin        } else {
211666ea1b28a76aeba74744148b15099254d918671Owen Lin            // verticalCheck makes sure the position is between the top and
212666ea1b28a76aeba74744148b15099254d918671Owen Lin            // the bottom edge (with some tolerance). Similar for horizCheck.
213666ea1b28a76aeba74744148b15099254d918671Owen Lin            boolean verticalCheck = (y >= r.top - hysteresis)
214666ea1b28a76aeba74744148b15099254d918671Owen Lin                    && (y < r.bottom + hysteresis);
215666ea1b28a76aeba74744148b15099254d918671Owen Lin            boolean horizCheck = (x >= r.left - hysteresis)
216666ea1b28a76aeba74744148b15099254d918671Owen Lin                    && (x < r.right + hysteresis);
217666ea1b28a76aeba74744148b15099254d918671Owen Lin
218666ea1b28a76aeba74744148b15099254d918671Owen Lin            // Check whether the position is near some edge(s).
219666ea1b28a76aeba74744148b15099254d918671Owen Lin            if ((Math.abs(r.left - x)     < hysteresis)  &&  verticalCheck) {
220666ea1b28a76aeba74744148b15099254d918671Owen Lin                retval |= GROW_LEFT_EDGE;
221666ea1b28a76aeba74744148b15099254d918671Owen Lin            }
222666ea1b28a76aeba74744148b15099254d918671Owen Lin            if ((Math.abs(r.right - x)    < hysteresis)  &&  verticalCheck) {
223666ea1b28a76aeba74744148b15099254d918671Owen Lin                retval |= GROW_RIGHT_EDGE;
224666ea1b28a76aeba74744148b15099254d918671Owen Lin            }
225666ea1b28a76aeba74744148b15099254d918671Owen Lin            if ((Math.abs(r.top - y)      < hysteresis)  &&  horizCheck) {
226666ea1b28a76aeba74744148b15099254d918671Owen Lin                retval |= GROW_TOP_EDGE;
227666ea1b28a76aeba74744148b15099254d918671Owen Lin            }
228666ea1b28a76aeba74744148b15099254d918671Owen Lin            if ((Math.abs(r.bottom - y)   < hysteresis)  &&  horizCheck) {
229666ea1b28a76aeba74744148b15099254d918671Owen Lin                retval |= GROW_BOTTOM_EDGE;
230666ea1b28a76aeba74744148b15099254d918671Owen Lin            }
231666ea1b28a76aeba74744148b15099254d918671Owen Lin
232666ea1b28a76aeba74744148b15099254d918671Owen Lin            // Not near any edge but inside the rectangle: move.
233666ea1b28a76aeba74744148b15099254d918671Owen Lin            if (retval == GROW_NONE && r.contains((int) x, (int) y)) {
234666ea1b28a76aeba74744148b15099254d918671Owen Lin                retval = MOVE;
235666ea1b28a76aeba74744148b15099254d918671Owen Lin            }
236666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
237666ea1b28a76aeba74744148b15099254d918671Owen Lin        return retval;
238666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
239666ea1b28a76aeba74744148b15099254d918671Owen Lin
240666ea1b28a76aeba74744148b15099254d918671Owen Lin    // Handles motion (dx, dy) in screen space.
241666ea1b28a76aeba74744148b15099254d918671Owen Lin    // The "edge" parameter specifies which edges the user is dragging.
242666ea1b28a76aeba74744148b15099254d918671Owen Lin    void handleMotion(int edge, float dx, float dy) {
243666ea1b28a76aeba74744148b15099254d918671Owen Lin        Rect r = computeLayout();
244666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (edge == GROW_NONE) {
245666ea1b28a76aeba74744148b15099254d918671Owen Lin            return;
246666ea1b28a76aeba74744148b15099254d918671Owen Lin        } else if (edge == MOVE) {
247666ea1b28a76aeba74744148b15099254d918671Owen Lin            // Convert to image space before sending to moveBy().
248666ea1b28a76aeba74744148b15099254d918671Owen Lin            moveBy(dx * (mCropRect.width() / r.width()),
249666ea1b28a76aeba74744148b15099254d918671Owen Lin                   dy * (mCropRect.height() / r.height()));
250666ea1b28a76aeba74744148b15099254d918671Owen Lin        } else {
251666ea1b28a76aeba74744148b15099254d918671Owen Lin            if (((GROW_LEFT_EDGE | GROW_RIGHT_EDGE) & edge) == 0) {
252666ea1b28a76aeba74744148b15099254d918671Owen Lin                dx = 0;
253666ea1b28a76aeba74744148b15099254d918671Owen Lin            }
254666ea1b28a76aeba74744148b15099254d918671Owen Lin
255666ea1b28a76aeba74744148b15099254d918671Owen Lin            if (((GROW_TOP_EDGE | GROW_BOTTOM_EDGE) & edge) == 0) {
256666ea1b28a76aeba74744148b15099254d918671Owen Lin                dy = 0;
257666ea1b28a76aeba74744148b15099254d918671Owen Lin            }
258666ea1b28a76aeba74744148b15099254d918671Owen Lin
259666ea1b28a76aeba74744148b15099254d918671Owen Lin            // Convert to image space before sending to growBy().
260666ea1b28a76aeba74744148b15099254d918671Owen Lin            float xDelta = dx * (mCropRect.width() / r.width());
261666ea1b28a76aeba74744148b15099254d918671Owen Lin            float yDelta = dy * (mCropRect.height() / r.height());
262666ea1b28a76aeba74744148b15099254d918671Owen Lin            growBy((((edge & GROW_LEFT_EDGE) != 0) ? -1 : 1) * xDelta,
263666ea1b28a76aeba74744148b15099254d918671Owen Lin                    (((edge & GROW_TOP_EDGE) != 0) ? -1 : 1) * yDelta);
264666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
265666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
266666ea1b28a76aeba74744148b15099254d918671Owen Lin
267666ea1b28a76aeba74744148b15099254d918671Owen Lin    // Grows the cropping rectange by (dx, dy) in image space.
268666ea1b28a76aeba74744148b15099254d918671Owen Lin    void moveBy(float dx, float dy) {
269666ea1b28a76aeba74744148b15099254d918671Owen Lin        Rect invalRect = new Rect(mDrawRect);
270666ea1b28a76aeba74744148b15099254d918671Owen Lin
271666ea1b28a76aeba74744148b15099254d918671Owen Lin        mCropRect.offset(dx, dy);
272666ea1b28a76aeba74744148b15099254d918671Owen Lin
273666ea1b28a76aeba74744148b15099254d918671Owen Lin        // Put the cropping rectangle inside image rectangle.
274666ea1b28a76aeba74744148b15099254d918671Owen Lin        mCropRect.offset(
275666ea1b28a76aeba74744148b15099254d918671Owen Lin                Math.max(0, mImageRect.left - mCropRect.left),
276666ea1b28a76aeba74744148b15099254d918671Owen Lin                Math.max(0, mImageRect.top  - mCropRect.top));
277666ea1b28a76aeba74744148b15099254d918671Owen Lin
278666ea1b28a76aeba74744148b15099254d918671Owen Lin        mCropRect.offset(
279666ea1b28a76aeba74744148b15099254d918671Owen Lin                Math.min(0, mImageRect.right  - mCropRect.right),
280666ea1b28a76aeba74744148b15099254d918671Owen Lin                Math.min(0, mImageRect.bottom - mCropRect.bottom));
281666ea1b28a76aeba74744148b15099254d918671Owen Lin
282666ea1b28a76aeba74744148b15099254d918671Owen Lin        mDrawRect = computeLayout();
283666ea1b28a76aeba74744148b15099254d918671Owen Lin        invalRect.union(mDrawRect);
284666ea1b28a76aeba74744148b15099254d918671Owen Lin        invalRect.inset(-10, -10);
285666ea1b28a76aeba74744148b15099254d918671Owen Lin        mContext.invalidate(invalRect);
286666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
287666ea1b28a76aeba74744148b15099254d918671Owen Lin
288666ea1b28a76aeba74744148b15099254d918671Owen Lin    // Grows the cropping rectange by (dx, dy) in image space.
289666ea1b28a76aeba74744148b15099254d918671Owen Lin    void growBy(float dx, float dy) {
290666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (mMaintainAspectRatio) {
291666ea1b28a76aeba74744148b15099254d918671Owen Lin            if (dx != 0) {
292666ea1b28a76aeba74744148b15099254d918671Owen Lin                dy = dx / mInitialAspectRatio;
293666ea1b28a76aeba74744148b15099254d918671Owen Lin            } else if (dy != 0) {
294666ea1b28a76aeba74744148b15099254d918671Owen Lin                dx = dy * mInitialAspectRatio;
295666ea1b28a76aeba74744148b15099254d918671Owen Lin            }
296666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
297666ea1b28a76aeba74744148b15099254d918671Owen Lin
298666ea1b28a76aeba74744148b15099254d918671Owen Lin        // Don't let the cropping rectangle grow too fast.
299666ea1b28a76aeba74744148b15099254d918671Owen Lin        // Grow at most half of the difference between the image rectangle and
300666ea1b28a76aeba74744148b15099254d918671Owen Lin        // the cropping rectangle.
301666ea1b28a76aeba74744148b15099254d918671Owen Lin        RectF r = new RectF(mCropRect);
302666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (dx > 0F && r.width() + 2 * dx > mImageRect.width()) {
303666ea1b28a76aeba74744148b15099254d918671Owen Lin            float adjustment = (mImageRect.width() - r.width()) / 2F;
304666ea1b28a76aeba74744148b15099254d918671Owen Lin            dx = adjustment;
305666ea1b28a76aeba74744148b15099254d918671Owen Lin            if (mMaintainAspectRatio) {
306666ea1b28a76aeba74744148b15099254d918671Owen Lin                dy = dx / mInitialAspectRatio;
307666ea1b28a76aeba74744148b15099254d918671Owen Lin            }
308666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
309666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (dy > 0F && r.height() + 2 * dy > mImageRect.height()) {
310666ea1b28a76aeba74744148b15099254d918671Owen Lin            float adjustment = (mImageRect.height() - r.height()) / 2F;
311666ea1b28a76aeba74744148b15099254d918671Owen Lin            dy = adjustment;
312666ea1b28a76aeba74744148b15099254d918671Owen Lin            if (mMaintainAspectRatio) {
313666ea1b28a76aeba74744148b15099254d918671Owen Lin                dx = dy * mInitialAspectRatio;
314666ea1b28a76aeba74744148b15099254d918671Owen Lin            }
315666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
316666ea1b28a76aeba74744148b15099254d918671Owen Lin
317666ea1b28a76aeba74744148b15099254d918671Owen Lin        r.inset(-dx, -dy);
318666ea1b28a76aeba74744148b15099254d918671Owen Lin
319666ea1b28a76aeba74744148b15099254d918671Owen Lin        // Don't let the cropping rectangle shrink too fast.
320666ea1b28a76aeba74744148b15099254d918671Owen Lin        final float widthCap = 25F;
321666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (r.width() < widthCap) {
322666ea1b28a76aeba74744148b15099254d918671Owen Lin            r.inset(-(widthCap - r.width()) / 2F, 0F);
323666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
324666ea1b28a76aeba74744148b15099254d918671Owen Lin        float heightCap = mMaintainAspectRatio
325666ea1b28a76aeba74744148b15099254d918671Owen Lin                ? (widthCap / mInitialAspectRatio)
326666ea1b28a76aeba74744148b15099254d918671Owen Lin                : widthCap;
327666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (r.height() < heightCap) {
328666ea1b28a76aeba74744148b15099254d918671Owen Lin            r.inset(0F, -(heightCap - r.height()) / 2F);
329666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
330666ea1b28a76aeba74744148b15099254d918671Owen Lin
331666ea1b28a76aeba74744148b15099254d918671Owen Lin        // Put the cropping rectangle inside the image rectangle.
332666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (r.left < mImageRect.left) {
333666ea1b28a76aeba74744148b15099254d918671Owen Lin            r.offset(mImageRect.left - r.left, 0F);
334666ea1b28a76aeba74744148b15099254d918671Owen Lin        } else if (r.right > mImageRect.right) {
335666ea1b28a76aeba74744148b15099254d918671Owen Lin            r.offset(-(r.right - mImageRect.right), 0);
336666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
337666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (r.top < mImageRect.top) {
338666ea1b28a76aeba74744148b15099254d918671Owen Lin            r.offset(0F, mImageRect.top - r.top);
339666ea1b28a76aeba74744148b15099254d918671Owen Lin        } else if (r.bottom > mImageRect.bottom) {
340666ea1b28a76aeba74744148b15099254d918671Owen Lin            r.offset(0F, -(r.bottom - mImageRect.bottom));
341666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
342666ea1b28a76aeba74744148b15099254d918671Owen Lin
343666ea1b28a76aeba74744148b15099254d918671Owen Lin        mCropRect.set(r);
344666ea1b28a76aeba74744148b15099254d918671Owen Lin        mDrawRect = computeLayout();
345666ea1b28a76aeba74744148b15099254d918671Owen Lin        mContext.invalidate();
346666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
347666ea1b28a76aeba74744148b15099254d918671Owen Lin
348666ea1b28a76aeba74744148b15099254d918671Owen Lin    // Returns the cropping rectangle in image space.
349666ea1b28a76aeba74744148b15099254d918671Owen Lin    public Rect getCropRect() {
350666ea1b28a76aeba74744148b15099254d918671Owen Lin        return new Rect((int) mCropRect.left, (int) mCropRect.top,
351666ea1b28a76aeba74744148b15099254d918671Owen Lin                        (int) mCropRect.right, (int) mCropRect.bottom);
352666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
353666ea1b28a76aeba74744148b15099254d918671Owen Lin
354666ea1b28a76aeba74744148b15099254d918671Owen Lin    // Maps the cropping rectangle from image space to screen space.
355666ea1b28a76aeba74744148b15099254d918671Owen Lin    private Rect computeLayout() {
356666ea1b28a76aeba74744148b15099254d918671Owen Lin        RectF r = new RectF(mCropRect.left, mCropRect.top,
357666ea1b28a76aeba74744148b15099254d918671Owen Lin                            mCropRect.right, mCropRect.bottom);
358666ea1b28a76aeba74744148b15099254d918671Owen Lin        mMatrix.mapRect(r);
359666ea1b28a76aeba74744148b15099254d918671Owen Lin        return new Rect(Math.round(r.left), Math.round(r.top),
360666ea1b28a76aeba74744148b15099254d918671Owen Lin                        Math.round(r.right), Math.round(r.bottom));
361666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
362666ea1b28a76aeba74744148b15099254d918671Owen Lin
363666ea1b28a76aeba74744148b15099254d918671Owen Lin    public void invalidate() {
364666ea1b28a76aeba74744148b15099254d918671Owen Lin        mDrawRect = computeLayout();
365666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
366666ea1b28a76aeba74744148b15099254d918671Owen Lin
367666ea1b28a76aeba74744148b15099254d918671Owen Lin    public void setup(Matrix m, Rect imageRect, RectF cropRect, boolean circle,
368666ea1b28a76aeba74744148b15099254d918671Owen Lin                      boolean maintainAspectRatio) {
369666ea1b28a76aeba74744148b15099254d918671Owen Lin        if (circle) {
370666ea1b28a76aeba74744148b15099254d918671Owen Lin            maintainAspectRatio = true;
371666ea1b28a76aeba74744148b15099254d918671Owen Lin        }
372666ea1b28a76aeba74744148b15099254d918671Owen Lin        mMatrix = new Matrix(m);
373666ea1b28a76aeba74744148b15099254d918671Owen Lin
374666ea1b28a76aeba74744148b15099254d918671Owen Lin        mCropRect = cropRect;
375666ea1b28a76aeba74744148b15099254d918671Owen Lin        mImageRect = new RectF(imageRect);
376666ea1b28a76aeba74744148b15099254d918671Owen Lin        mMaintainAspectRatio = maintainAspectRatio;
377666ea1b28a76aeba74744148b15099254d918671Owen Lin        mCircle = circle;
378666ea1b28a76aeba74744148b15099254d918671Owen Lin
379666ea1b28a76aeba74744148b15099254d918671Owen Lin        mInitialAspectRatio = mCropRect.width() / mCropRect.height();
380666ea1b28a76aeba74744148b15099254d918671Owen Lin        mDrawRect = computeLayout();
381666ea1b28a76aeba74744148b15099254d918671Owen Lin
382666ea1b28a76aeba74744148b15099254d918671Owen Lin        mFocusPaint.setARGB(125, 50, 50, 50);
383666ea1b28a76aeba74744148b15099254d918671Owen Lin        mNoFocusPaint.setARGB(125, 50, 50, 50);
384666ea1b28a76aeba74744148b15099254d918671Owen Lin        mOutlinePaint.setStrokeWidth(3F);
385666ea1b28a76aeba74744148b15099254d918671Owen Lin        mOutlinePaint.setStyle(Paint.Style.STROKE);
386666ea1b28a76aeba74744148b15099254d918671Owen Lin        mOutlinePaint.setAntiAlias(true);
387666ea1b28a76aeba74744148b15099254d918671Owen Lin
388666ea1b28a76aeba74744148b15099254d918671Owen Lin        mMode = ModifyMode.None;
389666ea1b28a76aeba74744148b15099254d918671Owen Lin        init();
390666ea1b28a76aeba74744148b15099254d918671Owen Lin    }
391666ea1b28a76aeba74744148b15099254d918671Owen Lin
392666ea1b28a76aeba74744148b15099254d918671Owen Lin    enum ModifyMode { None, Move, Grow }
393666ea1b28a76aeba74744148b15099254d918671Owen Lin
394666ea1b28a76aeba74744148b15099254d918671Owen Lin    private ModifyMode mMode = ModifyMode.None;
395666ea1b28a76aeba74744148b15099254d918671Owen Lin
396666ea1b28a76aeba74744148b15099254d918671Owen Lin    Rect mDrawRect;  // in screen space
397666ea1b28a76aeba74744148b15099254d918671Owen Lin    private RectF mImageRect;  // in image space
398666ea1b28a76aeba74744148b15099254d918671Owen Lin    RectF mCropRect;  // in image space
399666ea1b28a76aeba74744148b15099254d918671Owen Lin    Matrix mMatrix;
400666ea1b28a76aeba74744148b15099254d918671Owen Lin
401666ea1b28a76aeba74744148b15099254d918671Owen Lin    private boolean mMaintainAspectRatio = false;
402666ea1b28a76aeba74744148b15099254d918671Owen Lin    private float mInitialAspectRatio;
403666ea1b28a76aeba74744148b15099254d918671Owen Lin    private boolean mCircle = false;
404666ea1b28a76aeba74744148b15099254d918671Owen Lin
405666ea1b28a76aeba74744148b15099254d918671Owen Lin    private Drawable mResizeDrawableWidth;
406666ea1b28a76aeba74744148b15099254d918671Owen Lin    private Drawable mResizeDrawableHeight;
407666ea1b28a76aeba74744148b15099254d918671Owen Lin    private Drawable mResizeDrawableDiagonal;
408666ea1b28a76aeba74744148b15099254d918671Owen Lin
409666ea1b28a76aeba74744148b15099254d918671Owen Lin    private final Paint mFocusPaint = new Paint();
410666ea1b28a76aeba74744148b15099254d918671Owen Lin    private final Paint mNoFocusPaint = new Paint();
411666ea1b28a76aeba74744148b15099254d918671Owen Lin    private final Paint mOutlinePaint = new Paint();
412666ea1b28a76aeba74744148b15099254d918671Owen Lin}
413