blob: 08cf158e1ef2fe57ed18e2fb22ea4c2d7e63dd4a [file] [log] [blame]
Ruben Brunk1a664452013-04-04 17:20:42 -07001/*
2 * Copyright (C) 2013 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.gallery3d.filtershow.crop;
18
19import android.graphics.Rect;
20import android.graphics.RectF;
21
22import com.android.gallery3d.filtershow.imageshow.GeometryMath;
23
24public class CropObject {
25
26 private BoundedRect mBoundedRect;
27 private float mAspectWidth = 1;
28 private float mAspectHeight = 1;
29 private boolean mFixAspectRatio = false;
30 private float mRotation = 0;
31 private float mTouchTolerance = 45;
32 private float mMinSideSize = 20;
33
34 public static final int MOVE_NONE = 0;
35 // Sides
36 public static final int MOVE_LEFT = 1;
37 public static final int MOVE_TOP = 2;
38 public static final int MOVE_RIGHT = 4;
39 public static final int MOVE_BOTTOM = 8;
40 public static final int MOVE_BLOCK = 16;
41
42 // Corners
43 public static final int TOP_LEFT = MOVE_TOP | MOVE_LEFT;
44 public static final int TOP_RIGHT = MOVE_TOP | MOVE_RIGHT;
45 public static final int BOTTOM_RIGHT = MOVE_BOTTOM | MOVE_RIGHT;
46 public static final int BOTTOM_LEFT = MOVE_BOTTOM | MOVE_LEFT;
47
48 private int mMovingEdges = MOVE_NONE;
49
50 public CropObject(Rect outerBound, Rect innerBound, int outerAngle) {
51 mBoundedRect = new BoundedRect(outerAngle % 360, outerBound, innerBound);
52 }
53
54 public CropObject(RectF outerBound, RectF innerBound, int outerAngle) {
55 mBoundedRect = new BoundedRect(outerAngle % 360, outerBound, innerBound);
56 }
57
Ruben Brunk966f2192013-04-11 12:39:03 -070058 public void resetBoundsTo(RectF inner, RectF outer) {
59 mBoundedRect.resetTo(0, outer, inner);
60 }
61
62 public void getInnerBounds(RectF r) {
Ruben Brunk1a664452013-04-04 17:20:42 -070063 mBoundedRect.setToInner(r);
64 }
65
Ruben Brunk966f2192013-04-11 12:39:03 -070066 public void getOuterBounds(RectF r) {
Ruben Brunk1a664452013-04-04 17:20:42 -070067 mBoundedRect.setToOuter(r);
68 }
69
70 public RectF getInnerBounds() {
71 return mBoundedRect.getInner();
72 }
73
74 public RectF getOuterBounds() {
75 return mBoundedRect.getOuter();
76 }
77
78 public int getSelectState() {
79 return mMovingEdges;
80 }
81
82 public boolean isFixedAspect() {
83 return mFixAspectRatio;
84 }
85
86 public void rotateOuter(int angle) {
87 mRotation = angle % 360;
88 mBoundedRect.setRotation(mRotation);
89 clearSelectState();
90 }
91
92 public boolean setInnerAspectRatio(int width, int height) {
93 if (width <= 0 || height <= 0) {
94 throw new IllegalArgumentException("Width and Height must be greater than zero");
95 }
96 RectF inner = mBoundedRect.getInner();
97 CropMath.fixAspectRatioContained(inner, width, height);
98 if (inner.width() < mMinSideSize || inner.height() < mMinSideSize) {
99 return false;
100 }
101 mAspectWidth = width;
102 mAspectHeight = height;
103 mFixAspectRatio = true;
104 mBoundedRect.setInner(inner);
105 clearSelectState();
106 return true;
107 }
108
109 public void setTouchTolerance(float tolerance) {
110 if (tolerance <= 0) {
111 throw new IllegalArgumentException("Tolerance must be greater than zero");
112 }
113 mTouchTolerance = tolerance;
114 }
115
116 public void setMinInnerSideSize(float minSide) {
117 if (minSide <= 0) {
118 throw new IllegalArgumentException("Min dide must be greater than zero");
119 }
120 mMinSideSize = minSide;
121 }
122
123 public void unsetAspectRatio() {
124 mFixAspectRatio = false;
125 clearSelectState();
126 }
127
128 public boolean hasSelectedEdge() {
129 return mMovingEdges != MOVE_NONE;
130 }
131
132 public static boolean checkCorner(int selected) {
133 return selected == TOP_LEFT || selected == TOP_RIGHT || selected == BOTTOM_RIGHT
134 || selected == BOTTOM_LEFT;
135 }
136
137 public static boolean checkEdge(int selected) {
138 return selected == MOVE_LEFT || selected == MOVE_TOP || selected == MOVE_RIGHT
139 || selected == MOVE_BOTTOM;
140 }
141
142 public static boolean checkBlock(int selected) {
143 return selected == MOVE_BLOCK;
144 }
145
146 public static boolean checkValid(int selected) {
147 return selected == MOVE_NONE || checkBlock(selected) || checkEdge(selected)
148 || checkCorner(selected);
149 }
150
151 public void clearSelectState() {
152 mMovingEdges = MOVE_NONE;
153 }
154
155 public int wouldSelectEdge(float x, float y) {
156 int edgeSelected = calculateSelectedEdge(x, y);
157 if (edgeSelected != MOVE_NONE && edgeSelected != MOVE_BLOCK) {
158 return edgeSelected;
159 }
160 return MOVE_NONE;
161 }
162
163 public boolean selectEdge(int edge) {
164 if (!checkValid(edge)) {
165 // temporary
166 throw new IllegalArgumentException("bad edge selected");
167 // return false;
168 }
169 if ((mFixAspectRatio && !checkCorner(edge)) && !checkBlock(edge)) {
170 // temporary
171 throw new IllegalArgumentException("bad corner selected");
172 // return false;
173 }
174 mMovingEdges = edge;
175 return true;
176 }
177
178 public boolean selectEdge(float x, float y) {
179 int edgeSelected = calculateSelectedEdge(x, y);
180 if (mFixAspectRatio) {
181 edgeSelected = fixEdgeToCorner(edgeSelected);
182 }
183 if (edgeSelected == MOVE_NONE) {
184 return false;
185 }
186 return selectEdge(edgeSelected);
187 }
188
189 public boolean moveCurrentSelection(float dX, float dY) {
190 if (mMovingEdges == MOVE_NONE) {
191 return false;
192 }
193 RectF crop = mBoundedRect.getInner();
194
195 float minWidthHeight = mMinSideSize;
196
197 int movingEdges = mMovingEdges;
198 if (movingEdges == MOVE_BLOCK) {
199 mBoundedRect.moveInner(dX, dY);
200 return true;
201 } else {
202 float dx = 0;
203 float dy = 0;
204
205 if ((movingEdges & MOVE_LEFT) != 0) {
206 dx = Math.min(crop.left + dX, crop.right - minWidthHeight) - crop.left;
207 }
208 if ((movingEdges & MOVE_TOP) != 0) {
209 dy = Math.min(crop.top + dY, crop.bottom - minWidthHeight) - crop.top;
210 }
211 if ((movingEdges & MOVE_RIGHT) != 0) {
212 dx = Math.max(crop.right + dX, crop.left + minWidthHeight)
213 - crop.right;
214 }
215 if ((movingEdges & MOVE_BOTTOM) != 0) {
216 dy = Math.max(crop.bottom + dY, crop.top + minWidthHeight)
217 - crop.bottom;
218 }
219
220 if (mFixAspectRatio) {
221 float[] l1 = {
222 crop.left, crop.bottom
223 };
224 float[] l2 = {
225 crop.right, crop.top
226 };
227 if (movingEdges == TOP_LEFT || movingEdges == BOTTOM_RIGHT) {
228 l1[1] = crop.top;
229 l2[1] = crop.bottom;
230 }
231 float[] b = {
232 l1[0] - l2[0], l1[1] - l2[1]
233 };
234 float[] disp = {
235 dx, dy
236 };
237 float[] bUnit = GeometryMath.normalize(b);
238 float sp = GeometryMath.scalarProjection(disp, bUnit);
239 dx = sp * bUnit[0];
240 dy = sp * bUnit[1];
241 RectF newCrop = fixedCornerResize(crop, movingEdges, dx, dy);
242
243 mBoundedRect.fixedAspectResizeInner(newCrop);
244 } else {
245 if ((movingEdges & MOVE_LEFT) != 0) {
246 crop.left += dx;
247 }
248 if ((movingEdges & MOVE_TOP) != 0) {
249 crop.top += dy;
250 }
251 if ((movingEdges & MOVE_RIGHT) != 0) {
252 crop.right += dx;
253 }
254 if ((movingEdges & MOVE_BOTTOM) != 0) {
255 crop.bottom += dy;
256 }
257 mBoundedRect.resizeInner(crop);
258 }
259 }
260 return true;
261 }
262
263 // Helper methods
264
265 private int calculateSelectedEdge(float x, float y) {
266 RectF cropped = mBoundedRect.getInner();
267
268 float left = Math.abs(x - cropped.left);
269 float right = Math.abs(x - cropped.right);
270 float top = Math.abs(y - cropped.top);
271 float bottom = Math.abs(y - cropped.bottom);
272
273 int edgeSelected = MOVE_NONE;
274 // Check left or right.
275 if ((left <= mTouchTolerance) && ((y + mTouchTolerance) >= cropped.top)
276 && ((y - mTouchTolerance) <= cropped.bottom) && (left < right)) {
277 edgeSelected |= MOVE_LEFT;
278 }
279 else if ((right <= mTouchTolerance) && ((y + mTouchTolerance) >= cropped.top)
280 && ((y - mTouchTolerance) <= cropped.bottom)) {
281 edgeSelected |= MOVE_RIGHT;
282 }
283
284 // Check top or bottom.
285 if ((top <= mTouchTolerance) && ((x + mTouchTolerance) >= cropped.left)
286 && ((x - mTouchTolerance) <= cropped.right) && (top < bottom)) {
287 edgeSelected |= MOVE_TOP;
288 }
289 else if ((bottom <= mTouchTolerance) && ((x + mTouchTolerance) >= cropped.left)
290 && ((x - mTouchTolerance) <= cropped.right)) {
291 edgeSelected |= MOVE_BOTTOM;
292 }
293 return edgeSelected;
294 }
295
296 private static RectF fixedCornerResize(RectF r, int moving_corner, float dx, float dy) {
297 RectF newCrop = null;
298 // Fix opposite corner in place and move sides
299 if (moving_corner == BOTTOM_RIGHT) {
300 newCrop = new RectF(r.left, r.top, r.left + r.width() + dx, r.top + r.height()
301 + dy);
302 } else if (moving_corner == BOTTOM_LEFT) {
303 newCrop = new RectF(r.right - r.width() + dx, r.top, r.right, r.top + r.height()
304 + dy);
305 } else if (moving_corner == TOP_LEFT) {
306 newCrop = new RectF(r.right - r.width() + dx, r.bottom - r.height() + dy,
307 r.right, r.bottom);
308 } else if (moving_corner == TOP_RIGHT) {
309 newCrop = new RectF(r.left, r.bottom - r.height() + dy, r.left
310 + r.width() + dx, r.bottom);
311 }
312 return newCrop;
313 }
314
315 private static int fixEdgeToCorner(int moving_edges) {
316 if (moving_edges == MOVE_LEFT) {
317 moving_edges |= MOVE_TOP;
318 }
319 if (moving_edges == MOVE_TOP) {
320 moving_edges |= MOVE_LEFT;
321 }
322 if (moving_edges == MOVE_RIGHT) {
323 moving_edges |= MOVE_BOTTOM;
324 }
325 if (moving_edges == MOVE_BOTTOM) {
326 moving_edges |= MOVE_RIGHT;
327 }
328 return moving_edges;
329 }
330
331}