summaryrefslogtreecommitdiffstats
path: root/src/com/android/gallery3d/filtershow/imageshow
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/gallery3d/filtershow/imageshow')
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/BoundedRect.java340
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/CropMath.java191
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java76
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java98
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java731
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java206
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageRedEyes.java148
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageShow.java20
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java18
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java5
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageWithIcon.java50
11 files changed, 1188 insertions, 695 deletions
diff --git a/src/com/android/gallery3d/filtershow/imageshow/BoundedRect.java b/src/com/android/gallery3d/filtershow/imageshow/BoundedRect.java
new file mode 100644
index 000000000..e94d1ed9e
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/imageshow/BoundedRect.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2012 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.imageshow;
+
+import android.graphics.Matrix;
+import android.graphics.RectF;
+
+import java.util.Arrays;
+
+/**
+ * Maintains invariant that inner rectangle is constrained to be within the
+ * outer, rotated rectangle.
+ */
+public class BoundedRect {
+ private float rot;
+ private RectF outer;
+ private RectF inner;
+ private float[] innerRotated;
+
+ public BoundedRect() {
+ rot = 0;
+ outer = new RectF();
+ inner = new RectF();
+ innerRotated = new float[8];
+ }
+
+ public BoundedRect(float rotation, RectF outerRect, RectF innerRect) {
+ rot = rotation;
+ outer = new RectF(outerRect);
+ inner = new RectF(innerRect);
+ innerRotated = CropMath.getCornersFromRect(inner);
+ rotateInner();
+ if (!isConstrained())
+ reconstrain();
+ }
+
+ /**
+ * Sets inner, and re-constrains it to fit within the rotated bounding rect.
+ */
+ public void setInner(RectF newInner) {
+ if (inner.equals(newInner))
+ return;
+ inner = newInner;
+ innerRotated = CropMath.getCornersFromRect(inner);
+ rotateInner();
+ if (!isConstrained())
+ reconstrain();
+ }
+
+ /**
+ * Sets rotation, and re-constrains inner to fit within the rotated bounding rect.
+ */
+ public void setRotation(float rotation) {
+ if (rotation == rot)
+ return;
+ rot = rotation;
+ innerRotated = CropMath.getCornersFromRect(inner);
+ rotateInner();
+ if (!isConstrained())
+ reconstrain();
+ }
+
+ public RectF getInner() {
+ return new RectF(inner);
+ }
+
+ /**
+ * Tries to move the inner rectangle by (dx, dy). If this would cause it to leave
+ * the bounding rectangle, snaps the inner rectangle to the edge of the bounding
+ * rectangle.
+ */
+ public void moveInner(float dx, float dy) {
+ Matrix m0 = getInverseRotMatrix();
+
+ RectF translatedInner = new RectF(inner);
+ translatedInner.offset(dx, dy);
+
+ float[] translatedInnerCorners = CropMath.getCornersFromRect(translatedInner);
+ float[] outerCorners = CropMath.getCornersFromRect(outer);
+
+ m0.mapPoints(translatedInnerCorners);
+ float[] correction = {
+ 0, 0
+ };
+
+ // find correction vectors for corners that have moved out of bounds
+ for (int i = 0; i < translatedInnerCorners.length; i += 2) {
+ float correctedInnerX = translatedInnerCorners[i] + correction[0];
+ float correctedInnerY = translatedInnerCorners[i + 1] + correction[1];
+ if (!CropMath.inclusiveContains(outer, correctedInnerX, correctedInnerY)) {
+ float[] badCorner = {
+ correctedInnerX, correctedInnerY
+ };
+ float[] nearestSide = CropMath.closestSide(badCorner, outerCorners);
+ float[] correctionVec =
+ GeometryMath.shortestVectorFromPointToLine(badCorner, nearestSide);
+ correction[0] += correctionVec[0];
+ correction[1] += correctionVec[1];
+ }
+ }
+
+ for (int i = 0; i < translatedInnerCorners.length; i += 2) {
+ float correctedInnerX = translatedInnerCorners[i] + correction[0];
+ float correctedInnerY = translatedInnerCorners[i + 1] + correction[1];
+ if (!CropMath.inclusiveContains(outer, correctedInnerX, correctedInnerY)) {
+ float[] correctionVec = {
+ correctedInnerX, correctedInnerY
+ };
+ CropMath.getEdgePoints(outer, correctionVec);
+ correctionVec[0] -= correctedInnerX;
+ correctionVec[1] -= correctedInnerY;
+ correction[0] += correctionVec[0];
+ correction[1] += correctionVec[1];
+ }
+ }
+
+ // Set correction
+ for (int i = 0; i < translatedInnerCorners.length; i += 2) {
+ float correctedInnerX = translatedInnerCorners[i] + correction[0];
+ float correctedInnerY = translatedInnerCorners[i + 1] + correction[1];
+ // update translated corners with correction vectors
+ translatedInnerCorners[i] = correctedInnerX;
+ translatedInnerCorners[i + 1] = correctedInnerY;
+ }
+
+ innerRotated = translatedInnerCorners;
+ // reconstrain to update inner
+ reconstrain();
+ }
+
+ /**
+ * Attempts to resize the inner rectangle. If this would cause it to leave
+ * the bounding rect, clips the inner rectangle to fit.
+ */
+ public void resizeInner(RectF newInner) {
+ Matrix m = getRotMatrix();
+ Matrix m0 = getInverseRotMatrix();
+
+ float[] outerCorners = CropMath.getCornersFromRect(outer);
+ m.mapPoints(outerCorners);
+ float[] oldInnerCorners = CropMath.getCornersFromRect(inner);
+ float[] newInnerCorners = CropMath.getCornersFromRect(newInner);
+ RectF ret = new RectF(newInner);
+
+ for (int i = 0; i < newInnerCorners.length; i += 2) {
+ float[] c = {
+ newInnerCorners[i], newInnerCorners[i + 1]
+ };
+ float[] c0 = Arrays.copyOf(c, 2);
+ m0.mapPoints(c0);
+ if (!CropMath.inclusiveContains(outer, c0[0], c0[1])) {
+ float[] outerSide = CropMath.closestSide(c, outerCorners);
+ float[] pathOfCorner = {
+ newInnerCorners[i], newInnerCorners[i + 1],
+ oldInnerCorners[i], oldInnerCorners[i + 1]
+ };
+ float[] p = GeometryMath.lineIntersect(pathOfCorner, outerSide);
+ if (p == null) {
+ // lines are parallel or not well defined, so don't resize
+ p = new float[2];
+ p[0] = oldInnerCorners[i];
+ p[1] = oldInnerCorners[i + 1];
+ }
+ // relies on corners being in same order as method
+ // getCornersFromRect
+ switch (i) {
+ case 0:
+ case 1:
+ ret.left = (p[0] > ret.left) ? p[0] : ret.left;
+ ret.top = (p[1] > ret.top) ? p[1] : ret.top;
+ break;
+ case 2:
+ case 3:
+ ret.right = (p[0] < ret.right) ? p[0] : ret.right;
+ ret.top = (p[1] > ret.top) ? p[1] : ret.top;
+ break;
+ case 4:
+ case 5:
+ ret.right = (p[0] < ret.right) ? p[0] : ret.right;
+ ret.bottom = (p[1] < ret.bottom) ? p[1] : ret.bottom;
+ break;
+ case 6:
+ case 7:
+ ret.left = (p[0] > ret.left) ? p[0] : ret.left;
+ ret.bottom = (p[1] < ret.bottom) ? p[1] : ret.bottom;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ float[] retCorners = CropMath.getCornersFromRect(ret);
+ m0.mapPoints(retCorners);
+ innerRotated = retCorners;
+ // reconstrain to update inner
+ reconstrain();
+ }
+
+ /**
+ * Attempts to resize the inner rectangle. If this would cause it to leave
+ * the bounding rect, clips the inner rectangle to fit while maintaining
+ * aspect ratio.
+ */
+ public void fixedAspectResizeInner(RectF newInner) {
+ Matrix m = getRotMatrix();
+ Matrix m0 = getInverseRotMatrix();
+
+ float aspectW = inner.width();
+ float aspectH = inner.height();
+ float aspRatio = aspectW / aspectH;
+ float[] corners = CropMath.getCornersFromRect(outer);
+
+ m.mapPoints(corners);
+ float[] oldInnerCorners = CropMath.getCornersFromRect(inner);
+ float[] newInnerCorners = CropMath.getCornersFromRect(newInner);
+
+ // find fixed corner
+ int fixed = -1;
+ if (inner.top == newInner.top) {
+ if (inner.left == newInner.left)
+ fixed = 0; // top left
+ else if (inner.right == newInner.right)
+ fixed = 2; // top right
+ } else if (inner.bottom == newInner.bottom) {
+ if (inner.right == newInner.right)
+ fixed = 4; // bottom right
+ else if (inner.left == newInner.left)
+ fixed = 6; // bottom left
+ }
+ // no fixed corner, return without update
+ if (fixed == -1)
+ return;
+ float widthSoFar = newInner.width();
+ int moved = -1;
+ for (int i = 0; i < newInnerCorners.length; i += 2) {
+ float[] c = {
+ newInnerCorners[i], newInnerCorners[i + 1]
+ };
+ float[] c0 = Arrays.copyOf(c, 2);
+ m0.mapPoints(c0);
+ if (!CropMath.inclusiveContains(outer, c0[0], c0[1])) {
+ moved = i;
+ if (moved == fixed)
+ continue;
+ float[] l2 = CropMath.closestSide(c, corners);
+ float[] l1 = {
+ newInnerCorners[i], newInnerCorners[i + 1],
+ oldInnerCorners[i], oldInnerCorners[i + 1]
+ };
+ float[] p = GeometryMath.lineIntersect(l1, l2);
+ if (p == null) {
+ // lines are parallel or not well defined, so set to old
+ // corner
+ p = new float[2];
+ p[0] = oldInnerCorners[i];
+ p[1] = oldInnerCorners[i + 1];
+ }
+ // relies on corners being in same order as method
+ // getCornersFromRect
+ float fixed_x = oldInnerCorners[fixed];
+ float fixed_y = oldInnerCorners[fixed + 1];
+ float newWidth = Math.abs(fixed_x - p[0]);
+ float newHeight = Math.abs(fixed_y - p[1]);
+ newWidth = Math.max(newWidth, aspRatio * newHeight);
+ if (newWidth < widthSoFar)
+ widthSoFar = newWidth;
+ }
+ }
+
+ float heightSoFar = widthSoFar / aspRatio;
+ RectF ret = new RectF(inner);
+ if (fixed == 0) {
+ ret.right = ret.left + widthSoFar;
+ ret.bottom = ret.top + heightSoFar;
+ } else if (fixed == 2) {
+ ret.left = ret.right - widthSoFar;
+ ret.bottom = ret.top + heightSoFar;
+ } else if (fixed == 4) {
+ ret.left = ret.right - widthSoFar;
+ ret.top = ret.bottom - heightSoFar;
+ } else if (fixed == 6) {
+ ret.right = ret.left + widthSoFar;
+ ret.top = ret.bottom - heightSoFar;
+ }
+ float[] retCorners = CropMath.getCornersFromRect(ret);
+ m0.mapPoints(retCorners);
+ innerRotated = retCorners;
+ // reconstrain to update inner
+ reconstrain();
+ }
+
+ // internal methods
+
+ private boolean isConstrained() {
+ for (int i = 0; i < 8; i += 2) {
+ if (!CropMath.inclusiveContains(outer, innerRotated[i], innerRotated[i + 1]))
+ return false;
+ }
+ return true;
+ }
+
+ private void reconstrain() {
+ // innerRotated has been changed to have incorrect values
+ CropMath.getEdgePoints(outer, innerRotated);
+ Matrix m = getRotMatrix();
+ float[] unrotated = Arrays.copyOf(innerRotated, 8);
+ m.mapPoints(unrotated);
+ inner = CropMath.trapToRect(unrotated);
+ }
+
+ private void rotateInner() {
+ Matrix m = getInverseRotMatrix();
+ m.mapPoints(innerRotated);
+ }
+
+ private Matrix getRotMatrix() {
+ Matrix m = new Matrix();
+ m.setRotate(rot, outer.centerX(), outer.centerY());
+ return m;
+ }
+
+ private Matrix getInverseRotMatrix() {
+ Matrix m = new Matrix();
+ m.setRotate(-rot, outer.centerX(), outer.centerY());
+ return m;
+ }
+}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/CropMath.java b/src/com/android/gallery3d/filtershow/imageshow/CropMath.java
new file mode 100644
index 000000000..9037ca043
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/imageshow/CropMath.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2012 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.imageshow;
+
+import android.graphics.Matrix;
+import android.graphics.RectF;
+
+import java.util.Arrays;
+
+public class CropMath {
+
+ /**
+ * Gets a float array of the 2D coordinates representing a rectangles
+ * corners.
+ * The order of the corners in the float array is:
+ * 0------->1
+ * ^ |
+ * | v
+ * 3<-------2
+ *
+ * @param r the rectangle to get the corners of
+ * @return the float array of corners (8 floats)
+ */
+
+ public static float[] getCornersFromRect(RectF r) {
+ float[] corners = {
+ r.left, r.top,
+ r.right, r.top,
+ r.right, r.bottom,
+ r.left, r.bottom
+ };
+ return corners;
+ }
+
+ /**
+ * Returns true iff point (x, y) is within or on the rectangle's bounds.
+ * RectF's "contains" function treats points on the bottom and right bound
+ * as not being contained.
+ *
+ * @param r the rectangle
+ * @param x the x value of the point
+ * @param y the y value of the point
+ * @return
+ */
+ public static boolean inclusiveContains(RectF r, float x, float y) {
+ return !(x > r.right || x < r.left || y > r.bottom || y < r.top);
+ }
+
+ /**
+ * Takes an array of 2D coordinates representing corners and returns the
+ * smallest rectangle containing those coordinates.
+ *
+ * @param array array of 2D coordinates
+ * @return smallest rectangle containing coordinates
+ */
+ public static RectF trapToRect(float[] array) {
+ RectF r = new RectF(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY,
+ Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
+ for (int i = 1; i < array.length; i += 2) {
+ float x = array[i - 1];
+ float y = array[i];
+ r.left = (x < r.left) ? x : r.left;
+ r.top = (y < r.top) ? y : r.top;
+ r.right = (x > r.right) ? x : r.right;
+ r.bottom = (y > r.bottom) ? y : r.bottom;
+ }
+ r.sort();
+ return r;
+ }
+
+ /**
+ * If edge point [x, y] in array [x0, y0, x1, y1, ...] is outside of the
+ * image bound rectangle, clamps it to the edge of the rectangle.
+ *
+ * @param imageBound the rectangle to clamp edge points to.
+ * @param array an array of points to clamp to the rectangle, gets set to
+ * the clamped values.
+ */
+ public static void getEdgePoints(RectF imageBound, float[] array) {
+ if (array.length < 2)
+ return;
+ for (int x = 0; x < array.length; x += 2) {
+ array[x] = GeometryMath.clamp(array[x], imageBound.left, imageBound.right);
+ array[x + 1] = GeometryMath.clamp(array[x + 1], imageBound.top, imageBound.bottom);
+ }
+ }
+
+ /**
+ * Takes a point and the corners of a rectangle and returns the two corners
+ * representing the side of the rectangle closest to the point.
+ *
+ * @param point the point which is being checked
+ * @param corners the corners of the rectangle
+ * @return two corners representing the side of the rectangle
+ */
+ public static float[] closestSide(float[] point, float[] corners) {
+ int len = corners.length;
+ float oldMag = Float.POSITIVE_INFINITY;
+ float[] bestLine = null;
+ for (int i = 0; i < len; i += 2) {
+ float[] line = {
+ corners[i], corners[(i + 1) % len],
+ corners[(i + 2) % len], corners[(i + 3) % len]
+ };
+ float mag = GeometryMath.vectorLength(
+ GeometryMath.shortestVectorFromPointToLine(point, line));
+ if (mag < oldMag) {
+ oldMag = mag;
+ bestLine = line;
+ }
+ }
+ return bestLine;
+ }
+
+ /**
+ * Checks if a given point is within a rotated rectangle.
+ *
+ * @param point 2D point to check
+ * @param bound rectangle to rotate
+ * @param rot angle of rotation about rectangle center
+ * @return true if point is within rotated rectangle
+ */
+ public static boolean pointInRotatedRect(float[] point, RectF bound, float rot) {
+ Matrix m = new Matrix();
+ float[] p = Arrays.copyOf(point, 2);
+ m.setRotate(rot, bound.centerX(), bound.centerY());
+ Matrix m0 = new Matrix();
+ if (!m.invert(m0))
+ return false;
+ m0.mapPoints(p);
+ return inclusiveContains(bound, p[0], p[1]);
+ }
+
+ /**
+ * Checks if a given point is within a rotated rectangle.
+ *
+ * @param point 2D point to check
+ * @param rotatedRect corners of a rotated rectangle
+ * @param center center of the rotated rectangle
+ * @return true if point is within rotated rectangle
+ */
+ public static boolean pointInRotatedRect(float[] point, float[] rotatedRect, float[] center) {
+ RectF unrotated = new RectF();
+ float angle = getUnrotated(rotatedRect, center, unrotated);
+ return pointInRotatedRect(point, unrotated, angle);
+ }
+
+ /**
+ * Resizes rectangle to have a certain aspect ratio (center remains
+ * stationary).
+ *
+ * @param r rectangle to resize
+ * @param w new width aspect
+ * @param h new height aspect
+ */
+ public static void fixAspectRatio(RectF r, float w, float h) {
+ float scale = Math.min(r.width() / w, r.height() / h);
+ float centX = r.centerX();
+ float centY = r.centerY();
+ float hw = scale * w / 2;
+ float hh = scale * h / 2;
+ r.set(centX - hw, centY - hh, centX + hw, centY + hh);
+ }
+
+ private static float getUnrotated(float[] rotatedRect, float[] center, RectF unrotated) {
+ float dy = rotatedRect[1] - rotatedRect[3];
+ float dx = rotatedRect[0] - rotatedRect[2];
+ float angle = (float) (Math.atan(dy / dx) * 180 / Math.PI);
+ Matrix m = new Matrix();
+ m.setRotate(-angle, center[0], center[1]);
+ float[] unrotatedRect = new float[rotatedRect.length];
+ m.mapPoints(unrotatedRect, rotatedRect);
+ unrotated.set(trapToRect(unrotatedRect));
+ return angle;
+ }
+
+}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java b/src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java
index 55f791820..568dadfc3 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java
@@ -26,11 +26,37 @@ public class GeometryMath {
return Math.max(Math.min(i, high), low);
}
- protected static float[] shortestVectorFromPointToLine(float[] point, float[] l1, float[] l2) {
- float x1 = l1[0];
- float x2 = l2[0];
- float y1 = l1[1];
- float y2 = l2[1];
+ public static float[] lineIntersect(float[] line1, float[] line2) {
+ float a0 = line1[0];
+ float a1 = line1[1];
+ float b0 = line1[2];
+ float b1 = line1[3];
+ float c0 = line2[0];
+ float c1 = line2[1];
+ float d0 = line2[2];
+ float d1 = line2[3];
+ float t0 = a0 - b0;
+ float t1 = a1 - b1;
+ float t2 = b0 - d0;
+ float t3 = d1 - b1;
+ float t4 = c0 - d0;
+ float t5 = c1 - d1;
+
+ float denom = t1 * t4 - t0 * t5;
+ if (denom == 0)
+ return null;
+ float u = (t3 * t4 + t5 * t2) / denom;
+ float[] intersect = {
+ b0 + u * t0, b1 + u * t1
+ };
+ return intersect;
+ }
+
+ public static float[] shortestVectorFromPointToLine(float[] point, float[] line) {
+ float x1 = line[0];
+ float x2 = line[2];
+ float y1 = line[1];
+ float y2 = line[3];
float xdelt = x2 - x1;
float ydelt = y2 - y1;
if (xdelt == 0 && ydelt == 0)
@@ -40,67 +66,75 @@ public class GeometryMath {
float[] ret = {
(x1 + u * (x2 - x1)), (y1 + u * (y2 - y1))
};
- float [] vec = {ret[0] - point[0], ret[1] - point[1] };
+ float[] vec = {
+ ret[0] - point[0], ret[1] - point[1]
+ };
return vec;
}
// A . B
- public static float dotProduct(float[] a, float[] b){
+ public static float dotProduct(float[] a, float[] b) {
return a[0] * b[0] + a[1] * b[1];
}
- public static float[] normalize(float[] a){
+ public static float[] normalize(float[] a) {
float length = (float) Math.sqrt(a[0] * a[0] + a[1] * a[1]);
- float[] b = { a[0] / length, a[1] / length };
+ float[] b = {
+ a[0] / length, a[1] / length
+ };
return b;
}
// A onto B
- public static float scalarProjection(float[] a, float[] b){
+ public static float scalarProjection(float[] a, float[] b) {
float length = (float) Math.sqrt(b[0] * b[0] + b[1] * b[1]);
return dotProduct(a, b) / length;
}
- public static float[] getVectorFromPoints(float [] point1, float [] point2){
- float [] p = { point2[0] - point1[0], point2[1] - point1[1] };
+ public static float[] getVectorFromPoints(float[] point1, float[] point2) {
+ float[] p = {
+ point2[0] - point1[0], point2[1] - point1[1]
+ };
return p;
}
- public static float[] getUnitVectorFromPoints(float [] point1, float [] point2){
- float [] p = { point2[0] - point1[0], point2[1] - point1[1] };
+ public static float[] getUnitVectorFromPoints(float[] point1, float[] point2) {
+ float[] p = {
+ point2[0] - point1[0], point2[1] - point1[1]
+ };
float length = (float) Math.sqrt(p[0] * p[0] + p[1] * p[1]);
p[0] = p[0] / length;
p[1] = p[1] / length;
return p;
}
- public static RectF scaleRect(RectF r, float scale){
+ public static RectF scaleRect(RectF r, float scale) {
return new RectF(r.left * scale, r.top * scale, r.right * scale, r.bottom * scale);
}
// A - B
- public static float[] vectorSubtract(float [] a, float [] b){
+ public static float[] vectorSubtract(float[] a, float[] b) {
int len = a.length;
if (len != b.length)
return null;
- float [] ret = new float[len];
- for (int i = 0; i < len; i++){
+ float[] ret = new float[len];
+ for (int i = 0; i < len; i++) {
ret[i] = a[i] - b[i];
}
return ret;
}
- public static float vectorLength(float [] a){
+ public static float vectorLength(float[] a) {
return (float) Math.sqrt(a[0] * a[0] + a[1] * a[1]);
}
public static float scale(float oldWidth, float oldHeight, float newWidth, float newHeight) {
if (oldHeight == 0 || oldWidth == 0)
return 1;
- return Math.min(newWidth / oldWidth , newHeight / oldHeight);
+ return Math.min(newWidth / oldWidth, newHeight / oldHeight);
}
- public static Rect roundNearest(RectF r){
+ public static Rect roundNearest(RectF r) {
Rect q = new Rect(Math.round(r.left), Math.round(r.top), Math.round(r.right),
Math.round(r.bottom));
return q;
diff --git a/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java b/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java
index dffdc2449..b53284061 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java
@@ -21,12 +21,11 @@ import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
+import com.android.gallery3d.filtershow.CropExtras;
import com.android.gallery3d.filtershow.cache.ImageLoader;
import com.android.gallery3d.filtershow.filters.ImageFilterGeometry;
public class GeometryMetadata {
- // Applied in order: rotate, crop, scale.
- // Do not scale saved image (presumably?).
private static final ImageFilterGeometry mImageFilter = new ImageFilterGeometry();
private static final String LOGTAG = "GeometryMetadata";
private float mScaleFactor = 1.0f;
@@ -36,12 +35,29 @@ public class GeometryMetadata {
private final RectF mPhotoBounds = new RectF();
private FLIP mFlip = FLIP.NONE;
- private RectF mBounds = new RectF();
-
public enum FLIP {
NONE, VERTICAL, HORIZONTAL, BOTH
}
+ // Output format data from intent extras
+ private boolean mUseCropExtras = false;
+ private CropExtras mCropExtras = null;
+ public void setUseCropExtrasFlag(boolean f){
+ mUseCropExtras = f;
+ }
+
+ public boolean getUseCropExtrasFlag(){
+ return mUseCropExtras;
+ }
+
+ public void setCropExtras(CropExtras e){
+ mCropExtras = e;
+ }
+
+ public CropExtras getCropExtras(){
+ return mCropExtras;
+ }
+
public GeometryMetadata() {
}
@@ -86,7 +102,11 @@ public class GeometryMetadata {
mCropBounds.set(g.mCropBounds);
mPhotoBounds.set(g.mPhotoBounds);
mFlip = g.mFlip;
- mBounds = g.mBounds;
+
+ mUseCropExtras = g.mUseCropExtras;
+ if (g.mCropExtras != null){
+ mCropExtras = new CropExtras(g.mCropExtras);
+ }
}
public float getScaleFactor() {
@@ -184,48 +204,16 @@ public class GeometryMetadata {
+ ",photoRect=" + mPhotoBounds.toShortString() + "]";
}
- // TODO: refactor away
- protected static Matrix getHorizontalMatrix(float width) {
- Matrix flipHorizontalMatrix = new Matrix();
- flipHorizontalMatrix.setScale(-1, 1);
- flipHorizontalMatrix.postTranslate(width, 0);
- return flipHorizontalMatrix;
- }
-
protected static void concatHorizontalMatrix(Matrix m, float width) {
m.postScale(-1, 1);
m.postTranslate(width, 0);
}
- // TODO: refactor away
- protected static Matrix getVerticalMatrix(float height) {
- Matrix flipVerticalMatrix = new Matrix();
- flipVerticalMatrix.setScale(1, -1);
- flipVerticalMatrix.postTranslate(0, height);
- return flipVerticalMatrix;
- }
-
protected static void concatVerticalMatrix(Matrix m, float height) {
m.postScale(1, -1);
m.postTranslate(0, height);
}
- // TODO: refactor away
- public static Matrix getFlipMatrix(float width, float height, FLIP type) {
- if (type == FLIP.HORIZONTAL) {
- return getHorizontalMatrix(width);
- } else if (type == FLIP.VERTICAL) {
- return getVerticalMatrix(height);
- } else if (type == FLIP.BOTH) {
- Matrix flipper = getVerticalMatrix(height);
- flipper.postConcat(getHorizontalMatrix(width));
- return flipper;
- } else {
- Matrix m = new Matrix();
- m.reset(); // identity
- return m;
- }
- }
public static void concatMirrorMatrix(Matrix m, float width, float height, FLIP type) {
if (type == FLIP.HORIZONTAL) {
@@ -331,46 +319,10 @@ public class GeometryMetadata {
return m1;
}
- // TODO: refactor away
- public Matrix getFlipMatrix(float width, float height) {
- FLIP type = getFlipType();
- return getFlipMatrix(width, height, type);
- }
-
public boolean hasSwitchedWidthHeight() {
return (((int) (mRotation / 90)) % 2) != 0;
}
- // TODO: refactor away
- public Matrix buildGeometryMatrix(float width, float height, float scaling, float dx, float dy,
- float rotation) {
- float dx0 = width / 2;
- float dy0 = height / 2;
- Matrix m = getFlipMatrix(width, height);
- m.postTranslate(-dx0, -dy0);
- m.postRotate(rotation);
- m.postScale(scaling, scaling);
- m.postTranslate(dx, dy);
- return m;
- }
-
- // TODO: refactor away
- public Matrix buildGeometryMatrix(float width, float height, float scaling, float dx, float dy,
- boolean onlyRotate) {
- float rot = mRotation;
- if (!onlyRotate) {
- rot += mStraightenRotation;
- }
- return buildGeometryMatrix(width, height, scaling, dx, dy, rot);
- }
-
- // TODO: refactor away
- public Matrix buildGeometryUIMatrix(float scaling, float dx, float dy) {
- float w = mPhotoBounds.width();
- float h = mPhotoBounds.height();
- return buildGeometryMatrix(w, h, scaling, dx, dy, false);
- }
-
public static Matrix buildPhotoMatrix(RectF photo, RectF crop, float rotation,
float straighten, FLIP type) {
Matrix m = new Matrix();
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java b/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java
index a352a16e7..594e008a2 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java
@@ -29,22 +29,25 @@ import android.util.AttributeSet;
import android.util.Log;
import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.CropExtras;
public class ImageCrop extends ImageGeometry {
private static final boolean LOGV = false;
+
+ // Sides
private static final int MOVE_LEFT = 1;
private static final int MOVE_TOP = 2;
private static final int MOVE_RIGHT = 4;
private static final int MOVE_BOTTOM = 8;
private static final int MOVE_BLOCK = 16;
- //Corners
+ // Corners
private static final int TOP_LEFT = MOVE_TOP | MOVE_LEFT;
private static final int TOP_RIGHT = MOVE_TOP | MOVE_RIGHT;
private static final int BOTTOM_RIGHT = MOVE_BOTTOM | MOVE_RIGHT;
private static final int BOTTOM_LEFT = MOVE_BOTTOM | MOVE_LEFT;
- private static final float MIN_CROP_WIDTH_HEIGHT = 0.1f;
+ private static int mMinSideSize = 100;
private static int mTouchTolerance = 45;
private boolean mFirstDraw = true;
@@ -53,23 +56,30 @@ public class ImageCrop extends ImageGeometry {
private boolean mFixAspectRatio = false;
private float mLastRot = 0;
- private final Paint borderPaint;
+ private BoundedRect mBounded = null;
private int movingEdges;
private final Drawable cropIndicator;
private final int indicatorSize;
private final int mBorderColor = Color.argb(128, 255, 255, 255);
+ // Offset between crop center and photo center
+ private float[] mOffset = {
+ 0, 0
+ };
+ private CropExtras mCropExtras = null;
+ private boolean mDoingCropIntentAction = false;
+
private static final String LOGTAG = "ImageCrop";
private String mAspect = "";
private int mAspectTextSize = 24;
- public void setAspectTextSize(int textSize){
+ public void setAspectTextSize(int textSize) {
mAspectTextSize = textSize;
}
- public void setAspectString(String a){
+ public void setAspectString(String a) {
mAspect = a;
}
@@ -80,10 +90,6 @@ public class ImageCrop extends ImageGeometry {
Resources resources = context.getResources();
cropIndicator = resources.getDrawable(R.drawable.camera_crop);
indicatorSize = (int) resources.getDimension(R.dimen.crop_indicator_size);
- borderPaint = new Paint();
- borderPaint.setStyle(Paint.Style.STROKE);
- borderPaint.setColor(mBorderColor);
- borderPaint.setStrokeWidth(2f);
}
public ImageCrop(Context context, AttributeSet attrs) {
@@ -91,10 +97,6 @@ public class ImageCrop extends ImageGeometry {
Resources resources = context.getResources();
cropIndicator = resources.getDrawable(R.drawable.camera_crop);
indicatorSize = (int) resources.getDimension(R.dimen.crop_indicator_size);
- borderPaint = new Paint();
- borderPaint.setStyle(Paint.Style.STROKE);
- borderPaint.setColor(mBorderColor);
- borderPaint.setStrokeWidth(2f);
}
@Override
@@ -102,84 +104,46 @@ public class ImageCrop extends ImageGeometry {
return getContext().getString(R.string.crop);
}
- private void swapAspect(){
+ private void swapAspect() {
+ if (mDoingCropIntentAction) {
+ return;
+ }
float temp = mAspectWidth;
mAspectWidth = mAspectHeight;
mAspectHeight = temp;
}
- public static void setTouchTolerance(int tolerance){
+ /**
+ * Set tolerance for crop marker selection (in pixels)
+ */
+ public static void setTouchTolerance(int tolerance) {
mTouchTolerance = tolerance;
}
- private boolean switchCropBounds(int moving_corner, RectF dst) {
- RectF crop = getCropBoundsDisplayed();
- float dx1 = 0;
- float dy1 = 0;
- float dx2 = 0;
- float dy2 = 0;
- if ((moving_corner & MOVE_RIGHT) != 0) {
- dx1 = mCurrentX - crop.right;
- } else if ((moving_corner & MOVE_LEFT) != 0) {
- dx1 = mCurrentX - crop.left;
- }
- if ((moving_corner & MOVE_BOTTOM) != 0) {
- dy1 = mCurrentY - crop.bottom;
- } else if ((moving_corner & MOVE_TOP) != 0) {
- dy1 = mCurrentY - crop.top;
- }
- RectF newCrop = null;
- //Fix opposite corner in place and move sides
- if (moving_corner == BOTTOM_RIGHT) {
- newCrop = new RectF(crop.left, crop.top, crop.left + crop.height(), crop.top
- + crop.width());
- } else if (moving_corner == BOTTOM_LEFT) {
- newCrop = new RectF(crop.right - crop.height(), crop.top, crop.right, crop.top
- + crop.width());
- } else if (moving_corner == TOP_LEFT) {
- newCrop = new RectF(crop.right - crop.height(), crop.bottom - crop.width(),
- crop.right, crop.bottom);
- } else if (moving_corner == TOP_RIGHT) {
- newCrop = new RectF(crop.left, crop.bottom - crop.width(), crop.left
- + crop.height(), crop.bottom);
- }
- if ((moving_corner & MOVE_RIGHT) != 0) {
- dx2 = mCurrentX - newCrop.right;
- } else if ((moving_corner & MOVE_LEFT) != 0) {
- dx2 = mCurrentX - newCrop.left;
- }
- if ((moving_corner & MOVE_BOTTOM) != 0) {
- dy2 = mCurrentY - newCrop.bottom;
- } else if ((moving_corner & MOVE_TOP) != 0) {
- dy2 = mCurrentY - newCrop.top;
- }
- if (Math.sqrt(dx1*dx1 + dy1*dy1) > Math.sqrt(dx2*dx2 + dy2*dy2)){
- Matrix m = getCropBoundDisplayMatrix();
- Matrix m0 = new Matrix();
- if (!m.invert(m0)){
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO INVERT CROP MATRIX");
- return false;
- }
- if (!m0.mapRect(newCrop)){
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO MAP RECTANGLE TO RECTANGLE");
- return false;
- }
- swapAspect();
- dst.set(newCrop);
- return true;
- }
- return false;
+ /**
+ * Set minimum side length for crop box (in pixels)
+ */
+ public static void setMinCropSize(int minHeightWidth) {
+ mMinSideSize = minHeightWidth;
+ }
+
+ public void setExtras(CropExtras e) {
+ mCropExtras = e;
+ }
+
+ public void setCropActionFlag(boolean f) {
+ mDoingCropIntentAction = f;
}
- public void apply(float w, float h){
+ public void apply(float w, float h) {
mFixAspectRatio = true;
mAspectWidth = w;
mAspectHeight = h;
setLocalCropBounds(getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
getLocalStraighten()));
- cropSetup();
+ if (mVisibilityGained) {
+ cropSetup();
+ }
saveAndSetPreset();
invalidate();
}
@@ -194,202 +158,159 @@ public class ImageCrop extends ImageGeometry {
mAspectHeight = h / scale;
setLocalCropBounds(getUntranslatedStraightenCropBounds(photobounds,
getLocalStraighten()));
- cropSetup();
+ if (mVisibilityGained) {
+ cropSetup();
+ }
saveAndSetPreset();
invalidate();
}
public void applyClear() {
mFixAspectRatio = false;
+ mAspectWidth = 1;
+ mAspectHeight = 1;
setLocalCropBounds(getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
getLocalStraighten()));
- cropSetup();
+ if (mVisibilityGained) {
+ cropSetup();
+ }
saveAndSetPreset();
invalidate();
}
- private float getScaledMinWidthHeight() {
- RectF disp = new RectF(0, 0, getWidth(), getHeight());
- float scaled = Math.min(disp.width(), disp.height()) * MIN_CROP_WIDTH_HEIGHT
- / computeScale(getWidth(), getHeight());
- return scaled;
- }
-
- protected Matrix getCropRotationMatrix(float rotation, RectF localImage) {
- Matrix m = getLocalGeoFlipMatrix(localImage.width(), localImage.height());
- m.postRotate(rotation, localImage.centerX(), localImage.centerY());
- if (!m.rectStaysRect()) {
- return null;
- }
- return m;
- }
-
- protected Matrix getCropBoundDisplayMatrix(){
- Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
- if (m == null) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO MAP CROP BOUNDS TO RECTANGLE");
- m = new Matrix();
- }
- float zoom = computeScale(getWidth(), getHeight());
- m.postTranslate(mXOffset, mYOffset);
- m.postScale(zoom, zoom, mCenterX, mCenterY);
- return m;
- }
-
- protected RectF getCropBoundsDisplayed() {
- RectF bounds = getLocalCropBounds();
- RectF crop = new RectF(bounds);
- Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
-
- if (m == null) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO MAP CROP BOUNDS TO RECTANGLE");
- m = new Matrix();
+ public void clear() {
+ if (mCropExtras != null) {
+ int x = mCropExtras.getAspectX();
+ int y = mCropExtras.getAspectY();
+ if (mDoingCropIntentAction && x > 0 && y > 0) {
+ apply(x, y);
+ }
} else {
- m.mapRect(crop);
+ applyClear();
}
- m = new Matrix();
- float zoom = computeScale(getWidth(), getHeight());
- m.setScale(zoom, zoom, mCenterX, mCenterY);
- m.preTranslate(mXOffset, mYOffset);
- m.mapRect(crop);
- return crop;
}
- private RectF getRotatedCropBounds() {
- RectF bounds = getLocalCropBounds();
- RectF crop = new RectF(bounds);
- Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
-
- if (m == null) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO MAP CROP BOUNDS TO RECTANGLE");
- return null;
- } else {
- m.mapRect(crop);
- }
- return crop;
+ private Matrix getPhotoBoundDisplayedMatrix() {
+ float[] displayCenter = new float[2];
+ RectF scaledCrop = new RectF();
+ RectF scaledPhoto = new RectF();
+ float scale = getTransformState(scaledPhoto, scaledCrop, displayCenter);
+ Matrix m = GeometryMetadata.buildCenteredPhotoMatrix(scaledPhoto, scaledCrop,
+ getLocalRotation(), getLocalStraighten(), getLocalFlip(), displayCenter);
+ m.preScale(scale, scale);
+ return m;
}
- private RectF getUnrotatedCropBounds(RectF cropBounds) {
- Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
-
- if (m == null) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO GET ROTATION MATRIX");
- return null;
- }
- Matrix m0 = new Matrix();
- if (!m.invert(m0)) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO INVERT ROTATION MATRIX");
- return null;
- }
- RectF crop = new RectF(cropBounds);
- if (!m0.mapRect(crop)) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO UNROTATE CROPPING BOUNDS");
- return null;
- }
- return crop;
+ private Matrix getCropBoundDisplayedMatrix() {
+ float[] displayCenter = new float[2];
+ RectF scaledCrop = new RectF();
+ RectF scaledPhoto = new RectF();
+ float scale = getTransformState(scaledPhoto, scaledCrop, displayCenter);
+ Matrix m1 = GeometryMetadata.buildWanderingCropMatrix(scaledPhoto, scaledCrop,
+ getLocalRotation(), getLocalStraighten(), getLocalFlip(), displayCenter);
+ m1.preScale(scale, scale);
+ return m1;
}
- private RectF getRotatedStraightenBounds() {
- RectF straightenBounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
- getLocalStraighten());
- Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
-
- if (m == null) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO MAP STRAIGHTEN BOUNDS TO RECTANGLE");
- return null;
- } else {
- m.mapRect(straightenBounds);
- }
- return straightenBounds;
+ /**
+ * Takes the rotated corners of a rectangle and returns the angle; sets
+ * unrotated to be the unrotated version of the rectangle.
+ */
+ private static float getUnrotated(float[] rotatedRect, float[] center, RectF unrotated) {
+ float dy = rotatedRect[1] - rotatedRect[3];
+ float dx = rotatedRect[0] - rotatedRect[2];
+ float angle = (float) (Math.atan(dy / dx) * 180 / Math.PI);
+ Matrix m = new Matrix();
+ m.setRotate(-angle, center[0], center[1]);
+ float[] unrotatedRect = new float[rotatedRect.length];
+ m.mapPoints(unrotatedRect, rotatedRect);
+ unrotated.set(CropMath.trapToRect(unrotatedRect));
+ return angle;
}
/**
* Sets cropped bounds; modifies the bounds if it's smaller than the allowed
* dimensions.
*/
- public void setCropBounds(RectF bounds) {
- // Avoid cropping smaller than minimum width or height.
+ public boolean setCropBounds(RectF bounds) {
RectF cbounds = new RectF(bounds);
- float minWidthHeight = getScaledMinWidthHeight();
- float aw = mAspectWidth;
- float ah = mAspectHeight;
- if (mFixAspectRatio) {
- minWidthHeight /= aw * ah;
- int r = (int) (getLocalRotation() / 90);
- if (r % 2 != 0) {
- float temp = aw;
- aw = ah;
- ah = temp;
- }
- }
-
+ Matrix mc = getCropBoundDisplayedMatrix();
+ Matrix mcInv = new Matrix();
+ mc.invert(mcInv);
+ mcInv.mapRect(cbounds);
+ // Avoid cropping smaller than minimum
float newWidth = cbounds.width();
float newHeight = cbounds.height();
- if (mFixAspectRatio) {
- if (newWidth < (minWidthHeight * aw) || newHeight < (minWidthHeight * ah)) {
- newWidth = minWidthHeight * aw;
- newHeight = minWidthHeight * ah;
- }
- } else {
- if (newWidth < minWidthHeight) {
- newWidth = minWidthHeight;
- }
- if (newHeight < minWidthHeight) {
- newHeight = minWidthHeight;
- }
- }
+ float scale = getTransformState(null, null, null);
+ float minWidthHeight = mMinSideSize / scale;
RectF pbounds = getLocalPhotoBounds();
- if (pbounds.width() < minWidthHeight) {
- newWidth = pbounds.width();
+
+ // if photo is smaller than minimum, refuse to set crop bounds
+ if (pbounds.width() < minWidthHeight || pbounds.height() < minWidthHeight) {
+ return false;
}
- if (pbounds.height() < minWidthHeight) {
- newHeight = pbounds.height();
+
+ // if incoming crop is smaller than minimum, refuse to set crop bounds
+ if (newWidth < minWidthHeight || newHeight < minWidthHeight) {
+ return false;
}
- cbounds.set(cbounds.left, cbounds.top, cbounds.left + newWidth, cbounds.top + newHeight);
- RectF straightenBounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
- getLocalStraighten());
- cbounds.intersect(straightenBounds);
+ float newX = bounds.centerX() - (getWidth() / 2f);
+ float newY = bounds.centerY() - (getHeight() / 2f);
+ mOffset[0] = newX;
+ mOffset[1] = newY;
- if (mFixAspectRatio) {
- fixAspectRatio(cbounds, aw, ah);
- }
setLocalCropBounds(cbounds);
invalidate();
+ return true;
+ }
+
+ private BoundedRect getBoundedCrop(RectF crop) {
+ RectF photo = getLocalPhotoBounds();
+ Matrix mp = getPhotoBoundDisplayedMatrix();
+ float[] photoCorners = CropMath.getCornersFromRect(photo);
+ float[] photoCenter = {
+ photo.centerX(), photo.centerY()
+ };
+ mp.mapPoints(photoCorners);
+ mp.mapPoints(photoCenter);
+ RectF scaledPhoto = new RectF();
+ float angle = getUnrotated(photoCorners, photoCenter, scaledPhoto);
+ return new BoundedRect(angle, scaledPhoto, crop);
}
private void detectMovingEdges(float x, float y) {
- RectF cropped = getCropBoundsDisplayed();
+ Matrix m = getCropBoundDisplayedMatrix();
+ RectF cropped = getLocalCropBounds();
+ m.mapRect(cropped);
+ mBounded = getBoundedCrop(cropped);
movingEdges = 0;
- // Check left or right.
float left = Math.abs(x - cropped.left);
float right = Math.abs(x - cropped.right);
- if ((left <= mTouchTolerance) && (left < right)) {
+ float top = Math.abs(y - cropped.top);
+ float bottom = Math.abs(y - cropped.bottom);
+
+ // Check left or right.
+ if ((left <= mTouchTolerance) && ((y + mTouchTolerance) >= cropped.top)
+ && ((y - mTouchTolerance) <= cropped.bottom) && (left < right)) {
movingEdges |= MOVE_LEFT;
}
- else if (right <= mTouchTolerance) {
+ else if ((right <= mTouchTolerance) && ((y + mTouchTolerance) >= cropped.top)
+ && ((y - mTouchTolerance) <= cropped.bottom)) {
movingEdges |= MOVE_RIGHT;
}
// Check top or bottom.
- float top = Math.abs(y - cropped.top);
- float bottom = Math.abs(y - cropped.bottom);
- if ((top <= mTouchTolerance) & (top < bottom)) {
+ if ((top <= mTouchTolerance) && ((x + mTouchTolerance) >= cropped.left)
+ && ((x - mTouchTolerance) <= cropped.right) && (top < bottom)) {
movingEdges |= MOVE_TOP;
}
- else if (bottom <= mTouchTolerance) {
+ else if ((bottom <= mTouchTolerance) && ((x + mTouchTolerance) >= cropped.left)
+ && ((x - mTouchTolerance) <= cropped.right)) {
movingEdges |= MOVE_BOTTOM;
}
- // Check inside block.
- if (cropped.contains(x, y) && (movingEdges == 0)) {
+ if (movingEdges == 0) {
movingEdges = MOVE_BLOCK;
}
if (mFixAspectRatio && (movingEdges != MOVE_BLOCK)) {
@@ -398,7 +319,7 @@ public class ImageCrop extends ImageGeometry {
invalidate();
}
- private int fixEdgeToCorner(int moving_edges){
+ private int fixEdgeToCorner(int moving_edges) {
if (moving_edges == MOVE_LEFT) {
moving_edges |= MOVE_TOP;
}
@@ -414,9 +335,9 @@ public class ImageCrop extends ImageGeometry {
return moving_edges;
}
- private RectF fixedCornerResize(RectF r, int moving_corner, float dx, float dy){
+ private RectF fixedCornerResize(RectF r, int moving_corner, float dx, float dy) {
RectF newCrop = null;
- //Fix opposite corner in place and move sides
+ // 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);
@@ -434,120 +355,90 @@ public class ImageCrop extends ImageGeometry {
}
private void moveEdges(float dX, float dY) {
- RectF cropped = getRotatedCropBounds();
- float minWidthHeight = getScaledMinWidthHeight();
- float scale = computeScale(getWidth(), getHeight());
- float deltaX = dX / scale;
- float deltaY = dY / scale;
- int select = movingEdges;
- if (mFixAspectRatio && (select != MOVE_BLOCK)) {
-
- // TODO: add in orientation change for fixed aspect
- /*if (select == TOP_LEFT || select == TOP_RIGHT ||
- select == BOTTOM_LEFT || select == BOTTOM_RIGHT){
- RectF blank = new RectF();
- if(switchCropBounds(select, blank)){
- setCropBounds(blank);
- return;
- }
- }*/
- if (select == MOVE_LEFT) {
- select |= MOVE_TOP;
- }
- if (select == MOVE_TOP) {
- select |= MOVE_LEFT;
- }
- if (select == MOVE_RIGHT) {
- select |= MOVE_BOTTOM;
- }
- if (select == MOVE_BOTTOM) {
- select |= MOVE_RIGHT;
- }
- }
-
- if (select == MOVE_BLOCK) {
- RectF straight = getRotatedStraightenBounds();
- // Move the whole cropped bounds within the photo display bounds.
- deltaX = (deltaX > 0) ? Math.min(straight.right - cropped.right, deltaX)
- : Math.max(straight.left - cropped.left, deltaX);
- deltaY = (deltaY > 0) ? Math.min(straight.bottom - cropped.bottom, deltaY)
- : Math.max(straight.top - cropped.top, deltaY);
- cropped.offset(deltaX, deltaY);
+ RectF crop = mBounded.getInner();
+
+ Matrix mc = getCropBoundDisplayedMatrix();
+
+ RectF photo = getLocalPhotoBounds();
+ Matrix mp = getPhotoBoundDisplayedMatrix();
+ float[] photoCorners = CropMath.getCornersFromRect(photo);
+ float[] photoCenter = {
+ photo.centerX(), photo.centerY()
+ };
+ mp.mapPoints(photoCorners);
+ mp.mapPoints(photoCenter);
+
+ float minWidthHeight = mMinSideSize;
+
+ if (movingEdges == MOVE_BLOCK) {
+ mBounded.moveInner(-dX, -dY);
+ RectF r = mBounded.getInner();
+ setCropBounds(r);
+ return;
} else {
float dx = 0;
float dy = 0;
- if ((select & MOVE_LEFT) != 0) {
- dx = Math.min(cropped.left + deltaX, cropped.right - minWidthHeight) - cropped.left;
+ if ((movingEdges & MOVE_LEFT) != 0) {
+ dx = Math.min(crop.left + dX, crop.right - minWidthHeight) - crop.left;
}
- if ((select & MOVE_TOP) != 0) {
- dy = Math.min(cropped.top + deltaY, cropped.bottom - minWidthHeight) - cropped.top;
+ if ((movingEdges & MOVE_TOP) != 0) {
+ dy = Math.min(crop.top + dY, crop.bottom - minWidthHeight) - crop.top;
}
- if ((select & MOVE_RIGHT) != 0) {
- dx = Math.max(cropped.right + deltaX, cropped.left + minWidthHeight)
- - cropped.right;
+ if ((movingEdges & MOVE_RIGHT) != 0) {
+ dx = Math.max(crop.right + dX, crop.left + minWidthHeight)
+ - crop.right;
}
- if ((select & MOVE_BOTTOM) != 0) {
- dy = Math.max(cropped.bottom + deltaY, cropped.top + minWidthHeight)
- - cropped.bottom;
+ if ((movingEdges & MOVE_BOTTOM) != 0) {
+ dy = Math.max(crop.bottom + dY, crop.top + minWidthHeight)
+ - crop.bottom;
}
if (mFixAspectRatio) {
- RectF crop = getCropBoundsDisplayed();
- float [] l1 = {crop.left, crop.bottom};
- float [] l2 = {crop.right, crop.top};
- if(movingEdges == TOP_LEFT || movingEdges == BOTTOM_RIGHT){
+ 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[] 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, select, dx * scale, dy * scale);
- Matrix m = getCropBoundDisplayMatrix();
- Matrix m0 = new Matrix();
- if (!m.invert(m0)){
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO INVERT CROP MATRIX");
- return;
- }
- if (!m0.mapRect(newCrop)){
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO MAP RECTANGLE TO RECTANGLE");
- return;
- }
+ RectF newCrop = fixedCornerResize(crop, movingEdges, dx, dy);
+
+ mBounded.fixedAspectResizeInner(newCrop);
+ newCrop = mBounded.getInner();
setCropBounds(newCrop);
return;
} else {
- if ((select & MOVE_LEFT) != 0) {
- cropped.left += dx;
+ if ((movingEdges & MOVE_LEFT) != 0) {
+ crop.left += dx;
}
- if ((select & MOVE_TOP) != 0) {
- cropped.top += dy;
+ if ((movingEdges & MOVE_TOP) != 0) {
+ crop.top += dy;
}
- if ((select & MOVE_RIGHT) != 0) {
- cropped.right += dx;
+ if ((movingEdges & MOVE_RIGHT) != 0) {
+ crop.right += dx;
}
- if ((select & MOVE_BOTTOM) != 0) {
- cropped.bottom += dy;
+ if ((movingEdges & MOVE_BOTTOM) != 0) {
+ crop.bottom += dy;
}
}
}
- movingEdges = select;
- Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
- Matrix m0 = new Matrix();
- if (!m.invert(m0)) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO INVERT ROTATION MATRIX");
- }
- if (!m0.mapRect(cropped)) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO UNROTATE CROPPING BOUNDS");
- }
- setCropBounds(cropped);
+ mBounded.resizeInner(crop);
+ crop = mBounded.getInner();
+ setCropBounds(crop);
}
private void drawIndicator(Canvas canvas, Drawable indicator, float centerX, float centerY) {
@@ -560,7 +451,8 @@ public class ImageCrop extends ImageGeometry {
@Override
protected void setActionDown(float x, float y) {
super.setActionDown(x, y);
- detectMovingEdges(x, y);
+ detectMovingEdges(x + mOffset[0], y + mOffset[1]);
+
}
@Override
@@ -571,20 +463,54 @@ public class ImageCrop extends ImageGeometry {
@Override
protected void setActionMove(float x, float y) {
- if (movingEdges != 0){
+
+ if (movingEdges != 0) {
moveEdges(x - mCurrentX, y - mCurrentY);
}
super.setActionMove(x, y);
+
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ setActionUp();
+ cropSetup();
+ invalidate();
}
private void cropSetup() {
+ RectF crop = getLocalCropBounds();
+ Matrix m = getCropBoundDisplayedMatrix();
+ m.mapRect(crop);
if (mFixAspectRatio) {
- RectF cb = getRotatedCropBounds();
- fixAspectRatio(cb, mAspectWidth, mAspectHeight);
- RectF cb0 = getUnrotatedCropBounds(cb);
- setCropBounds(cb0);
- } else {
- setCropBounds(getLocalCropBounds());
+ CropMath.fixAspectRatio(crop, mAspectWidth, mAspectHeight);
+ }
+ float dCentX = getWidth() / 2;
+ float dCentY = getHeight() / 2;
+
+ BoundedRect r = getBoundedCrop(crop);
+ crop = r.getInner();
+ if (!setCropBounds(crop)) {
+ float h = mMinSideSize / 2;
+ float wScale = 1;
+ float hScale = mAspectHeight / mAspectWidth;
+ if (hScale < 1) {
+ wScale = mAspectWidth / mAspectHeight;
+ hScale = 1;
+ }
+ crop.set(dCentX - h * wScale, dCentY - h * hScale, dCentX + h * wScale, dCentY + h
+ * hScale);
+ if (mFixAspectRatio) {
+ CropMath.fixAspectRatio(crop, mAspectWidth, mAspectHeight);
+ }
+ r.setInner(crop);
+ crop = r.getInner();
+ if (!setCropBounds(crop)) {
+ crop.set(dCentX - h, dCentY - h, dCentX + h, dCentY + h);
+ r.setInner(crop);
+ crop = r.getInner();
+ setCropBounds(crop);
+ }
}
}
@@ -592,7 +518,7 @@ public class ImageCrop extends ImageGeometry {
public void imageLoaded() {
super.imageLoaded();
syncLocalToMasterGeometry();
- applyClear();
+ clear();
invalidate();
}
@@ -600,7 +526,7 @@ public class ImageCrop extends ImageGeometry {
protected void gainedVisibility() {
float rot = getLocalRotation();
// if has changed orientation via rotate
- if( ((int) ((rot - mLastRot) / 90)) % 2 != 0 ){
+ if (((int) ((rot - mLastRot) / 90)) % 2 != 0) {
swapAspect();
}
cropSetup();
@@ -610,7 +536,6 @@ public class ImageCrop extends ImageGeometry {
@Override
public void resetParameter() {
super.resetParameter();
- cropSetup();
}
@Override
@@ -635,7 +560,6 @@ public class ImageCrop extends ImageGeometry {
@Override
protected void drawShape(Canvas canvas, Bitmap image) {
- // TODO: move style to xml
gPaint.setAntiAlias(true);
gPaint.setFilterBitmap(true);
gPaint.setDither(true);
@@ -645,91 +569,102 @@ public class ImageCrop extends ImageGeometry {
cropSetup();
mFirstDraw = false;
}
- float rotation = getLocalRotation();
- RectF crop = drawTransformed(canvas, image, gPaint);
+ RectF crop = drawTransformed(canvas, image, gPaint, mOffset);
gPaint.setColor(mBorderColor);
gPaint.setStrokeWidth(3);
gPaint.setStyle(Paint.Style.STROKE);
- drawRuleOfThird(canvas, crop, gPaint);
-
- if (mFixAspectRatio){
- float w = crop.width();
- float h = crop.height();
- float diag = (float) Math.sqrt(w*w + h*h);
-
- float dash_len = 20;
- int num_intervals = (int) (diag / dash_len);
- float [] tl = { crop.left, crop.top };
- float centX = tl[0] + w/2;
- float centY = tl[1] + h/2 + 5;
- float [] br = { crop.right, crop.bottom };
- float [] vec = GeometryMath.getUnitVectorFromPoints(tl, br);
-
- float [] counter = tl;
- for (int x = 0; x < num_intervals; x++ ){
- float tempX = counter[0] + vec[0] * dash_len;
- float tempY = counter[1] + vec[1] * dash_len;
- if ((x % 2) == 0 && Math.abs(x - num_intervals / 2) > 2){
- canvas.drawLine(counter[0], counter[1], tempX, tempY, gPaint);
- }
- counter[0] = tempX;
- counter[1] = tempY;
+
+ boolean doThirds = true;
+
+ if (mFixAspectRatio) {
+ float spotlightX = 0;
+ float spotlightY = 0;
+ if (mCropExtras != null) {
+ spotlightX = mCropExtras.getSpotlightX();
+ spotlightY = mCropExtras.getSpotlightY();
}
+ if (mDoingCropIntentAction && spotlightX > 0 && spotlightY > 0) {
+ float sx = crop.width() * spotlightX;
+ float sy = crop.height() * spotlightY;
+ float cx = crop.centerX();
+ float cy = crop.centerY();
+ RectF r1 = new RectF(cx - sx / 2, cy - sy / 2, cx + sx / 2, cy + sy / 2);
+ float temp = sx;
+ sx = sy;
+ sy = temp;
+ RectF r2 = new RectF(cx - sx / 2, cy - sy / 2, cx + sx / 2, cy + sy / 2);
+ canvas.drawRect(r1, gPaint);
+ canvas.drawRect(r2, gPaint);
+ doThirds = false;
+ } else {
+ float w = crop.width();
+ float h = crop.height();
+ float diag = (float) Math.sqrt(w * w + h * h);
+
+ float dash_len = 20;
+ int num_intervals = (int) (diag / dash_len);
+ float[] tl = {
+ crop.left, crop.top
+ };
+ float centX = tl[0] + w / 2;
+ float centY = tl[1] + h / 2 + 5;
+ float[] br = {
+ crop.right, crop.bottom
+ };
+ float[] vec = GeometryMath.getUnitVectorFromPoints(tl, br);
+
+ float[] counter = tl;
+ for (int x = 0; x < num_intervals; x++) {
+ float tempX = counter[0] + vec[0] * dash_len;
+ float tempY = counter[1] + vec[1] * dash_len;
+ if ((x % 2) == 0 && Math.abs(x - num_intervals / 2) > 2) {
+ canvas.drawLine(counter[0], counter[1], tempX, tempY, gPaint);
+ }
+ counter[0] = tempX;
+ counter[1] = tempY;
+ }
- gPaint.setTextAlign(Paint.Align.CENTER);
- gPaint.setTextSize(mAspectTextSize);
- canvas.drawText(mAspect, centX, centY, gPaint);
+ gPaint.setTextAlign(Paint.Align.CENTER);
+ gPaint.setTextSize(mAspectTextSize);
+ canvas.drawText(mAspect, centX, centY, gPaint);
+ }
}
- gPaint.setColor(mBorderColor);
- gPaint.setStrokeWidth(3);
- gPaint.setStyle(Paint.Style.STROKE);
- drawStraighten(canvas, gPaint);
-
- int decoded_moving = decoder(movingEdges, rotation);
- canvas.save();
- canvas.rotate(rotation, mCenterX, mCenterY);
- RectF scaledCrop = unrotatedCropBounds();
- boolean notMoving = decoded_moving == 0;
- if (((decoded_moving & MOVE_TOP) != 0) || notMoving) {
- drawIndicator(canvas, cropIndicator, scaledCrop.centerX(), scaledCrop.top);
- }
- if (((decoded_moving & MOVE_BOTTOM) != 0) || notMoving) {
- drawIndicator(canvas, cropIndicator, scaledCrop.centerX(), scaledCrop.bottom);
- }
- if (((decoded_moving & MOVE_LEFT) != 0) || notMoving) {
- drawIndicator(canvas, cropIndicator, scaledCrop.left, scaledCrop.centerY());
- }
- if (((decoded_moving & MOVE_RIGHT) != 0) || notMoving) {
- drawIndicator(canvas, cropIndicator, scaledCrop.right, scaledCrop.centerY());
+ if (doThirds) {
+ drawRuleOfThird(canvas, crop, gPaint);
+
}
- canvas.restore();
- }
-
- private int bitCycleLeft(int x, int times, int d) {
- int mask = (1 << d) - 1;
- int mout = x & mask;
- times %= d;
- int hi = mout >> (d - times);
- int low = (mout << times) & mask;
- int ret = x & ~mask;
- ret |= low;
- ret |= hi;
- return ret;
- }
-
- protected int decoder(int movingEdges, float rotation) {
- int rot = constrainedRotation(rotation);
- switch (rot) {
- case 90:
- return bitCycleLeft(movingEdges, 3, 4);
- case 180:
- return bitCycleLeft(movingEdges, 2, 4);
- case 270:
- return bitCycleLeft(movingEdges, 1, 4);
- default:
- return movingEdges;
+
+ RectF scaledCrop = crop;
+ boolean notMoving = (movingEdges == 0);
+ if (mFixAspectRatio) {
+ if ((movingEdges == TOP_LEFT) || notMoving) {
+ drawIndicator(canvas, cropIndicator, scaledCrop.left, scaledCrop.top);
+ }
+ if ((movingEdges == TOP_RIGHT) || notMoving) {
+ drawIndicator(canvas, cropIndicator, scaledCrop.right, scaledCrop.top);
+ }
+ if ((movingEdges == BOTTOM_LEFT) || notMoving) {
+ drawIndicator(canvas, cropIndicator, scaledCrop.left, scaledCrop.bottom);
+ }
+ if ((movingEdges == BOTTOM_RIGHT) || notMoving) {
+ drawIndicator(canvas, cropIndicator, scaledCrop.right, scaledCrop.bottom);
+ }
+ } else {
+ if (((movingEdges & MOVE_TOP) != 0) || notMoving) {
+ drawIndicator(canvas, cropIndicator, scaledCrop.centerX(), scaledCrop.top);
+ }
+ if (((movingEdges & MOVE_BOTTOM) != 0) || notMoving) {
+ drawIndicator(canvas, cropIndicator, scaledCrop.centerX(), scaledCrop.bottom);
+ }
+ if (((movingEdges & MOVE_LEFT) != 0) || notMoving) {
+ drawIndicator(canvas, cropIndicator, scaledCrop.left, scaledCrop.centerY());
+ }
+ if (((movingEdges & MOVE_RIGHT) != 0) || notMoving) {
+ drawIndicator(canvas, cropIndicator, scaledCrop.right, scaledCrop.centerY());
+ }
}
}
+
}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java b/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java
index 42dd139bc..c8ae444da 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java
@@ -33,7 +33,7 @@ import com.android.gallery3d.filtershow.imageshow.GeometryMetadata.FLIP;
import com.android.gallery3d.filtershow.presets.ImagePreset;
public abstract class ImageGeometry extends ImageSlave {
- private boolean mVisibilityGained = false;
+ protected boolean mVisibilityGained = false;
private boolean mHasDrawn = false;
protected static final float MAX_STRAIGHTEN_ANGLE = 45;
@@ -191,8 +191,8 @@ public abstract class ImageGeometry extends ImageSlave {
return r * 90;
}
- protected Matrix getLocalGeoFlipMatrix(float width, float height) {
- return mLocalGeometry.getFlipMatrix(width, height);
+ protected boolean isHeightWidthSwapped() {
+ return ((int) (getLocalRotation() / 90)) % 2 != 0;
}
protected void setLocalStraighten(float r) {
@@ -217,32 +217,6 @@ public abstract class ImageGeometry extends ImageSlave {
return getLocalRotation() + getLocalStraighten();
}
- protected static float[] getCornersFromRect(RectF r) {
- // Order is:
- // 0------->1
- // ^ |
- // | v
- // 3<-------2
- float[] corners = {
- r.left, r.top, // 0
- r.right, r.top, // 1
- r.right, r.bottom,// 2
- r.left, r.bottom // 3
- };
- return corners;
- }
-
- // If edge point [x, y] in array [x0, y0, x1, y1, ...] is outside of the
- // image bound rectangle, clamps it to the edge of the rectangle.
- protected static void getEdgePoints(RectF imageBound, float[] array) {
- if (array.length < 2)
- return;
- for (int x = 0; x < array.length; x += 2) {
- array[x] = GeometryMath.clamp(array[x], imageBound.left, imageBound.right);
- array[x + 1] = GeometryMath.clamp(array[x + 1], imageBound.top, imageBound.bottom);
- }
- }
-
protected static Path drawClosedPath(Canvas canvas, Paint paint, float[] points) {
Path crop = new Path();
crop.moveTo(points[0], points[1]);
@@ -254,16 +228,6 @@ public abstract class ImageGeometry extends ImageSlave {
return crop;
}
- protected static void fixAspectRatio(RectF r, float w, float h) {
- float scale = Math.min(r.width() / w, r.height() / h);
- float centX = r.centerX();
- float centY = r.centerY();
- float hw = scale * w / 2;
- float hh = scale * h / 2;
- r.set(centX - hw, centY - hh, centX + hw, centY + hh);
-
- }
-
protected static float getNewHeightForWidthAspect(float width, float w, float h) {
return width * h / w;
}
@@ -290,11 +254,11 @@ public abstract class ImageGeometry extends ImageSlave {
}
protected void gainedVisibility() {
- // TODO: Override this stub.
+ // Override this stub.
}
protected void lostVisibility() {
- // TODO: Override this stub.
+ // Override this stub.
}
@Override
@@ -327,7 +291,7 @@ public abstract class ImageGeometry extends ImageSlave {
}
protected int getLocalValue() {
- return 0; // TODO: Override this
+ return 0; // Override this
}
protected void setActionDown(float x, float y) {
@@ -402,110 +366,19 @@ public abstract class ImageGeometry extends ImageSlave {
return new RectF(left, top, right, bottom);
}
- protected Matrix getGeoMatrix(RectF r, boolean onlyRotate) {
- RectF pbounds = getLocalPhotoBounds();
- float scale = GeometryMath
- .scale(pbounds.width(), pbounds.height(), getWidth(), getHeight());
- if (((int) (getLocalRotation() / 90)) % 2 != 0) {
- scale = GeometryMath.scale(pbounds.width(), pbounds.height(), getHeight(), getWidth());
- }
- float yoff = getHeight() / 2;
- float xoff = getWidth() / 2;
- float w = r.left * 2 + r.width();
- float h = r.top * 2 + r.height();
- return mLocalGeometry.buildGeometryMatrix(w, h, scale, xoff, yoff, onlyRotate);
- }
-
- protected void drawImageBitmap(Canvas canvas, Bitmap bitmap, Paint paint, Matrix m) {
- canvas.save();
- canvas.drawBitmap(bitmap, m, paint);
- canvas.restore();
- }
-
- protected void drawImageBitmap(Canvas canvas, Bitmap bitmap, Paint paint) {
- float scale = computeScale(getWidth(), getHeight());
- float yoff = getHeight() / 2;
- float xoff = getWidth() / 2;
- Matrix m = mLocalGeometry.buildGeometryUIMatrix(scale, xoff, yoff);
- drawImageBitmap(canvas, bitmap, paint, m);
- }
-
protected RectF straightenBounds() {
RectF bounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
getLocalStraighten());
- Matrix m = getGeoMatrix(bounds, true);
- m.mapRect(bounds);
- return bounds;
- }
-
- protected void drawStraighten(Canvas canvas, Paint paint) {
- RectF bounds = straightenBounds();
- canvas.save();
- canvas.drawRect(bounds, paint);
- canvas.restore();
- }
-
- protected RectF unrotatedCropBounds() {
- RectF bounds = getLocalCropBounds();
- RectF pbounds = getLocalPhotoBounds();
float scale = computeScale(getWidth(), getHeight());
- float yoff = getHeight() / 2;
- float xoff = getWidth() / 2;
- Matrix m = mLocalGeometry.buildGeometryMatrix(pbounds.width(), pbounds.height(), scale,
- xoff, yoff, 0);
- m.mapRect(bounds);
- return bounds;
- }
-
- protected RectF cropBounds() {
- RectF bounds = getLocalCropBounds();
- Matrix m = getGeoMatrix(getLocalPhotoBounds(), true);
- m.mapRect(bounds);
+ bounds = GeometryMath.scaleRect(bounds, scale);
+ float dx = (getWidth() / 2) - bounds.centerX();
+ float dy = (getHeight() / 2) - bounds.centerY();
+ bounds.offset(dx, dy);
return bounds;
}
- // Fails for non-90 degree
- protected void drawCrop(Canvas canvas, Paint paint) {
- RectF bounds = cropBounds();
- canvas.save();
- canvas.drawRect(bounds, paint);
- canvas.restore();
- }
-
- protected void drawCropSafe(Canvas canvas, Paint paint) {
- Matrix m = getGeoMatrix(getLocalPhotoBounds(), true);
- RectF crop = getLocalCropBounds();
- if (!m.rectStaysRect()) {
- float[] corners = getCornersFromRect(crop);
- m.mapPoints(corners);
- drawClosedPath(canvas, paint, corners);
- } else {
- m.mapRect(crop);
- Path path = new Path();
- path.addRect(crop, Path.Direction.CCW);
- canvas.drawPath(path, paint);
- }
- }
-
- protected void drawTransformedBitmap(Canvas canvas, Bitmap bitmap, Paint paint, boolean clip) {
- paint.setARGB(255, 0, 0, 0);
- drawImageBitmap(canvas, bitmap, paint);
- paint.setColor(Color.WHITE);
- paint.setStyle(Style.STROKE);
- paint.setStrokeWidth(2);
- drawCropSafe(canvas, paint);
- paint.setColor(getDefaultBackgroundColor());
- paint.setStyle(Paint.Style.FILL);
- drawShadows(canvas, paint, unrotatedCropBounds());
- }
-
- protected void drawShadows(Canvas canvas, Paint p, RectF innerBounds) {
- RectF display = new RectF(0, 0, getWidth(), getHeight());
- drawShadows(canvas, p, innerBounds, display, getLocalRotation(), getWidth() / 2,
- getHeight() / 2);
- }
-
- protected static void drawShadows(Canvas canvas, Paint p, RectF innerBounds, RectF outerBounds,
+ protected static void drawRotatedShadows(Canvas canvas, Paint p, RectF innerBounds,
+ RectF outerBounds,
float rotation, float centerX, float centerY) {
canvas.save();
canvas.rotate(rotation, centerX, centerY);
@@ -527,6 +400,15 @@ public abstract class ImageGeometry extends ImageSlave {
canvas.restore();
}
+ protected void drawShadows(Canvas canvas, Paint p, RectF innerBounds) {
+ float w = getWidth();
+ float h = getHeight();
+ canvas.drawRect(0f, 0f, w, innerBounds.top, p);
+ canvas.drawRect(0f, innerBounds.top, innerBounds.left, innerBounds.bottom, p);
+ canvas.drawRect(innerBounds.right, innerBounds.top, w, innerBounds.bottom, p);
+ canvas.drawRect(0f, innerBounds.bottom, w, h, p);
+ }
+
@Override
public void onDraw(Canvas canvas) {
if (getDirtyGeometryFlag()) {
@@ -547,21 +429,38 @@ public abstract class ImageGeometry extends ImageSlave {
// TODO: Override this stub.
}
- protected RectF drawTransformed(Canvas canvas, Bitmap photo, Paint p) {
- p.setARGB(255, 0, 0, 0);
+ /**
+ * Sets up inputs for buildCenteredPhotoMatrix and buildWanderingCropMatrix
+ * and returns the scale factor.
+ */
+ protected float getTransformState(RectF photo, RectF crop, float[] displayCenter) {
RectF photoBounds = getLocalPhotoBounds();
RectF cropBounds = getLocalCropBounds();
float scale = computeScale(getWidth(), getHeight());
// checks if local rotation is an odd multiple of 90.
- if (((int) (getLocalRotation() / 90)) % 2 != 0) {
+ if (isHeightWidthSwapped()) {
scale = computeScale(getHeight(), getWidth());
}
// put in screen coordinates
- RectF scaledCrop = GeometryMath.scaleRect(cropBounds, scale);
- RectF scaledPhoto = GeometryMath.scaleRect(photoBounds, scale);
- float[] displayCenter = {
- getWidth() / 2f, getHeight() / 2f
- };
+ if (crop != null) {
+ crop.set(GeometryMath.scaleRect(cropBounds, scale));
+ }
+ if (photo != null) {
+ photo.set(GeometryMath.scaleRect(photoBounds, scale));
+ }
+ if (displayCenter != null && displayCenter.length >= 2) {
+ displayCenter[0] = getWidth() / 2f;
+ displayCenter[1] = getHeight() / 2f;
+ }
+ return scale;
+ }
+
+ protected RectF drawTransformed(Canvas canvas, Bitmap photo, Paint p, float[] offset) {
+ p.setARGB(255, 0, 0, 0);
+ float[] displayCenter = new float[2];
+ RectF scaledCrop = new RectF();
+ RectF scaledPhoto = new RectF();
+ float scale = getTransformState(scaledPhoto, scaledCrop, displayCenter);
Matrix m = GeometryMetadata.buildCenteredPhotoMatrix(scaledPhoto, scaledCrop,
getLocalRotation(), getLocalStraighten(), getLocalFlip(), displayCenter);
@@ -569,9 +468,11 @@ public abstract class ImageGeometry extends ImageSlave {
getLocalRotation(), getLocalStraighten(), getLocalFlip(), displayCenter);
m1.mapRect(scaledCrop);
Path path = new Path();
+ scaledCrop.offset(-offset[0], -offset[1]);
path.addRect(scaledCrop, Path.Direction.CCW);
m.preScale(scale, scale);
+ m.postTranslate(-offset[0], -offset[1]);
canvas.save();
canvas.drawBitmap(photo, m, p);
canvas.restore();
@@ -580,6 +481,11 @@ public abstract class ImageGeometry extends ImageSlave {
p.setStyle(Style.STROKE);
p.setStrokeWidth(2);
canvas.drawPath(path, p);
+
+ p.setColor(getDefaultBackgroundColor());
+ p.setAlpha(128);
+ p.setStyle(Paint.Style.FILL);
+ drawShadows(canvas, p, scaledCrop);
return scaledCrop;
}
@@ -590,7 +496,7 @@ public abstract class ImageGeometry extends ImageSlave {
float imageHeight = cropBounds.height();
float scale = GeometryMath.scale(imageWidth, imageHeight, getWidth(), getHeight());
// checks if local rotation is an odd multiple of 90.
- if (((int) (getLocalRotation() / 90)) % 2 != 0) {
+ if (isHeightWidthSwapped()) {
scale = GeometryMath.scale(imageWidth, imageHeight, getHeight(), getWidth());
}
// put in screen coordinates
@@ -618,6 +524,8 @@ public abstract class ImageGeometry extends ImageSlave {
p.setStyle(Paint.Style.FILL);
scaledCrop.offset(displayCenter[0] - scaledCrop.centerX(), displayCenter[1]
- scaledCrop.centerY());
- drawShadows(canvas, p, scaledCrop);
+ RectF display = new RectF(0, 0, getWidth(), getHeight());
+ drawRotatedShadows(canvas, p, scaledCrop, display, getLocalRotation(), getWidth() / 2,
+ getHeight() / 2);
}
}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageRedEyes.java b/src/com/android/gallery3d/filtershow/imageshow/ImageRedEyes.java
new file mode 100644
index 000000000..5119dff3c
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageRedEyes.java
@@ -0,0 +1,148 @@
+
+package com.android.gallery3d.filtershow.imageshow;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+import com.android.gallery3d.filtershow.filters.ImageFilterRedEye;
+import com.android.gallery3d.filtershow.filters.RedEyeCandidate;
+
+public class ImageRedEyes extends ImageSlave {
+
+ private static final String LOGTAG = "ImageRedEyes";
+ private RectF mCurrentRect = null;
+ private static float mTouchPadding = 80;
+
+ public static void setTouchPadding(float padding) {
+ mTouchPadding = padding;
+ }
+
+ public ImageRedEyes(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public ImageRedEyes(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void resetParameter() {
+ ImageFilterRedEye filter = (ImageFilterRedEye) getCurrentFilter();
+ if (filter != null) {
+ filter.clear();
+ }
+ mCurrentRect = null;
+ invalidate();
+ }
+
+ @Override
+ public void updateImage() {
+ super.updateImage();
+ invalidate();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ super.onTouchEvent(event);
+ float ex = event.getX();
+ float ey = event.getY();
+
+ ImageFilterRedEye filter = (ImageFilterRedEye) getCurrentFilter();
+
+ // let's transform (ex, ey) to displayed image coordinates
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ mCurrentRect = new RectF();
+ mCurrentRect.left = ex - mTouchPadding;
+ mCurrentRect.top = ey - mTouchPadding;
+ }
+ if (event.getAction() == MotionEvent.ACTION_MOVE) {
+ mCurrentRect.right = ex + mTouchPadding;
+ mCurrentRect.bottom = ey + mTouchPadding;
+ }
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ if (mCurrentRect != null) {
+ // transform to original coordinates
+ GeometryMetadata geo = getImagePreset().mGeoData;
+ Matrix originalToScreen = geo.getOriginalToScreen(true,
+ mImageLoader.getOriginalBounds().width(),
+ mImageLoader.getOriginalBounds().height(),
+ getWidth(), getHeight());
+ Matrix originalNoRotateToScreen = geo.getOriginalToScreen(false,
+ mImageLoader.getOriginalBounds().width(),
+ mImageLoader.getOriginalBounds().height(),
+ getWidth(), getHeight());
+
+ Matrix invert = new Matrix();
+ originalToScreen.invert(invert);
+ RectF r = new RectF(mCurrentRect);
+ invert.mapRect(r);
+ RectF r2 = new RectF(mCurrentRect);
+ invert.reset();
+ originalNoRotateToScreen.invert(invert);
+ invert.mapRect(r2);
+ filter.addRect(r, r2);
+ this.resetImageCaches(this);
+ }
+ mCurrentRect = null;
+ }
+ invalidate();
+ return true;
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ Paint paint = new Paint();
+ paint.setStyle(Style.STROKE);
+ paint.setColor(Color.RED);
+ paint.setStrokeWidth(2);
+ if (mCurrentRect != null) {
+ paint.setColor(Color.RED);
+ RectF drawRect = new RectF(mCurrentRect);
+ canvas.drawRect(drawRect, paint);
+ }
+
+ GeometryMetadata geo = getImagePreset().mGeoData;
+ Matrix originalToScreen = geo.getOriginalToScreen(false,
+ mImageLoader.getOriginalBounds().width(),
+ mImageLoader.getOriginalBounds().height(), getWidth(), getHeight());
+ Matrix originalRotateToScreen = geo.getOriginalToScreen(true,
+ mImageLoader.getOriginalBounds().width(),
+ mImageLoader.getOriginalBounds().height(), getWidth(), getHeight());
+
+ ImageFilterRedEye filter = (ImageFilterRedEye) getCurrentFilter();
+ for (RedEyeCandidate candidate : filter.getCandidates()) {
+ RectF rect = candidate.getRect();
+ RectF drawRect = new RectF();
+ originalToScreen.mapRect(drawRect, rect);
+ RectF fullRect = new RectF();
+ originalRotateToScreen.mapRect(fullRect, rect);
+ paint.setColor(Color.BLUE);
+ canvas.drawRect(fullRect, paint);
+ canvas.drawLine(fullRect.centerX(), fullRect.top,
+ fullRect.centerX(), fullRect.bottom, paint);
+ canvas.drawLine(fullRect.left, fullRect.centerY(),
+ fullRect.right, fullRect.centerY(), paint);
+ paint.setColor(Color.GREEN);
+ float dw = drawRect.width();
+ float dh = drawRect.height();
+ float dx = fullRect.centerX() - dw/2;
+ float dy = fullRect.centerY() - dh/2;
+ drawRect.set(dx, dy, dx + dw, dy + dh);
+ canvas.drawRect(drawRect, paint);
+ canvas.drawLine(drawRect.centerX(), drawRect.top,
+ drawRect.centerX(), drawRect.bottom, paint);
+ canvas.drawLine(drawRect.left, drawRect.centerY(),
+ drawRect.right, drawRect.centerY(), paint);
+ canvas.drawCircle(drawRect.centerX(), drawRect.centerY(),
+ mTouchPadding, paint);
+ }
+ }
+}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
index 0145c24dc..d9df7b7fc 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
@@ -23,6 +23,7 @@ import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.net.Uri;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.GestureDetector;
@@ -83,7 +84,7 @@ public class ImageShow extends View implements OnGestureListener,
private HistoryAdapter mHistoryAdapter = null;
private ImageStateAdapter mImageStateAdapter = null;
- private Rect mImageBounds = new Rect();
+ protected Rect mImageBounds = new Rect();
private boolean mTouchShowOriginal = false;
private long mTouchShowOriginalDate = 0;
@@ -230,6 +231,7 @@ public class ImageShow extends View implements OnGestureListener,
}
updateSeekBar(parameter, minp, maxp);
invalidate();
+ mActivity.enableSave(hasModifications());
}
@Override
@@ -323,6 +325,10 @@ public class ImageShow extends View implements OnGestureListener,
return dst;
}
+ public Rect getImageCropBounds() {
+ return GeometryMath.roundNearest(getImagePreset().mGeoData.getPreviewCropBounds());
+ }
+
public Rect getDisplayedImageBounds() {
return mImageBounds;
}
@@ -396,6 +402,7 @@ public class ImageShow extends View implements OnGestureListener,
public void updateImagePresets(boolean force) {
ImagePreset preset = getImagePreset();
if (preset == null) {
+ mActivity.enableSave(false);
return;
}
if (force) {
@@ -419,6 +426,7 @@ public class ImageShow extends View implements OnGestureListener,
mFiltersOnlyImage = null;
}
}
+ mActivity.enableSave(hasModifications());
}
public void requestFilteredImages() {
@@ -643,13 +651,13 @@ public class ImageShow extends View implements OnGestureListener,
}
public void updateImage() {
+ invalidate();
if (!updateGeometryFlags()) {
return;
}
Bitmap bitmap = mImageLoader.getOriginalBitmapLarge();
if (bitmap != null) {
imageSizeChanged(bitmap);
- invalidate();
}
}
@@ -666,6 +674,14 @@ public class ImageShow extends View implements OnGestureListener,
mImageLoader.saveImage(getImagePreset(), filterShowActivity, file);
}
+ public void saveToUri(Bitmap f, Uri u, String m, FilterShowActivity filterShowActivity) {
+ mImageLoader.saveToUri(f, u, m, filterShowActivity);
+ }
+
+ public void returnFilteredResult(FilterShowActivity filterShowActivity) {
+ mImageLoader.returnFilteredResult(getImagePreset(), filterShowActivity);
+ }
+
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java b/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java
index 6a79e18a1..2a3ee2856 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java
@@ -47,6 +47,13 @@ public class ImageSmallFilter extends ImageShow implements View.OnClickListener
protected final int mTextColor = Color.WHITE;
private ImageSmallFilter mNullFilter;
+ private Bitmap mOverlayBitmap = null;
+ private final int mOverlayTint = Color.argb(100, 0, 0, 0);
+
+ public void setOverlayBitmap(Bitmap bitmap){
+ mOverlayBitmap = bitmap;
+ }
+
public static void setMargin(int value) {
mMargin = value;
}
@@ -77,6 +84,10 @@ public class ImageSmallFilter extends ImageShow implements View.OnClickListener
mImagePreset.add(mImageFilter);
}
+ public ImageFilter getImageFilter() {
+ return mImageFilter;
+ }
+
@Override
public void setSelected(boolean value) {
if (mIsSelected != value) {
@@ -188,6 +199,13 @@ public class ImageSmallFilter extends ImageShow implements View.OnClickListener
mPaint.setTextSize(mTextSize);
mPaint.setColor(mTextColor);
canvas.drawText(mImageFilter.getName(), x, y - mTextMargin, mPaint);
+ if (mOverlayBitmap != null) {
+ mPaint.setColor(mOverlayTint);
+ canvas.drawRect(0, mMargin, getWidth(), getWidth() + mMargin, mPaint);
+ Rect d = new Rect(0, mMargin, getWidth() - mMargin, getWidth());
+ mPaint.setColor(Color.BLACK);
+ drawImage(canvas, mOverlayBitmap, d);
+ }
}
public void drawImage(Canvas canvas, Bitmap image, Rect destination) {
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java
index 57a22aab3..7a539da8f 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
@@ -105,10 +106,10 @@ public class ImageStraighten extends ImageGeometry {
@Override
protected void drawShape(Canvas canvas, Bitmap image) {
- drawTransformed(canvas, image, gPaint);
+ float [] o = {0, 0};
+ RectF bounds = drawTransformed(canvas, image, gPaint, o);
// Draw the grid
- RectF bounds = straightenBounds();
Path path = new Path();
path.addRect(bounds, Path.Direction.CCW);
gPaint.setARGB(255, 255, 255, 255);
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageWithIcon.java b/src/com/android/gallery3d/filtershow/imageshow/ImageWithIcon.java
deleted file mode 100644
index a332fa72a..000000000
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageWithIcon.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2012 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.imageshow;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-
-/**
- * TODO: Insert description here. (generated by hoford)
- */
-public class ImageWithIcon extends ImageSmallFilter {
- /**
- * @param context
- */
- public ImageWithIcon(Context context) {
- super(context);
- // TODO(hoford): Auto-generated constructor stub
- }
-
- private Bitmap bitmap;
-
- public void setIcon(Bitmap bitmap){
- this.bitmap = bitmap;
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- if (bitmap != null) {
- Rect d = new Rect(0, mMargin, getWidth() - mMargin, getWidth());
- drawImage(canvas, bitmap, d);
- }
- }
-}