16e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk/*
26e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk * Copyright (C) 2012 The Android Open Source Project
36e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk *
46e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk * Licensed under the Apache License, Version 2.0 (the "License");
56e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk * you may not use this file except in compliance with the License.
66e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk * You may obtain a copy of the License at
76e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk *
86e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk *      http://www.apache.org/licenses/LICENSE-2.0
96e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk *
106e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk * Unless required by applicable law or agreed to in writing, software
116e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk * distributed under the License is distributed on an "AS IS" BASIS,
126e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk * See the License for the specific language governing permissions and
146e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk * limitations under the License.
156e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk */
166fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunkpackage com.android.gallery3d.filtershow.crop;
176e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
186e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunkimport android.graphics.Matrix;
196fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunkimport android.graphics.Rect;
206e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunkimport android.graphics.RectF;
216e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
22b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport com.android.gallery3d.filtershow.imageshow.GeometryMathUtils;
236fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunk
246e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunkimport java.util.Arrays;
256e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
266e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk/**
276e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk * Maintains invariant that inner rectangle is constrained to be within the
286e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk * outer, rotated rectangle.
296e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk */
306e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunkpublic class BoundedRect {
316e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    private float rot;
326e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    private RectF outer;
336e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    private RectF inner;
346e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    private float[] innerRotated;
356e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
366fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunk    public BoundedRect(float rotation, Rect outerRect, Rect innerRect) {
376fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunk        rot = rotation;
386fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunk        outer = new RectF(outerRect);
396fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunk        inner = new RectF(innerRect);
406fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunk        innerRotated = CropMath.getCornersFromRect(inner);
416fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunk        rotateInner();
426fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunk        if (!isConstrained())
436fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunk            reconstrain();
446e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    }
456e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
466e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    public BoundedRect(float rotation, RectF outerRect, RectF innerRect) {
476e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        rot = rotation;
486e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        outer = new RectF(outerRect);
496e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        inner = new RectF(innerRect);
506e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        innerRotated = CropMath.getCornersFromRect(inner);
516e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        rotateInner();
526e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        if (!isConstrained())
536e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            reconstrain();
546e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    }
556e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
56ea2fa83c2dd2b59b8a67ec363e4ac431748f997bRuben Brunk    public void resetTo(float rotation, RectF outerRect, RectF innerRect) {
57ea2fa83c2dd2b59b8a67ec363e4ac431748f997bRuben Brunk        rot = rotation;
58ea2fa83c2dd2b59b8a67ec363e4ac431748f997bRuben Brunk        outer.set(outerRect);
59ea2fa83c2dd2b59b8a67ec363e4ac431748f997bRuben Brunk        inner.set(innerRect);
60ea2fa83c2dd2b59b8a67ec363e4ac431748f997bRuben Brunk        innerRotated = CropMath.getCornersFromRect(inner);
61ea2fa83c2dd2b59b8a67ec363e4ac431748f997bRuben Brunk        rotateInner();
62ea2fa83c2dd2b59b8a67ec363e4ac431748f997bRuben Brunk        if (!isConstrained())
63ea2fa83c2dd2b59b8a67ec363e4ac431748f997bRuben Brunk            reconstrain();
64ea2fa83c2dd2b59b8a67ec363e4ac431748f997bRuben Brunk    }
65ea2fa83c2dd2b59b8a67ec363e4ac431748f997bRuben Brunk
666e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    /**
676e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk     * Sets inner, and re-constrains it to fit within the rotated bounding rect.
686e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk     */
696e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    public void setInner(RectF newInner) {
706e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        if (inner.equals(newInner))
716e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            return;
726e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        inner = newInner;
736e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        innerRotated = CropMath.getCornersFromRect(inner);
746e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        rotateInner();
756e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        if (!isConstrained())
766e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            reconstrain();
776e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    }
786e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
796e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    /**
806e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk     * Sets rotation, and re-constrains inner to fit within the rotated bounding rect.
816e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk     */
826e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    public void setRotation(float rotation) {
836e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        if (rotation == rot)
846e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            return;
856e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        rot = rotation;
866e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        innerRotated = CropMath.getCornersFromRect(inner);
876e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        rotateInner();
886e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        if (!isConstrained())
896e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            reconstrain();
906e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    }
916e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
926fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunk    public void setToInner(RectF r) {
936fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunk        r.set(inner);
946fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunk    }
956fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunk
966fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunk    public void setToOuter(RectF r) {
976fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunk        r.set(outer);
986fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunk    }
996fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunk
1006e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    public RectF getInner() {
1016e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        return new RectF(inner);
1026e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    }
1036e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
1046fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunk    public RectF getOuter() {
1056fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunk        return new RectF(outer);
1066fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunk    }
1076fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunk
1086e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    /**
1096e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk     * Tries to move the inner rectangle by (dx, dy).  If this would cause it to leave
1106e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk     * the bounding rectangle, snaps the inner rectangle to the edge of the bounding
1116e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk     * rectangle.
1126e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk     */
1136e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    public void moveInner(float dx, float dy) {
1146e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        Matrix m0 = getInverseRotMatrix();
1156e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
1166e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        RectF translatedInner = new RectF(inner);
1176e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        translatedInner.offset(dx, dy);
1186e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
1196e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        float[] translatedInnerCorners = CropMath.getCornersFromRect(translatedInner);
1206e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        float[] outerCorners = CropMath.getCornersFromRect(outer);
1216e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
1226e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        m0.mapPoints(translatedInnerCorners);
1236e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        float[] correction = {
1246e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                0, 0
1256e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        };
1266e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
1276e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        // find correction vectors for corners that have moved out of bounds
1286e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        for (int i = 0; i < translatedInnerCorners.length; i += 2) {
1296e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            float correctedInnerX = translatedInnerCorners[i] + correction[0];
1306e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            float correctedInnerY = translatedInnerCorners[i + 1] + correction[1];
1316e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            if (!CropMath.inclusiveContains(outer, correctedInnerX, correctedInnerY)) {
1326e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                float[] badCorner = {
1336e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                        correctedInnerX, correctedInnerY
1346e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                };
1356e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                float[] nearestSide = CropMath.closestSide(badCorner, outerCorners);
1366e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                float[] correctionVec =
137b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                        GeometryMathUtils.shortestVectorFromPointToLine(badCorner, nearestSide);
1386e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                correction[0] += correctionVec[0];
1396e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                correction[1] += correctionVec[1];
1406e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            }
1416e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        }
1426e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
1436e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        for (int i = 0; i < translatedInnerCorners.length; i += 2) {
1446e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            float correctedInnerX = translatedInnerCorners[i] + correction[0];
1456e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            float correctedInnerY = translatedInnerCorners[i + 1] + correction[1];
1466e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            if (!CropMath.inclusiveContains(outer, correctedInnerX, correctedInnerY)) {
1476e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                float[] correctionVec = {
1486e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                        correctedInnerX, correctedInnerY
1496e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                };
1506e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                CropMath.getEdgePoints(outer, correctionVec);
1516e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                correctionVec[0] -= correctedInnerX;
1526e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                correctionVec[1] -= correctedInnerY;
1536e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                correction[0] += correctionVec[0];
1546e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                correction[1] += correctionVec[1];
1556e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            }
1566e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        }
1576e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
1586e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        // Set correction
1596e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        for (int i = 0; i < translatedInnerCorners.length; i += 2) {
1606e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            float correctedInnerX = translatedInnerCorners[i] + correction[0];
1616e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            float correctedInnerY = translatedInnerCorners[i + 1] + correction[1];
1626e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            // update translated corners with correction vectors
1636e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            translatedInnerCorners[i] = correctedInnerX;
1646e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            translatedInnerCorners[i + 1] = correctedInnerY;
1656e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        }
1666e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
1676e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        innerRotated = translatedInnerCorners;
1686e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        // reconstrain to update inner
1696e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        reconstrain();
1706e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    }
1716e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
1726e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    /**
1736e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk     * Attempts to resize the inner rectangle.  If this would cause it to leave
1746e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk     * the bounding rect, clips the inner rectangle to fit.
1756e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk     */
1766e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    public void resizeInner(RectF newInner) {
1776e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        Matrix m = getRotMatrix();
1786e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        Matrix m0 = getInverseRotMatrix();
1796e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
1806e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        float[] outerCorners = CropMath.getCornersFromRect(outer);
1816e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        m.mapPoints(outerCorners);
1826e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        float[] oldInnerCorners = CropMath.getCornersFromRect(inner);
1836e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        float[] newInnerCorners = CropMath.getCornersFromRect(newInner);
1846e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        RectF ret = new RectF(newInner);
1856e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
1866e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        for (int i = 0; i < newInnerCorners.length; i += 2) {
1876e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            float[] c = {
1886e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                    newInnerCorners[i], newInnerCorners[i + 1]
1896e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            };
1906e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            float[] c0 = Arrays.copyOf(c, 2);
1916e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            m0.mapPoints(c0);
1926e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            if (!CropMath.inclusiveContains(outer, c0[0], c0[1])) {
1936e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                float[] outerSide = CropMath.closestSide(c, outerCorners);
1946e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                float[] pathOfCorner = {
1956e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                        newInnerCorners[i], newInnerCorners[i + 1],
1966e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                        oldInnerCorners[i], oldInnerCorners[i + 1]
1976e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                };
198b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                float[] p = GeometryMathUtils.lineIntersect(pathOfCorner, outerSide);
1996e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                if (p == null) {
2006e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                    // lines are parallel or not well defined, so don't resize
2016e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                    p = new float[2];
2026e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                    p[0] = oldInnerCorners[i];
2036e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                    p[1] = oldInnerCorners[i + 1];
2046e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                }
2056e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                // relies on corners being in same order as method
2066e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                // getCornersFromRect
2076e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                switch (i) {
2086e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                    case 0:
2096e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                    case 1:
2106e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                        ret.left = (p[0] > ret.left) ? p[0] : ret.left;
2116e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                        ret.top = (p[1] > ret.top) ? p[1] : ret.top;
2126e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                        break;
2136e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                    case 2:
2146e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                    case 3:
2156e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                        ret.right = (p[0] < ret.right) ? p[0] : ret.right;
2166e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                        ret.top = (p[1] > ret.top) ? p[1] : ret.top;
2176e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                        break;
2186e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                    case 4:
2196e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                    case 5:
2206e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                        ret.right = (p[0] < ret.right) ? p[0] : ret.right;
2216e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                        ret.bottom = (p[1] < ret.bottom) ? p[1] : ret.bottom;
2226e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                        break;
2236e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                    case 6:
2246e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                    case 7:
2256e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                        ret.left = (p[0] > ret.left) ? p[0] : ret.left;
2266e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                        ret.bottom = (p[1] < ret.bottom) ? p[1] : ret.bottom;
2276e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                        break;
2286e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                    default:
2296e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                        break;
2306e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                }
2316e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            }
2326e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        }
2336e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        float[] retCorners = CropMath.getCornersFromRect(ret);
2346e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        m0.mapPoints(retCorners);
2356e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        innerRotated = retCorners;
2366e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        // reconstrain to update inner
2376e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        reconstrain();
2386e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    }
2396e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
2406e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    /**
2416e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk     * Attempts to resize the inner rectangle.  If this would cause it to leave
2426e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk     * the bounding rect, clips the inner rectangle to fit while maintaining
2436e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk     * aspect ratio.
2446e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk     */
2456e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    public void fixedAspectResizeInner(RectF newInner) {
2466e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        Matrix m = getRotMatrix();
2476e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        Matrix m0 = getInverseRotMatrix();
2486e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
2496e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        float aspectW = inner.width();
2506e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        float aspectH = inner.height();
2516e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        float aspRatio = aspectW / aspectH;
2526e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        float[] corners = CropMath.getCornersFromRect(outer);
2536e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
2546e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        m.mapPoints(corners);
2556e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        float[] oldInnerCorners = CropMath.getCornersFromRect(inner);
2566e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        float[] newInnerCorners = CropMath.getCornersFromRect(newInner);
2576e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
2586e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        // find fixed corner
2596e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        int fixed = -1;
2606e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        if (inner.top == newInner.top) {
2616e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            if (inner.left == newInner.left)
2626e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                fixed = 0; // top left
2636e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            else if (inner.right == newInner.right)
2646e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                fixed = 2; // top right
2656e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        } else if (inner.bottom == newInner.bottom) {
2666e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            if (inner.right == newInner.right)
2676e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                fixed = 4; // bottom right
2686e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            else if (inner.left == newInner.left)
2696e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                fixed = 6; // bottom left
2706e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        }
2716e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        // no fixed corner, return without update
2726e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        if (fixed == -1)
2736e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            return;
2746e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        float widthSoFar = newInner.width();
2756e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        int moved = -1;
2766e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        for (int i = 0; i < newInnerCorners.length; i += 2) {
2776e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            float[] c = {
2786e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                    newInnerCorners[i], newInnerCorners[i + 1]
2796e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            };
2806e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            float[] c0 = Arrays.copyOf(c, 2);
2816e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            m0.mapPoints(c0);
2826e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            if (!CropMath.inclusiveContains(outer, c0[0], c0[1])) {
2836e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                moved = i;
2846e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                if (moved == fixed)
2856e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                    continue;
2866e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                float[] l2 = CropMath.closestSide(c, corners);
2876e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                float[] l1 = {
2886e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                        newInnerCorners[i], newInnerCorners[i + 1],
2896e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                        oldInnerCorners[i], oldInnerCorners[i + 1]
2906e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                };
291b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk                float[] p = GeometryMathUtils.lineIntersect(l1, l2);
2926e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                if (p == null) {
2936e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                    // lines are parallel or not well defined, so set to old
2946e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                    // corner
2956e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                    p = new float[2];
2966e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                    p[0] = oldInnerCorners[i];
2976e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                    p[1] = oldInnerCorners[i + 1];
2986e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                }
2996e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                // relies on corners being in same order as method
3006e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                // getCornersFromRect
3016e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                float fixed_x = oldInnerCorners[fixed];
3026e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                float fixed_y = oldInnerCorners[fixed + 1];
3036e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                float newWidth = Math.abs(fixed_x - p[0]);
3046e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                float newHeight = Math.abs(fixed_y - p[1]);
3056e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                newWidth = Math.max(newWidth, aspRatio * newHeight);
3066e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                if (newWidth < widthSoFar)
3076e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                    widthSoFar = newWidth;
3086e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            }
3096e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        }
3106e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
3116e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        float heightSoFar = widthSoFar / aspRatio;
3126e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        RectF ret = new RectF(inner);
3136e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        if (fixed == 0) {
3146e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            ret.right = ret.left + widthSoFar;
3156e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            ret.bottom = ret.top + heightSoFar;
3166e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        } else if (fixed == 2) {
3176e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            ret.left = ret.right - widthSoFar;
3186e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            ret.bottom = ret.top + heightSoFar;
3196e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        } else if (fixed == 4) {
3206e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            ret.left = ret.right - widthSoFar;
3216e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            ret.top = ret.bottom - heightSoFar;
3226e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        } else if (fixed == 6) {
3236e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            ret.right = ret.left + widthSoFar;
3246e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            ret.top = ret.bottom - heightSoFar;
3256e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        }
3266e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        float[] retCorners = CropMath.getCornersFromRect(ret);
3276e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        m0.mapPoints(retCorners);
3286e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        innerRotated = retCorners;
3296e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        // reconstrain to update inner
3306e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        reconstrain();
3316e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    }
3326e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
3336e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    // internal methods
3346e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
3356e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    private boolean isConstrained() {
3366e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        for (int i = 0; i < 8; i += 2) {
3376e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk            if (!CropMath.inclusiveContains(outer, innerRotated[i], innerRotated[i + 1]))
3386e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk                return false;
3396e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        }
3406e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        return true;
3416e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    }
3426e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
3436e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    private void reconstrain() {
3446e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        // innerRotated has been changed to have incorrect values
3456e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        CropMath.getEdgePoints(outer, innerRotated);
3466e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        Matrix m = getRotMatrix();
3476e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        float[] unrotated = Arrays.copyOf(innerRotated, 8);
3486e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        m.mapPoints(unrotated);
3496e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        inner = CropMath.trapToRect(unrotated);
3506e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    }
3516e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
3526e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    private void rotateInner() {
3536e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        Matrix m = getInverseRotMatrix();
3546e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        m.mapPoints(innerRotated);
3556e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    }
3566e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
3576e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    private Matrix getRotMatrix() {
3586e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        Matrix m = new Matrix();
3596e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        m.setRotate(rot, outer.centerX(), outer.centerY());
3606e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        return m;
3616e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    }
3626e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk
3636e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    private Matrix getInverseRotMatrix() {
3646e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        Matrix m = new Matrix();
3656e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        m.setRotate(-rot, outer.centerX(), outer.centerY());
3666e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk        return m;
3676e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk    }
3686e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk}
369