ImageCrop.java revision f46da69aefd9afe0b4326a2fcea8e33c294136bb
18537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk/*
28537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk * Copyright (C) 2012 The Android Open Source Project
38537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk *
48537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk * Licensed under the Apache License, Version 2.0 (the "License");
58537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk * you may not use this file except in compliance with the License.
68537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk * You may obtain a copy of the License at
78537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk *
88537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk *      http://www.apache.org/licenses/LICENSE-2.0
98537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk *
108537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk * Unless required by applicable law or agreed to in writing, software
118537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk * distributed under the License is distributed on an "AS IS" BASIS,
128537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk * See the License for the specific language governing permissions and
148537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk * limitations under the License.
158537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk */
168537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
178537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkpackage com.android.gallery3d.filtershow.imageshow;
188537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
198537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.content.Context;
208537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.content.res.Resources;
218537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.graphics.Bitmap;
228537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.graphics.Canvas;
23c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroardimport android.graphics.Color;
248537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.graphics.Matrix;
258537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.graphics.Paint;
268537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.graphics.RectF;
278537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.graphics.drawable.Drawable;
288537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.util.AttributeSet;
298537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.util.Log;
30c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard
31e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroardimport com.android.gallery3d.R;
328537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
338537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkpublic class ImageCrop extends ImageGeometry {
348537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private static final boolean LOGV = false;
358537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private static final int MOVE_LEFT = 1;
368537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private static final int MOVE_TOP = 2;
378537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private static final int MOVE_RIGHT = 4;
388537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private static final int MOVE_BOTTOM = 8;
398537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private static final int MOVE_BLOCK = 16;
408537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
410f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    //Corners
420f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    private static final int TOP_LEFT = MOVE_TOP | MOVE_LEFT;
430f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    private static final int TOP_RIGHT = MOVE_TOP | MOVE_RIGHT;
440f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    private static final int BOTTOM_RIGHT = MOVE_BOTTOM | MOVE_RIGHT;
450f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    private static final int BOTTOM_LEFT = MOVE_BOTTOM | MOVE_LEFT;
460f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk
478537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private static final float MIN_CROP_WIDTH_HEIGHT = 0.1f;
48f46da69aefd9afe0b4326a2fcea8e33c294136bbRuben Brunk    private static int mTouchTolerance = 45;
498537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
5062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    private boolean mFirstDraw = true;
5162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    private float mAspectWidth = 1;
5262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    private float mAspectHeight = 1;
5362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    private boolean mFixAspectRatio = false;
548537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
55a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk    private float mLastRot = 0;
568537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private final Paint borderPaint;
578537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
588537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private int movingEdges;
598537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private final Drawable cropIndicator;
608537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private final int indicatorSize;
61104165f1f9858bdebb2d219b077f5227203f7c02nicolasroard    private final int mBorderColor = Color.argb(128, 255, 255, 255);
628537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
638537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private static final String LOGTAG = "ImageCrop";
648537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
658537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private static final Paint gPaint = new Paint();
668537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
678537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    public ImageCrop(Context context) {
688537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        super(context);
698537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        Resources resources = context.getResources();
70c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard        cropIndicator = resources.getDrawable(R.drawable.camera_crop);
718537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        indicatorSize = (int) resources.getDimension(R.dimen.crop_indicator_size);
728537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        borderPaint = new Paint();
738537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        borderPaint.setStyle(Paint.Style.STROKE);
74104165f1f9858bdebb2d219b077f5227203f7c02nicolasroard        borderPaint.setColor(mBorderColor);
758537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        borderPaint.setStrokeWidth(2f);
768537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
778537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
788537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    public ImageCrop(Context context, AttributeSet attrs) {
798537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        super(context, attrs);
808537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        Resources resources = context.getResources();
81c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard        cropIndicator = resources.getDrawable(R.drawable.camera_crop);
828537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        indicatorSize = (int) resources.getDimension(R.dimen.crop_indicator_size);
838537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        borderPaint = new Paint();
848537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        borderPaint.setStyle(Paint.Style.STROKE);
85104165f1f9858bdebb2d219b077f5227203f7c02nicolasroard        borderPaint.setColor(mBorderColor);
868537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        borderPaint.setStrokeWidth(2f);
878537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
888537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
89d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard    @Override
90d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard    public String getName() {
910a32b7afc5286a5c7aa334b9338591d61a49731fRuben Brunk        return getContext().getString(R.string.crop);
92d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard    }
93d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard
94a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk    private void swapAspect(){
95a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        float temp = mAspectWidth;
96a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        mAspectWidth = mAspectHeight;
97a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        mAspectHeight = temp;
98a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk    }
99a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk
100f46da69aefd9afe0b4326a2fcea8e33c294136bbRuben Brunk    public static void setTouchTolerance(int tolerance){
101f46da69aefd9afe0b4326a2fcea8e33c294136bbRuben Brunk        mTouchTolerance = tolerance;
102f46da69aefd9afe0b4326a2fcea8e33c294136bbRuben Brunk    }
103f46da69aefd9afe0b4326a2fcea8e33c294136bbRuben Brunk
1040f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    private boolean switchCropBounds(int moving_corner, RectF dst) {
1050f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        RectF crop = getCropBoundsDisplayed();
1060f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        float dx1 = 0;
1070f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        float dy1 = 0;
1080f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        float dx2 = 0;
1090f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        float dy2 = 0;
1100f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        if ((moving_corner & MOVE_RIGHT) != 0) {
1110f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            dx1 = mCurrentX - crop.right;
1120f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        } else if ((moving_corner & MOVE_LEFT) != 0) {
1130f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            dx1 = mCurrentX - crop.left;
1140f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        }
1150f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        if ((moving_corner & MOVE_BOTTOM) != 0) {
1160f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            dy1 = mCurrentY - crop.bottom;
1170f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        } else if ((moving_corner & MOVE_TOP) != 0) {
1180f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            dy1 = mCurrentY - crop.top;
1190f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        }
1200f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        RectF newCrop = null;
1210f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        //Fix opposite corner in place and move sides
1220f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        if (moving_corner == BOTTOM_RIGHT) {
1230f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            newCrop = new RectF(crop.left, crop.top, crop.left + crop.height(), crop.top
1240f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                    + crop.width());
1250f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        } else if (moving_corner == BOTTOM_LEFT) {
1260f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            newCrop = new RectF(crop.right - crop.height(), crop.top, crop.right, crop.top
1270f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                    + crop.width());
1280f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        } else if (moving_corner == TOP_LEFT) {
1290f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            newCrop = new RectF(crop.right - crop.height(), crop.bottom - crop.width(),
1300f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                    crop.right, crop.bottom);
1310f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        } else if (moving_corner == TOP_RIGHT) {
1320f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            newCrop = new RectF(crop.left, crop.bottom - crop.width(), crop.left
1330f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                    + crop.height(), crop.bottom);
1340f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        }
1350f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        if ((moving_corner & MOVE_RIGHT) != 0) {
1360f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            dx2 = mCurrentX - newCrop.right;
1370f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        } else if ((moving_corner & MOVE_LEFT) != 0) {
1380f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            dx2 = mCurrentX - newCrop.left;
1390f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        }
1400f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        if ((moving_corner & MOVE_BOTTOM) != 0) {
1410f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            dy2 = mCurrentY - newCrop.bottom;
1420f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        } else if ((moving_corner & MOVE_TOP) != 0) {
1430f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            dy2 = mCurrentY - newCrop.top;
1440f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        }
1450f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        if (Math.sqrt(dx1*dx1 + dy1*dy1) > Math.sqrt(dx2*dx2 + dy2*dy2)){
1460f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk             Matrix m = getCropBoundDisplayMatrix();
1470f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk             Matrix m0 = new Matrix();
1480f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk             if (!m.invert(m0)){
1490f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                 if (LOGV)
1500f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                     Log.v(LOGTAG, "FAILED TO INVERT CROP MATRIX");
1510f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                 return false;
1520f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk             }
1530f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk             if (!m0.mapRect(newCrop)){
1540f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                 if (LOGV)
1550f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                     Log.v(LOGTAG, "FAILED TO MAP RECTANGLE TO RECTANGLE");
1560f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                 return false;
1570f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk             }
158a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk             swapAspect();
1590f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk             dst.set(newCrop);
1600f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk             return true;
1610f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        }
1620f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        return false;
1630f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    }
1640f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk
1650f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    public void apply(float w, float h){
1660f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        mFixAspectRatio = true;
1670f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        mAspectWidth = w;
1680f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        mAspectHeight = h;
1690f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        setLocalCropBounds(getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
1700f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                getLocalStraighten()));
1710f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        cropSetup();
1720f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        saveAndSetPreset();
1730f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        invalidate();
1740f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    }
1750f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk
1760f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    public void applyOriginal() {
1770f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        mFixAspectRatio = true;
1780f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        RectF photobounds = getLocalPhotoBounds();
1790f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        float w = photobounds.width();
1800f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        float h = photobounds.height();
1810f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        float scale = Math.min(w, h);
1820f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        mAspectWidth = w / scale;
1830f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        mAspectHeight = h / scale;
1840f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        setLocalCropBounds(getUntranslatedStraightenCropBounds(photobounds,
1850f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                getLocalStraighten()));
1860f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        cropSetup();
1870f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        saveAndSetPreset();
1880f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        invalidate();
1890f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    }
1900f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk
1910f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    public void applyClear() {
1920f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        mFixAspectRatio = false;
1930f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        setLocalCropBounds(getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
1940f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                getLocalStraighten()));
1950f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        cropSetup();
1960f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        saveAndSetPreset();
1970f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        invalidate();
1980f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    }
1990f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk
2008537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private float getScaledMinWidthHeight() {
20162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        RectF disp = new RectF(0, 0, getWidth(), getHeight());
2028537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        float scaled = Math.min(disp.width(), disp.height()) * MIN_CROP_WIDTH_HEIGHT
20362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                / computeScale(getWidth(), getHeight());
2048537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        return scaled;
2058537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
2068537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
20762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    protected Matrix getCropRotationMatrix(float rotation, RectF localImage) {
20862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        Matrix m = getLocalGeoFlipMatrix(localImage.width(), localImage.height());
20962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        m.postRotate(rotation, localImage.centerX(), localImage.centerY());
2108537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        if (!m.rectStaysRect()) {
2118537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            return null;
2128537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
2138537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        return m;
2148537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
2158537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
2160f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    protected Matrix getCropBoundDisplayMatrix(){
2170f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
2180f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        if (m == null) {
2190f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            if (LOGV)
2200f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                Log.v(LOGTAG, "FAILED TO MAP CROP BOUNDS TO RECTANGLE");
2210f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            m = new Matrix();
2220f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        }
2230f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        float zoom = computeScale(getWidth(), getHeight());
2240f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        m.postTranslate(mXOffset, mYOffset);
2250f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        m.postScale(zoom, zoom, mCenterX, mCenterY);
2260f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        return m;
2270f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    }
2280f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk
2298537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected RectF getCropBoundsDisplayed() {
2308537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        RectF bounds = getLocalCropBounds();
2318537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        RectF crop = new RectF(bounds);
2328537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
2338537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
2348537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        if (m == null) {
2358537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            if (LOGV)
2368537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk                Log.v(LOGTAG, "FAILED TO MAP CROP BOUNDS TO RECTANGLE");
2378537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            m = new Matrix();
2388537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        } else {
2398537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            m.mapRect(crop);
2408537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
2418537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        m = new Matrix();
24262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float zoom = computeScale(getWidth(), getHeight());
2438537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        m.setScale(zoom, zoom, mCenterX, mCenterY);
2448537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        m.preTranslate(mXOffset, mYOffset);
2458537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        m.mapRect(crop);
2468537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        return crop;
2478537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
2488537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
2498537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private RectF getRotatedCropBounds() {
2508537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        RectF bounds = getLocalCropBounds();
2518537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        RectF crop = new RectF(bounds);
2528537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
2538537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
2548537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        if (m == null) {
2558537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            if (LOGV)
2568537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk                Log.v(LOGTAG, "FAILED TO MAP CROP BOUNDS TO RECTANGLE");
2578537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            return null;
2588537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        } else {
2598537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            m.mapRect(crop);
2608537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
2618537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        return crop;
2628537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
2638537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
26462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    private RectF getUnrotatedCropBounds(RectF cropBounds) {
26562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
26662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk
26762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        if (m == null) {
26862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            if (LOGV)
26962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                Log.v(LOGTAG, "FAILED TO GET ROTATION MATRIX");
27062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            return null;
27162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        }
27262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        Matrix m0 = new Matrix();
27362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        if (!m.invert(m0)) {
27462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            if (LOGV)
27562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                Log.v(LOGTAG, "FAILED TO INVERT ROTATION MATRIX");
27662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            return null;
27762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        }
27862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        RectF crop = new RectF(cropBounds);
27962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        if (!m0.mapRect(crop)) {
28062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            if (LOGV)
28162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                Log.v(LOGTAG, "FAILED TO UNROTATE CROPPING BOUNDS");
28262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            return null;
28362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        }
28462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        return crop;
28562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    }
28662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk
28762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    private RectF getRotatedStraightenBounds() {
28862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        RectF straightenBounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
28962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                getLocalStraighten());
29062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
29162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk
29262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        if (m == null) {
29362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            if (LOGV)
29462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                Log.v(LOGTAG, "FAILED TO MAP STRAIGHTEN BOUNDS TO RECTANGLE");
29562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            return null;
29662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        } else {
29762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            m.mapRect(straightenBounds);
29862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        }
29962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        return straightenBounds;
30062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    }
30162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk
3028537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    /**
3038537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk     * Sets cropped bounds; modifies the bounds if it's smaller than the allowed
3048537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk     * dimensions.
3058537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk     */
3068537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    public void setCropBounds(RectF bounds) {
3078537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        // Avoid cropping smaller than minimum width or height.
3088537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        RectF cbounds = new RectF(bounds);
3098537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        float minWidthHeight = getScaledMinWidthHeight();
31062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float aw = mAspectWidth;
31162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float ah = mAspectHeight;
31262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        if (mFixAspectRatio) {
31362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            minWidthHeight /= aw * ah;
31462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            int r = (int) (getLocalRotation() / 90);
31562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            if (r % 2 != 0) {
31662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                float temp = aw;
31762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                aw = ah;
31862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                ah = temp;
31962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            }
32062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        }
3218537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
3228537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        float newWidth = cbounds.width();
3238537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        float newHeight = cbounds.height();
32462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        if (mFixAspectRatio) {
32562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            if (newWidth < (minWidthHeight * aw) || newHeight < (minWidthHeight * ah)) {
32662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                newWidth = minWidthHeight * aw;
32762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                newHeight = minWidthHeight * ah;
32862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            }
32962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        } else {
33062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            if (newWidth < minWidthHeight) {
33162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                newWidth = minWidthHeight;
33262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            }
33362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            if (newHeight < minWidthHeight) {
33462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                newHeight = minWidthHeight;
33562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            }
3368537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
3378537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        RectF pbounds = getLocalPhotoBounds();
3388537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        if (pbounds.width() < minWidthHeight) {
3398537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            newWidth = pbounds.width();
3408537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
3418537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        if (pbounds.height() < minWidthHeight) {
3428537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            newHeight = pbounds.height();
3438537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
3448537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
3458537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        cbounds.set(cbounds.left, cbounds.top, cbounds.left + newWidth, cbounds.top + newHeight);
34662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        RectF straightenBounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
34762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                getLocalStraighten());
34862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        cbounds.intersect(straightenBounds);
349e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard
3508537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        if (mFixAspectRatio) {
35162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            fixAspectRatio(cbounds, aw, ah);
3528537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
35362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        setLocalCropBounds(cbounds);
3548537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        invalidate();
3558537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
3568537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
3578537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private void detectMovingEdges(float x, float y) {
3588537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        RectF cropped = getCropBoundsDisplayed();
3598537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        movingEdges = 0;
3608537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
3618537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        // Check left or right.
3628537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        float left = Math.abs(x - cropped.left);
3638537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        float right = Math.abs(x - cropped.right);
364f46da69aefd9afe0b4326a2fcea8e33c294136bbRuben Brunk        if ((left <= mTouchTolerance) && (left < right)) {
3658537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            movingEdges |= MOVE_LEFT;
3668537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
367f46da69aefd9afe0b4326a2fcea8e33c294136bbRuben Brunk        else if (right <= mTouchTolerance) {
3688537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            movingEdges |= MOVE_RIGHT;
3698537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
3708537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
3718537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        // Check top or bottom.
3728537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        float top = Math.abs(y - cropped.top);
3738537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        float bottom = Math.abs(y - cropped.bottom);
374f46da69aefd9afe0b4326a2fcea8e33c294136bbRuben Brunk        if ((top <= mTouchTolerance) & (top < bottom)) {
3758537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            movingEdges |= MOVE_TOP;
3768537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
377f46da69aefd9afe0b4326a2fcea8e33c294136bbRuben Brunk        else if (bottom <= mTouchTolerance) {
3788537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            movingEdges |= MOVE_BOTTOM;
3798537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
38062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        // Check inside block.
38162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        if (cropped.contains(x, y) && (movingEdges == 0)) {
38262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            movingEdges = MOVE_BLOCK;
38362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        }
3840f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        if (mFixAspectRatio && (movingEdges != MOVE_BLOCK)) {
3850f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            movingEdges = fixEdgeToCorner(movingEdges);
3860f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        }
3878537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        invalidate();
3888537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
3898537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
3900f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    private int fixEdgeToCorner(int moving_edges){
3910f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        if (moving_edges == MOVE_LEFT) {
3920f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            moving_edges |= MOVE_TOP;
3930f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        }
3940f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        if (moving_edges == MOVE_TOP) {
3950f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            moving_edges |= MOVE_LEFT;
3960f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        }
3970f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        if (moving_edges == MOVE_RIGHT) {
3980f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            moving_edges |= MOVE_BOTTOM;
3990f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        }
4000f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        if (moving_edges == MOVE_BOTTOM) {
4010f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            moving_edges |= MOVE_RIGHT;
4020f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        }
4030f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        return moving_edges;
4040f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    }
4050f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk
4060f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    private RectF fixedCornerResize(RectF r, int moving_corner, float dx, float dy){
4070f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        RectF newCrop = null;
4080f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        //Fix opposite corner in place and move sides
4090f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        if (moving_corner == BOTTOM_RIGHT) {
4100f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            newCrop = new RectF(r.left, r.top, r.left + r.width() + dx, r.top + r.height()
4110f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                    + dy);
4120f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        } else if (moving_corner == BOTTOM_LEFT) {
4130f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            newCrop = new RectF(r.right - r.width() + dx, r.top, r.right, r.top + r.height()
4140f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                    + dy);
4150f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        } else if (moving_corner == TOP_LEFT) {
4160f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            newCrop = new RectF(r.right - r.width() + dx, r.bottom - r.height() + dy,
4170f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                    r.right, r.bottom);
4180f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        } else if (moving_corner == TOP_RIGHT) {
4190f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            newCrop = new RectF(r.left, r.bottom - r.height() + dy, r.left
4200f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                    + r.width() + dx, r.bottom);
4210f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        }
4220f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        return newCrop;
4230f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    }
4240f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk
4258537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private void moveEdges(float dX, float dY) {
4268537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        RectF cropped = getRotatedCropBounds();
4278537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        float minWidthHeight = getScaledMinWidthHeight();
42862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float scale = computeScale(getWidth(), getHeight());
4298537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        float deltaX = dX / scale;
4308537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        float deltaY = dY / scale;
43162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        int select = movingEdges;
43262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        if (mFixAspectRatio && (select != MOVE_BLOCK)) {
4330f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            if (select == MOVE_LEFT) {
43462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                select |= MOVE_TOP;
43562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            }
4360f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            if (select == MOVE_TOP) {
43762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                select |= MOVE_LEFT;
43862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            }
4390f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            if (select == MOVE_RIGHT) {
44062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                select |= MOVE_BOTTOM;
44162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            }
4420f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            if (select == MOVE_BOTTOM) {
44362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                select |= MOVE_RIGHT;
4440f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            }
4450f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            RectF blank = new RectF();
4460f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            if(switchCropBounds(select, blank)){
4470f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                setCropBounds(blank);
4480f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                return;
44962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            }
45062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        }
45162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk
45262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        if (select == MOVE_BLOCK) {
45362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            RectF straight = getRotatedStraightenBounds();
45462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            // Move the whole cropped bounds within the photo display bounds.
45562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            deltaX = (deltaX > 0) ? Math.min(straight.right - cropped.right, deltaX)
45662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                    : Math.max(straight.left - cropped.left, deltaX);
45762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            deltaY = (deltaY > 0) ? Math.min(straight.bottom - cropped.bottom, deltaY)
45862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                    : Math.max(straight.top - cropped.top, deltaY);
45962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            cropped.offset(deltaX, deltaY);
4608537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        } else {
46162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            float dx = 0;
46262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            float dy = 0;
4630f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk
46462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            if ((select & MOVE_LEFT) != 0) {
46562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                dx = Math.min(cropped.left + deltaX, cropped.right - minWidthHeight) - cropped.left;
46662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            }
46762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            if ((select & MOVE_TOP) != 0) {
46862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                dy = Math.min(cropped.top + deltaY, cropped.bottom - minWidthHeight) - cropped.top;
46962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            }
47062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            if ((select & MOVE_RIGHT) != 0) {
47162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                dx = Math.max(cropped.right + deltaX, cropped.left + minWidthHeight)
47262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                        - cropped.right;
4738537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            }
47462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            if ((select & MOVE_BOTTOM) != 0) {
47562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                dy = Math.max(cropped.bottom + deltaY, cropped.top + minWidthHeight)
47662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                        - cropped.bottom;
4778537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            }
47862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk
47962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            if (mFixAspectRatio) {
4800f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                RectF crop = getCropBoundsDisplayed();
4810f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                float [] l1 = {crop.left, crop.bottom};
4820f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                float [] l2 = {crop.right, crop.top};
4830f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                if(movingEdges == TOP_LEFT || movingEdges == BOTTOM_RIGHT){
4840f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                    l1[1] = crop.top;
4850f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                    l2[1] = crop.bottom;
4860f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                }
4870f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                float[] b = { l1[0] - l2[0], l1[1] - l2[1] };
4880f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                float[] disp = {dx, dy};
4890f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                float[] bUnit = GeometryMath.normalize(b);
4900f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                float sp = GeometryMath.scalarProjection(disp, bUnit);
4910f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                dx = sp * bUnit[0];
4920f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                dy = sp * bUnit[1];
4930f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                RectF newCrop = fixedCornerResize(crop, select, dx * scale, dy * scale);
4940f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                Matrix m = getCropBoundDisplayMatrix();
4950f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                Matrix m0 = new Matrix();
4960f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                if (!m.invert(m0)){
4970f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                    if (LOGV)
4980f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                        Log.v(LOGTAG, "FAILED TO INVERT CROP MATRIX");
4990f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                    return;
5000f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                }
5010f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                if (!m0.mapRect(newCrop)){
5020f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                    if (LOGV)
5030f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                        Log.v(LOGTAG, "FAILED TO MAP RECTANGLE TO RECTANGLE");
5040f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                    return;
5050f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                }
5060f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                setCropBounds(newCrop);
5070f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                return;
5080f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            } else {
5090f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                if ((select & MOVE_LEFT) != 0) {
5100f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                    cropped.left += dx;
5110f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                }
5120f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                if ((select & MOVE_TOP) != 0) {
5130f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                    cropped.top += dy;
5140f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                }
5150f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                if ((select & MOVE_RIGHT) != 0) {
5160f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                    cropped.right += dx;
5170f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                }
5180f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                if ((select & MOVE_BOTTOM) != 0) {
5190f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk                    cropped.bottom += dy;
52062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                }
5218537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            }
5228537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
5230f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        movingEdges = select;
5248537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
5258537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        Matrix m0 = new Matrix();
5268537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        if (!m.invert(m0)) {
5278537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            if (LOGV)
5288537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk                Log.v(LOGTAG, "FAILED TO INVERT ROTATION MATRIX");
5298537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
5308537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        if (!m0.mapRect(cropped)) {
5318537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            if (LOGV)
5328537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk                Log.v(LOGTAG, "FAILED TO UNROTATE CROPPING BOUNDS");
5338537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
5348537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        setCropBounds(cropped);
5358537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
5368537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
5378537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private void drawIndicator(Canvas canvas, Drawable indicator, float centerX, float centerY) {
5388537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        int left = (int) centerX - indicatorSize / 2;
5398537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        int top = (int) centerY - indicatorSize / 2;
5408537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        indicator.setBounds(left, top, left + indicatorSize, top + indicatorSize);
5418537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        indicator.draw(canvas);
5428537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
5438537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
5448537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    @Override
5458537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected void setActionDown(float x, float y) {
5468537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        super.setActionDown(x, y);
5478537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        detectMovingEdges(x, y);
54862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    }
54962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk
55062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    @Override
55162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    protected void setActionUp() {
55262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        super.setActionUp();
55362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        movingEdges = 0;
5548537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
5558537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
5568537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    @Override
5578537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected void setActionMove(float x, float y) {
5580f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        if (movingEdges != 0){
5598537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            moveEdges(x - mCurrentX, y - mCurrentY);
5600f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        }
56162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        super.setActionMove(x, y);
56262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    }
56362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk
56462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    private void cropSetup() {
56562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        if (mFixAspectRatio) {
56662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            RectF cb = getRotatedCropBounds();
56762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            fixAspectRatio(cb, mAspectWidth, mAspectHeight);
56862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            RectF cb0 = getUnrotatedCropBounds(cb);
56962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            setCropBounds(cb0);
5708537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        } else {
57162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            setCropBounds(getLocalCropBounds());
5728537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
5738537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
5748537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
5758537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    @Override
5768537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected void gainedVisibility() {
577a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        float rot = getLocalRotation();
578a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        // if has changed orientation via rotate
579a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        if( ((int) ((rot - mLastRot) / 90)) % 2 != 0 ){
580a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk            swapAspect();
581a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        }
58262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        cropSetup();
58362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        mFirstDraw = true;
5848537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
5858537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
58662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    @Override
58762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    public void resetParameter() {
58862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        super.resetParameter();
58962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        cropSetup();
59062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    }
591e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard
59262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    @Override
59362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    protected void lostVisibility() {
594a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        mLastRot = getLocalRotation();
5958537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
5968537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
597a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk    private void drawRuleOfThird(Canvas canvas, RectF bounds, Paint p) {
598104165f1f9858bdebb2d219b077f5227203f7c02nicolasroard        float stepX = bounds.width() / 3.0f;
599104165f1f9858bdebb2d219b077f5227203f7c02nicolasroard        float stepY = bounds.height() / 3.0f;
600104165f1f9858bdebb2d219b077f5227203f7c02nicolasroard        float x = bounds.left + stepX;
601104165f1f9858bdebb2d219b077f5227203f7c02nicolasroard        float y = bounds.top + stepY;
602104165f1f9858bdebb2d219b077f5227203f7c02nicolasroard        for (int i = 0; i < 2; i++) {
603a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk            canvas.drawLine(x, bounds.top, x, bounds.bottom, p);
604104165f1f9858bdebb2d219b077f5227203f7c02nicolasroard            x += stepX;
605104165f1f9858bdebb2d219b077f5227203f7c02nicolasroard        }
606104165f1f9858bdebb2d219b077f5227203f7c02nicolasroard        for (int j = 0; j < 2; j++) {
607a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk            canvas.drawLine(bounds.left, y, bounds.right, y, p);
608104165f1f9858bdebb2d219b077f5227203f7c02nicolasroard            y += stepY;
609104165f1f9858bdebb2d219b077f5227203f7c02nicolasroard        }
610104165f1f9858bdebb2d219b077f5227203f7c02nicolasroard    }
611104165f1f9858bdebb2d219b077f5227203f7c02nicolasroard
6128537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    @Override
6138537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected void drawShape(Canvas canvas, Bitmap image) {
61462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        // TODO: move style to xml
6158537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        gPaint.setAntiAlias(true);
6168537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        gPaint.setFilterBitmap(true);
6178537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        gPaint.setDither(true);
6188537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        gPaint.setARGB(255, 255, 255, 255);
6198537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
62062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        if (mFirstDraw) {
62162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            cropSetup();
62262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            mFirstDraw = false;
62362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        }
624e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard        float rotation = getLocalRotation();
625a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk
626a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        RectF crop = drawTransformed(canvas, image, gPaint);
627a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        gPaint.setColor(mBorderColor);
628a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        gPaint.setStrokeWidth(3);
629a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        gPaint.setStyle(Paint.Style.STROKE);
630a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        drawRuleOfThird(canvas, crop, gPaint);
63162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk
632104165f1f9858bdebb2d219b077f5227203f7c02nicolasroard        gPaint.setColor(mBorderColor);
63362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        gPaint.setStrokeWidth(3);
63462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        gPaint.setStyle(Paint.Style.STROKE);
63562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        drawStraighten(canvas, gPaint);
636a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk
63762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        int decoded_moving = decoder(movingEdges, rotation);
63862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        canvas.save();
63962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        canvas.rotate(rotation, mCenterX, mCenterY);
640a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        RectF scaledCrop = unrotatedCropBounds();
64162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        boolean notMoving = decoded_moving == 0;
64262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        if (((decoded_moving & MOVE_TOP) != 0) || notMoving) {
643e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard            drawIndicator(canvas, cropIndicator, scaledCrop.centerX(), scaledCrop.top);
6448537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
64562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        if (((decoded_moving & MOVE_BOTTOM) != 0) || notMoving) {
646e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard            drawIndicator(canvas, cropIndicator, scaledCrop.centerX(), scaledCrop.bottom);
6478537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
64862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        if (((decoded_moving & MOVE_LEFT) != 0) || notMoving) {
649e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard            drawIndicator(canvas, cropIndicator, scaledCrop.left, scaledCrop.centerY());
6508537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
65162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        if (((decoded_moving & MOVE_RIGHT) != 0) || notMoving) {
652e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard            drawIndicator(canvas, cropIndicator, scaledCrop.right, scaledCrop.centerY());
6538537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
65462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        canvas.restore();
65562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    }
65662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk
6570f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    private int bitCycleLeft(int x, int times, int d) {
65862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        int mask = (1 << d) - 1;
65962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        int mout = x & mask;
66062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        times %= d;
66162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        int hi = mout >> (d - times);
66262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        int low = (mout << times) & mask;
66362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        int ret = x & ~mask;
66462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        ret |= low;
66562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        ret |= hi;
66662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        return ret;
6678537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
6688537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
66962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    protected int decoder(int movingEdges, float rotation) {
67062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        int rot = constrainedRotation(rotation);
6710f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        switch (rot) {
67262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            case 90:
67362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                return bitCycleLeft(movingEdges, 3, 4);
67462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            case 180:
67562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                return bitCycleLeft(movingEdges, 2, 4);
67662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            case 270:
67762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                return bitCycleLeft(movingEdges, 1, 4);
67862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            default:
67962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                return movingEdges;
68062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        }
68162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    }
6820f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk}
683