blob: bea3ffabd8f1cadc1bdd74183024652fa2409c5b [file] [log] [blame]
/*
* 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.gallery3d.filtershow.crop;
import android.graphics.Rect;
import android.graphics.RectF;
import com.android.gallery3d.filtershow.imageshow.GeometryMath;
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 = GeometryMath.normalize(b);
float sp = GeometryMath.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;
}
}