OSDN Git Service

Refactoring crop, made unconstrained.
authorRuben Brunk <rubenbrunk@google.com>
Wed, 31 Oct 2012 18:44:15 +0000 (11:44 -0700)
committerRuben Brunk <rubenbrunk@google.com>
Fri, 30 Nov 2012 02:30:09 +0000 (18:30 -0800)
Bug 7401363

Change-Id: I3ef10c43fd2a01216d6d6c8cc77b31ddef3fdd9b

src/com/android/gallery3d/filtershow/FilterShowActivity.java
src/com/android/gallery3d/filtershow/imageshow/BoundedRect.java [new file with mode: 0644]
src/com/android/gallery3d/filtershow/imageshow/CropMath.java [new file with mode: 0644]
src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java
src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java
src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java
src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java
src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java

index 43697bf..9a0c69d 100644 (file)
@@ -207,6 +207,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
 
         mImageCrop.setAspectTextSize((int) getPixelsFromDip(18));
         ImageCrop.setTouchTolerance((int) getPixelsFromDip(25));
+        ImageCrop.setMinCropSize((int) getPixelsFromDip(55));
         mImageViews.add(mImageShow);
         mImageViews.add(mImageCurves);
         mImageViews.add(mImageBorders);
diff --git a/src/com/android/gallery3d/filtershow/imageshow/BoundedRect.java b/src/com/android/gallery3d/filtershow/imageshow/BoundedRect.java
new file mode 100644 (file)
index 0000000..e94d1ed
--- /dev/null
@@ -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 (file)
index 0000000..9037ca0
--- /dev/null
@@ -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;
+    }
+
+}
index 55f7918..568dadf 100644 (file)
@@ -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;
index dffdc24..0deb1e1 100644 (file)
@@ -25,8 +25,6 @@ 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,8 +34,6 @@ 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
     }
@@ -86,7 +82,6 @@ public class GeometryMetadata {
         mCropBounds.set(g.mCropBounds);
         mPhotoBounds.set(g.mPhotoBounds);
         mFlip = g.mFlip;
-        mBounds = g.mBounds;
     }
 
     public float getScaleFactor() {
@@ -184,48 +179,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 +294,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();
index a352a16..f25f5b1 100644 (file)
@@ -29,22 +29,25 @@ import android.util.AttributeSet;
 import android.util.Log;
 
 import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.imageshow.ImageGeometry.MODES;
 
 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,28 @@ 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 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 +88,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 +95,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 +102,35 @@ public class ImageCrop extends ImageGeometry {
         return getContext().getString(R.string.crop);
     }
 
-    private void swapAspect(){
+    private void swapAspect() {
         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 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 +145,147 @@ 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;
-        }
+    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;
     }
 
-    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;
+    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;
     }
 
-    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();
-        } else {
-            m.mapRect(crop);
-        }
-        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 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 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 +294,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 +310,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 +330,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 +426,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 +438,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);
+            }
         }
     }
 
@@ -600,7 +501,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 +511,6 @@ public class ImageCrop extends ImageGeometry {
     @Override
     public void resetParameter() {
         super.resetParameter();
-        cropSetup();
     }
 
     @Override
@@ -635,7 +535,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,32 +544,35 @@ 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){
+        if (mFixAspectRatio) {
             float w = crop.width();
             float h = crop.height();
-            float diag = (float) Math.sqrt(w*w + h*h);
+            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[] 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){
+                if ((x % 2) == 0 && Math.abs(x - num_intervals / 2) > 2) {
                     canvas.drawLine(counter[0], counter[1], tempX, tempY, gPaint);
                 }
                 counter[0] = tempX;
@@ -682,54 +584,35 @@ public class ImageCrop extends ImageGeometry {
             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());
-        }
-        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());
+            }
         }
     }
+
 }
index 42dd139..c8ae444 100644 (file)
@@ -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);
     }
 }
index 57a22aa..7a539da 100644 (file)
@@ -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);