diff options
Diffstat (limited to 'src/com/android/camera/crop/CropObject.java')
-rw-r--r-- | src/com/android/camera/crop/CropObject.java | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/src/com/android/camera/crop/CropObject.java b/src/com/android/camera/crop/CropObject.java new file mode 100644 index 000000000..4a566b3ef --- /dev/null +++ b/src/com/android/camera/crop/CropObject.java @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.crop; + +import android.graphics.Rect; +import android.graphics.RectF; + +public class CropObject { + private BoundedRect mBoundedRect; + private float mAspectWidth = 1; + private float mAspectHeight = 1; + private boolean mFixAspectRatio = false; + private float mRotation = 0; + private float mTouchTolerance = 45; + private float mMinSideSize = 20; + + public static final int MOVE_NONE = 0; + // Sides + public static final int MOVE_LEFT = 1; + public static final int MOVE_TOP = 2; + public static final int MOVE_RIGHT = 4; + public static final int MOVE_BOTTOM = 8; + public static final int MOVE_BLOCK = 16; + + // Corners + public static final int TOP_LEFT = MOVE_TOP | MOVE_LEFT; + public static final int TOP_RIGHT = MOVE_TOP | MOVE_RIGHT; + public static final int BOTTOM_RIGHT = MOVE_BOTTOM | MOVE_RIGHT; + public static final int BOTTOM_LEFT = MOVE_BOTTOM | MOVE_LEFT; + + private int mMovingEdges = MOVE_NONE; + + public CropObject(Rect outerBound, Rect innerBound, int outerAngle) { + mBoundedRect = new BoundedRect(outerAngle % 360, outerBound, innerBound); + } + + public CropObject(RectF outerBound, RectF innerBound, int outerAngle) { + mBoundedRect = new BoundedRect(outerAngle % 360, outerBound, innerBound); + } + + public void resetBoundsTo(RectF inner, RectF outer) { + mBoundedRect.resetTo(0, outer, inner); + } + + public void getInnerBounds(RectF r) { + mBoundedRect.setToInner(r); + } + + public void getOuterBounds(RectF r) { + mBoundedRect.setToOuter(r); + } + + public RectF getInnerBounds() { + return mBoundedRect.getInner(); + } + + public RectF getOuterBounds() { + return mBoundedRect.getOuter(); + } + + public int getSelectState() { + return mMovingEdges; + } + + public boolean isFixedAspect() { + return mFixAspectRatio; + } + + public void rotateOuter(int angle) { + mRotation = angle % 360; + mBoundedRect.setRotation(mRotation); + clearSelectState(); + } + + public boolean setInnerAspectRatio(float width, float height) { + if (width <= 0 || height <= 0) { + throw new IllegalArgumentException("Width and Height must be greater than zero"); + } + RectF inner = mBoundedRect.getInner(); + CropMath.fixAspectRatioContained(inner, width, height); + if (inner.width() < mMinSideSize || inner.height() < mMinSideSize) { + return false; + } + mAspectWidth = width; + mAspectHeight = height; + mFixAspectRatio = true; + mBoundedRect.setInner(inner); + clearSelectState(); + return true; + } + + public void setTouchTolerance(float tolerance) { + if (tolerance <= 0) { + throw new IllegalArgumentException("Tolerance must be greater than zero"); + } + mTouchTolerance = tolerance; + } + + public void setMinInnerSideSize(float minSide) { + if (minSide <= 0) { + throw new IllegalArgumentException("Min dide must be greater than zero"); + } + mMinSideSize = minSide; + } + + public void unsetAspectRatio() { + mFixAspectRatio = false; + clearSelectState(); + } + + public boolean hasSelectedEdge() { + return mMovingEdges != MOVE_NONE; + } + + public static boolean checkCorner(int selected) { + return selected == TOP_LEFT || selected == TOP_RIGHT || selected == BOTTOM_RIGHT + || selected == BOTTOM_LEFT; + } + + public static boolean checkEdge(int selected) { + return selected == MOVE_LEFT || selected == MOVE_TOP || selected == MOVE_RIGHT + || selected == MOVE_BOTTOM; + } + + public static boolean checkBlock(int selected) { + return selected == MOVE_BLOCK; + } + + public static boolean checkValid(int selected) { + return selected == MOVE_NONE || checkBlock(selected) || checkEdge(selected) + || checkCorner(selected); + } + + public void clearSelectState() { + mMovingEdges = MOVE_NONE; + } + + public int wouldSelectEdge(float x, float y) { + int edgeSelected = calculateSelectedEdge(x, y); + if (edgeSelected != MOVE_NONE && edgeSelected != MOVE_BLOCK) { + return edgeSelected; + } + return MOVE_NONE; + } + + public boolean selectEdge(int edge) { + if (!checkValid(edge)) { + // temporary + throw new IllegalArgumentException("bad edge selected"); + // return false; + } + if ((mFixAspectRatio && !checkCorner(edge)) && !checkBlock(edge) && edge != MOVE_NONE) { + // temporary + throw new IllegalArgumentException("bad corner selected"); + // return false; + } + mMovingEdges = edge; + return true; + } + + public boolean selectEdge(float x, float y) { + int edgeSelected = calculateSelectedEdge(x, y); + if (mFixAspectRatio) { + edgeSelected = fixEdgeToCorner(edgeSelected); + } + if (edgeSelected == MOVE_NONE) { + return false; + } + return selectEdge(edgeSelected); + } + + public boolean moveCurrentSelection(float dX, float dY) { + if (mMovingEdges == MOVE_NONE) { + return false; + } + RectF crop = mBoundedRect.getInner(); + + float minWidthHeight = mMinSideSize; + + int movingEdges = mMovingEdges; + if (movingEdges == MOVE_BLOCK) { + mBoundedRect.moveInner(dX, dY); + return true; + } else { + float dx = 0; + float dy = 0; + + if ((movingEdges & MOVE_LEFT) != 0) { + dx = Math.min(crop.left + dX, crop.right - minWidthHeight) - crop.left; + } + if ((movingEdges & MOVE_TOP) != 0) { + dy = Math.min(crop.top + dY, crop.bottom - minWidthHeight) - crop.top; + } + if ((movingEdges & MOVE_RIGHT) != 0) { + dx = Math.max(crop.right + dX, crop.left + minWidthHeight) + - crop.right; + } + if ((movingEdges & MOVE_BOTTOM) != 0) { + dy = Math.max(crop.bottom + dY, crop.top + minWidthHeight) + - crop.bottom; + } + + if (mFixAspectRatio) { + float[] l1 = { + crop.left, crop.bottom + }; + float[] l2 = { + crop.right, crop.top + }; + if (movingEdges == TOP_LEFT || movingEdges == BOTTOM_RIGHT) { + l1[1] = crop.top; + l2[1] = crop.bottom; + } + float[] b = { + l1[0] - l2[0], l1[1] - l2[1] + }; + float[] disp = { + dx, dy + }; + float[] bUnit = GeometryMathUtils.normalize(b); + float sp = GeometryMathUtils.scalarProjection(disp, bUnit); + dx = sp * bUnit[0]; + dy = sp * bUnit[1]; + RectF newCrop = fixedCornerResize(crop, movingEdges, dx, dy); + + mBoundedRect.fixedAspectResizeInner(newCrop); + } else { + if ((movingEdges & MOVE_LEFT) != 0) { + crop.left += dx; + } + if ((movingEdges & MOVE_TOP) != 0) { + crop.top += dy; + } + if ((movingEdges & MOVE_RIGHT) != 0) { + crop.right += dx; + } + if ((movingEdges & MOVE_BOTTOM) != 0) { + crop.bottom += dy; + } + mBoundedRect.resizeInner(crop); + } + } + return true; + } + + // Helper methods + + private int calculateSelectedEdge(float x, float y) { + RectF cropped = mBoundedRect.getInner(); + + float left = Math.abs(x - cropped.left); + float right = Math.abs(x - cropped.right); + float top = Math.abs(y - cropped.top); + float bottom = Math.abs(y - cropped.bottom); + + int edgeSelected = MOVE_NONE; + // Check left or right. + if ((left <= mTouchTolerance) && ((y + mTouchTolerance) >= cropped.top) + && ((y - mTouchTolerance) <= cropped.bottom) && (left < right)) { + edgeSelected |= MOVE_LEFT; + } + else if ((right <= mTouchTolerance) && ((y + mTouchTolerance) >= cropped.top) + && ((y - mTouchTolerance) <= cropped.bottom)) { + edgeSelected |= MOVE_RIGHT; + } + + // Check top or bottom. + if ((top <= mTouchTolerance) && ((x + mTouchTolerance) >= cropped.left) + && ((x - mTouchTolerance) <= cropped.right) && (top < bottom)) { + edgeSelected |= MOVE_TOP; + } + else if ((bottom <= mTouchTolerance) && ((x + mTouchTolerance) >= cropped.left) + && ((x - mTouchTolerance) <= cropped.right)) { + edgeSelected |= MOVE_BOTTOM; + } + return edgeSelected; + } + + private static RectF fixedCornerResize(RectF r, int moving_corner, float dx, float dy) { + RectF newCrop = null; + // Fix opposite corner in place and move sides + if (moving_corner == BOTTOM_RIGHT) { + newCrop = new RectF(r.left, r.top, r.left + r.width() + dx, r.top + r.height() + + dy); + } else if (moving_corner == BOTTOM_LEFT) { + newCrop = new RectF(r.right - r.width() + dx, r.top, r.right, r.top + r.height() + + dy); + } else if (moving_corner == TOP_LEFT) { + newCrop = new RectF(r.right - r.width() + dx, r.bottom - r.height() + dy, + r.right, r.bottom); + } else if (moving_corner == TOP_RIGHT) { + newCrop = new RectF(r.left, r.bottom - r.height() + dy, r.left + + r.width() + dx, r.bottom); + } + return newCrop; + } + + private static int fixEdgeToCorner(int moving_edges) { + if (moving_edges == MOVE_LEFT) { + moving_edges |= MOVE_TOP; + } + if (moving_edges == MOVE_TOP) { + moving_edges |= MOVE_LEFT; + } + if (moving_edges == MOVE_RIGHT) { + moving_edges |= MOVE_BOTTOM; + } + if (moving_edges == MOVE_BOTTOM) { + moving_edges |= MOVE_RIGHT; + } + return moving_edges; + } + +} |