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.graphics.Bitmap;
218537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.graphics.Canvas;
22e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroardimport android.graphics.Color;
238537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.graphics.Matrix;
248537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.graphics.Paint;
25e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroardimport android.graphics.Paint.Style;
268537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.graphics.Path;
278537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.graphics.RectF;
288537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.util.AttributeSet;
298537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.view.MotionEvent;
308537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.view.View;
318537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
328537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport com.android.gallery3d.filtershow.imageshow.GeometryMetadata.FLIP;
338537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport com.android.gallery3d.filtershow.presets.ImagePreset;
348537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
358537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkpublic abstract class ImageGeometry extends ImageSlave {
368537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private boolean mVisibilityGained = false;
378537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private boolean mHasDrawn = false;
388537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
398537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected static final float MAX_STRAIGHTEN_ANGLE = 45;
408537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected static final float MIN_STRAIGHTEN_ANGLE = -45;
418537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
428537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected float mCenterX;
438537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected float mCenterY;
448537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
458537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected float mCurrentX;
468537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected float mCurrentY;
478537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected float mTouchCenterX;
488537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected float mTouchCenterY;
498537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
508537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    // Local geometry data
5162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    private GeometryMetadata mLocalGeometry = null;
528537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private RectF mLocalDisplayBounds = null;
538537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected float mXOffset = 0;
548537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected float mYOffset = 0;
558537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
568537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected enum MODES {
578537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        NONE, DOWN, UP, MOVE
588537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
598537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
608537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected MODES mMode = MODES.NONE;
618537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
628537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private static final String LOGTAG = "ImageGeometry";
638537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
648537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    public ImageGeometry(Context context, AttributeSet attrs) {
658537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        super(context, attrs);
668537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
678537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
688537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    public ImageGeometry(Context context) {
698537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        super(context);
708537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
718537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
728537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private void setupLocalDisplayBounds(RectF b) {
738537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        mLocalDisplayBounds = b;
748537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        calculateLocalScalingFactorAndOffset();
758537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
768537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
770f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    protected static float angleFor(float dx, float dy) {
780f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        return (float) (Math.atan2(dx, dy) * 180 / Math.PI);
790f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    }
800f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk
810f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    protected static int snappedAngle(float angle) {
820f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        float remainder = angle % 90;
830f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        int current = (int) (angle / 90); // truncates
840f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        if (remainder < -45) {
850f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            --current;
860f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        } else if (remainder > 45) {
870f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            ++current;
880f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        }
890f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        return current * 90;
900f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    }
910f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk
92eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk    protected float getCurrentTouchAngle() {
930f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        if (mCurrentX == mTouchCenterX && mCurrentY == mTouchCenterY) {
940f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            return 0;
950f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        }
960f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        float dX1 = mTouchCenterX - mCenterX;
970f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        float dY1 = mTouchCenterY - mCenterY;
980f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        float dX2 = mCurrentX - mCenterX;
990f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        float dY2 = mCurrentY - mCenterY;
1000f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk
1010f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        float angleA = angleFor(dX1, dY1);
1020f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        float angleB = angleFor(dX2, dY2);
1030f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk        return (angleB - angleA) % 360;
1040f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk    }
1050f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk
10662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    protected float computeScale(float width, float height) {
10762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float imageWidth = mLocalGeometry.getPhotoBounds().width();
10862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float imageHeight = mLocalGeometry.getPhotoBounds().height();
109eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk        return GeometryMath.scale(imageWidth, imageHeight, width, height);
110e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard    }
111e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard
1128537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    private void calculateLocalScalingFactorAndOffset() {
11362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        if (mLocalGeometry == null || mLocalDisplayBounds == null)
1148537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            return;
11562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        RectF imageBounds = mLocalGeometry.getPhotoBounds();
1168537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        float imageWidth = imageBounds.width();
1178537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        float imageHeight = imageBounds.height();
1188537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        float displayWidth = mLocalDisplayBounds.width();
1198537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        float displayHeight = mLocalDisplayBounds.height();
1208537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
1218537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        mCenterX = displayWidth / 2;
1228537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        mCenterY = displayHeight / 2;
1238537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        mYOffset = (displayHeight - imageHeight) / 2.0f;
1248537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        mXOffset = (displayWidth - imageWidth) / 2.0f;
12562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        updateScale();
1268537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
1278537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
1288537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    @Override
1298537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    public void resetParameter() {
1308537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        super.resetParameter();
1318537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        setLocalRotation(0);
1328537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        setLocalStraighten(0);
1338537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        setLocalCropBounds(getLocalPhotoBounds());
1348537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        setLocalFlip(FLIP.NONE);
1358537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        saveAndSetPreset();
1368537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        invalidate();
1378537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
1388537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
1398537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    // Overwrites local with master
1408537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected void syncLocalToMasterGeometry() {
14162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        mLocalGeometry = getMaster().getGeometry();
1428537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        calculateLocalScalingFactorAndOffset();
1438537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
1448537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
1458537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected RectF getLocalPhotoBounds() {
14662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        return mLocalGeometry.getPhotoBounds();
1478537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
1488537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
1498537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected RectF getLocalCropBounds() {
150b470b229fbfd782db6758f331e4a6f918e305b02nicolasroard        return mLocalGeometry.getPreviewCropBounds();
1518537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
1528537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
1538537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected RectF getLocalDisplayBounds() {
1548537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        return new RectF(mLocalDisplayBounds);
1558537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
1568537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
1578537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected float getLocalScale() {
15862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        return mLocalGeometry.getScaleFactor();
1598537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
1608537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
1618537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected float getLocalRotation() {
16262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        return mLocalGeometry.getRotation();
1638537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
1648537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
1658537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected float getLocalStraighten() {
16662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        return mLocalGeometry.getStraightenRotation();
1678537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
1688537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
1698537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected void setLocalScale(float s) {
17062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        mLocalGeometry.setScaleFactor(s);
1718537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
1728537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
17362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    protected void updateScale() {
17462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        RectF bounds = getUntranslatedStraightenCropBounds(mLocalGeometry.getPhotoBounds(),
175e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard                getLocalStraighten());
176e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard        float zoom = computeScale(bounds.width(), bounds.height());
177e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard        setLocalScale(zoom);
178e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard    }
179e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard
1808537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected void setLocalRotation(float r) {
18162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        mLocalGeometry.setRotation(r);
18246163a396828fa000b4a53cf87811e4111e3cb88Ruben Brunk        updateScale();
18346163a396828fa000b4a53cf87811e4111e3cb88Ruben Brunk    }
18446163a396828fa000b4a53cf87811e4111e3cb88Ruben Brunk
18562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    /**
18662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk     * Constrains rotation to be in [0, 90, 180, 270].
18762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk     */
18862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    protected int constrainedRotation(float rotation) {
18962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        int r = (int) ((rotation % 360) / 90);
19062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        r = (r < 0) ? (r + 4) : r;
19162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        return r * 90;
19262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    }
19362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk
19462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    protected Matrix getLocalGeoFlipMatrix(float width, float height) {
19562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        return mLocalGeometry.getFlipMatrix(width, height);
1968537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
1978537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
1988537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected void setLocalStraighten(float r) {
19962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        mLocalGeometry.setStraightenRotation(r);
20046163a396828fa000b4a53cf87811e4111e3cb88Ruben Brunk        updateScale();
2018537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
2028537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
2038537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected void setLocalCropBounds(RectF c) {
20462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        mLocalGeometry.setCropBounds(c);
20562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        updateScale();
2068537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
2078537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
2088537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected FLIP getLocalFlip() {
20962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        return mLocalGeometry.getFlipType();
2108537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
2118537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
2128537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected void setLocalFlip(FLIP flip) {
21362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        mLocalGeometry.setFlipType(flip);
2148537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
2158537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
2168537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected float getTotalLocalRotation() {
2178537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        return getLocalRotation() + getLocalStraighten();
2188537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
2198537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
2208537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected static float[] getCornersFromRect(RectF r) {
2218537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        // Order is:
2228537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        // 0------->1
22362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        // ^        |
22462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        // |        v
2258537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        // 3<-------2
2268537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        float[] corners = {
2278537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk                r.left, r.top, // 0
2288537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk                r.right, r.top, // 1
2298537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk                r.right, r.bottom,// 2
23062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                r.left, r.bottom // 3
231e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard        };
2328537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        return corners;
2338537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
2348537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
2358537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    // If edge point [x, y] in array [x0, y0, x1, y1, ...] is outside of the
2368537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    // image bound rectangle, clamps it to the edge of the rectangle.
2378537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected static void getEdgePoints(RectF imageBound, float[] array) {
2388537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        if (array.length < 2)
2398537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            return;
2408537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        for (int x = 0; x < array.length; x += 2) {
2410f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            array[x] = GeometryMath.clamp(array[x], imageBound.left, imageBound.right);
2420f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk            array[x + 1] = GeometryMath.clamp(array[x + 1], imageBound.top, imageBound.bottom);
2438537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
2448537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
2458537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
2468537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected static Path drawClosedPath(Canvas canvas, Paint paint, float[] points) {
2478537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        Path crop = new Path();
2488537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        crop.moveTo(points[0], points[1]);
2498537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        crop.lineTo(points[2], points[3]);
2508537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        crop.lineTo(points[4], points[5]);
2518537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        crop.lineTo(points[6], points[7]);
2528537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        crop.close();
2538537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        canvas.drawPath(crop, paint);
2548537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        return crop;
2558537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
2568537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
2578537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected static void fixAspectRatio(RectF r, float w, float h) {
2588537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        float scale = Math.min(r.width() / w, r.height() / h);
25962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float centX = r.centerX();
26062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float centY = r.centerY();
26162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float hw = scale * w / 2;
26262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float hh = scale * h / 2;
26362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        r.set(centX - hw, centY - hh, centX + hw, centY + hh);
26462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk
2658537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
2668537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
2678537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected static float getNewHeightForWidthAspect(float width, float w, float h) {
2688537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        return width * h / w;
2698537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
2708537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
2718537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected static float getNewWidthForHeightAspect(float height, float w, float h) {
2728537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        return height * w / h;
2738537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
2748537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
2758537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    @Override
2768537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected void onVisibilityChanged(View changedView, int visibility) {
2778537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        super.onVisibilityChanged(changedView, visibility);
2788537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        if (visibility == View.VISIBLE) {
2798537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            mVisibilityGained = true;
2808537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            syncLocalToMasterGeometry();
28162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            updateScale();
2828537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            gainedVisibility();
2838537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        } else {
2848537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            if (mVisibilityGained == true && mHasDrawn == true) {
2858537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk                lostVisibility();
2868537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            }
2878537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            mVisibilityGained = false;
2888537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            mHasDrawn = false;
2898537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
2908537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
2918537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
2928537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected void gainedVisibility() {
2938537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        // TODO: Override this stub.
2948537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
2958537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
2968537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected void lostVisibility() {
2978537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        // TODO: Override this stub.
2988537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
2998537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
3008537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    @Override
3018537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
3028537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        super.onSizeChanged(w, h, oldw, oldh);
3038537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        setupLocalDisplayBounds(new RectF(0, 0, w, h));
3048537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
3058537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
3068537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    @Override
3078537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    public boolean onTouchEvent(MotionEvent event) {
3088537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        switch (event.getActionMasked()) {
3098537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            case (MotionEvent.ACTION_DOWN):
3108537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk                setActionDown(event.getX(), event.getY());
3118537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk                break;
3128537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            case (MotionEvent.ACTION_UP):
3138537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk                setActionUp();
3148537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk                saveAndSetPreset();
3158537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk                break;
3168537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            case (MotionEvent.ACTION_MOVE):
3178537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk                setActionMove(event.getX(), event.getY());
3188537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk                break;
3198537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            default:
3208537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk                setNoAction();
3218537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
3228537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        if (getPanelController() != null) {
323e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard            getPanelController().onNewValue(getLocalValue());
3248537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
3258537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        invalidate();
3268537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        return true;
3278537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
3288537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
3298537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected int getLocalValue() {
3308537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        return 0; // TODO: Override this
3318537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
3328537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
3338537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected void setActionDown(float x, float y) {
3348537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        mTouchCenterX = x;
3358537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        mTouchCenterY = y;
3368537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        mCurrentX = x;
3378537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        mCurrentY = y;
3388537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        mMode = MODES.DOWN;
3398537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
3408537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
3418537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected void setActionMove(float x, float y) {
3428537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        mCurrentX = x;
3438537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        mCurrentY = y;
3448537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        mMode = MODES.MOVE;
3458537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
3468537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
3478537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected void setActionUp() {
3488537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        mMode = MODES.UP;
3498537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
3508537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
3518537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected void setNoAction() {
3528537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        mMode = MODES.NONE;
3538537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
3548537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
3558537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    @Override
3568537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    public boolean showTitle() {
3578537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        return false;
3588537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
3598537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
360d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard    public String getName() {
361d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard        return "Geometry";
362d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard    }
363d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard
3647f999551167ce313e36ba35e3682593f8cf80e52Ruben Brunk    public void saveAndSetPreset() {
365d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard        ImagePreset lastHistoryItem = getHistory().getLast();
366d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard        if (lastHistoryItem != null && lastHistoryItem.historyName().equalsIgnoreCase(getName())) {
367d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard            getImagePreset().setGeometry(mLocalGeometry);
36831529940021b9a18611b1a3fb4a0317ab8c89618nicolasroard            resetImageCaches(this);
369d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard        } else {
370a2a4c9a985d789e853211f21cd265460f16d2de1John Hoford            if (mLocalGeometry.hasModifications()) {
371a2a4c9a985d789e853211f21cd265460f16d2de1John Hoford                ImagePreset copy = new ImagePreset(getImagePreset());
372a2a4c9a985d789e853211f21cd265460f16d2de1John Hoford                copy.setGeometry(mLocalGeometry);
373a2a4c9a985d789e853211f21cd265460f16d2de1John Hoford                copy.setHistoryName(getName());
374a2a4c9a985d789e853211f21cd265460f16d2de1John Hoford                copy.setIsFx(false);
375a2a4c9a985d789e853211f21cd265460f16d2de1John Hoford                setImagePreset(copy, true);
376a2a4c9a985d789e853211f21cd265460f16d2de1John Hoford            }
377d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard        }
378d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard        invalidate();
3798537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
3808537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
381a66df0c9e8106ef6a275e20ab2a47286e054fe7cRuben Brunk    public static RectF getUntranslatedStraightenCropBounds(RectF imageRect, float straightenAngle) {
3828537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        float deg = straightenAngle;
3838537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        if (deg < 0) {
3848537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            deg = -deg;
3858537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
3868537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        double a = Math.toRadians(deg);
3878537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        double sina = Math.sin(a);
3888537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        double cosa = Math.cos(a);
3898537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
3908537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        double rw = imageRect.width();
3918537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        double rh = imageRect.height();
3928537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        double h1 = rh * rh / (rw * sina + rh * cosa);
3938537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        double h2 = rh * rw / (rw * cosa + rh * sina);
3948537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        double hh = Math.min(h1, h2);
3958537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        double ww = hh * rw / rh;
3968537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
3978537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        float left = (float) ((rw - ww) * 0.5f);
3988537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        float top = (float) ((rh - hh) * 0.5f);
3998537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        float right = (float) (left + ww);
4008537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        float bottom = (float) (top + hh);
4018537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
402a66df0c9e8106ef6a275e20ab2a47286e054fe7cRuben Brunk        return new RectF(left, top, right, bottom);
4038537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
4048537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
40562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    protected Matrix getGeoMatrix(RectF r, boolean onlyRotate) {
406eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk        RectF pbounds = getLocalPhotoBounds();
407eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk        float scale = GeometryMath
408eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk                .scale(pbounds.width(), pbounds.height(), getWidth(), getHeight());
409eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk        if (((int) (getLocalRotation() / 90)) % 2 != 0) {
410eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk            scale = GeometryMath.scale(pbounds.width(), pbounds.height(), getHeight(), getWidth());
411eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk        }
41262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float yoff = getHeight() / 2;
41362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float xoff = getWidth() / 2;
41462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float w = r.left * 2 + r.width();
41562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float h = r.top * 2 + r.height();
41662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        return mLocalGeometry.buildGeometryMatrix(w, h, scale, xoff, yoff, onlyRotate);
41762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    }
41862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk
41962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    protected void drawImageBitmap(Canvas canvas, Bitmap bitmap, Paint paint, Matrix m) {
420e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard        canvas.save();
42162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        canvas.drawBitmap(bitmap, m, paint);
422e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard        canvas.restore();
423e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard    }
424e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard
42562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    protected void drawImageBitmap(Canvas canvas, Bitmap bitmap, Paint paint) {
42662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float scale = computeScale(getWidth(), getHeight());
42762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float yoff = getHeight() / 2;
42862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float xoff = getWidth() / 2;
42962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        Matrix m = mLocalGeometry.buildGeometryUIMatrix(scale, xoff, yoff);
43062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        drawImageBitmap(canvas, bitmap, paint, m);
431e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard    }
432e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard
43362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    protected RectF straightenBounds() {
434e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard        RectF bounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
435e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard                getLocalStraighten());
43662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        Matrix m = getGeoMatrix(bounds, true);
43762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        m.mapRect(bounds);
43862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        return bounds;
439e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard    }
440e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard
44162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    protected void drawStraighten(Canvas canvas, Paint paint) {
44262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        RectF bounds = straightenBounds();
44346163a396828fa000b4a53cf87811e4111e3cb88Ruben Brunk        canvas.save();
44462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        canvas.drawRect(bounds, paint);
44562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        canvas.restore();
44646163a396828fa000b4a53cf87811e4111e3cb88Ruben Brunk    }
44746163a396828fa000b4a53cf87811e4111e3cb88Ruben Brunk
44862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    protected RectF unrotatedCropBounds() {
44962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        RectF bounds = getLocalCropBounds();
45062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        RectF pbounds = getLocalPhotoBounds();
45162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float scale = computeScale(getWidth(), getHeight());
45262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float yoff = getHeight() / 2;
45362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float xoff = getWidth() / 2;
454eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk        Matrix m = mLocalGeometry.buildGeometryMatrix(pbounds.width(), pbounds.height(), scale,
455eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk                xoff, yoff, 0);
45662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        m.mapRect(bounds);
45762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        return bounds;
45862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    }
45962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk
46062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    protected RectF cropBounds() {
46162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        RectF bounds = getLocalCropBounds();
46262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        Matrix m = getGeoMatrix(getLocalPhotoBounds(), true);
46362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        m.mapRect(bounds);
46462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        return bounds;
46562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    }
46662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk
46762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    // Fails for non-90 degree
46862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    protected void drawCrop(Canvas canvas, Paint paint) {
46962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        RectF bounds = cropBounds();
470e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard        canvas.save();
471e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard        canvas.drawRect(bounds, paint);
472e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard        canvas.restore();
47362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    }
474e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard
47562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    protected void drawCropSafe(Canvas canvas, Paint paint) {
47662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        Matrix m = getGeoMatrix(getLocalPhotoBounds(), true);
47762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        RectF crop = getLocalCropBounds();
47862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        if (!m.rectStaysRect()) {
47962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            float[] corners = getCornersFromRect(crop);
48062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            m.mapPoints(corners);
48162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            drawClosedPath(canvas, paint, corners);
48262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        } else {
48362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            m.mapRect(crop);
48462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            Path path = new Path();
48562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            path.addRect(crop, Path.Direction.CCW);
48662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            canvas.drawPath(path, paint);
487e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard        }
488e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard    }
489e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard
49062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    protected void drawTransformedBitmap(Canvas canvas, Bitmap bitmap, Paint paint, boolean clip) {
49162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        paint.setARGB(255, 0, 0, 0);
49262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        drawImageBitmap(canvas, bitmap, paint);
49362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        paint.setColor(Color.WHITE);
49462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        paint.setStyle(Style.STROKE);
49562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        paint.setStrokeWidth(2);
49662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        drawCropSafe(canvas, paint);
4978d8cdf7f67db69309fd740ca301fbeaadac8a9f0nicolasroard        paint.setColor(getDefaultBackgroundColor());
49862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        paint.setStyle(Paint.Style.FILL);
49962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        drawShadows(canvas, paint, unrotatedCropBounds());
500e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard    }
501e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard
50262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    protected void drawShadows(Canvas canvas, Paint p, RectF innerBounds) {
50362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        RectF display = new RectF(0, 0, getWidth(), getHeight());
50462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        drawShadows(canvas, p, innerBounds, display, getLocalRotation(), getWidth() / 2,
50562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk                getHeight() / 2);
506e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard    }
507e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard
50862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk    protected static void drawShadows(Canvas canvas, Paint p, RectF innerBounds, RectF outerBounds,
50962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk            float rotation, float centerX, float centerY) {
51062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        canvas.save();
51162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        canvas.rotate(rotation, centerX, centerY);
512e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroard
51362e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float x = (outerBounds.left - outerBounds.right);
51462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float y = (outerBounds.top - outerBounds.bottom);
51562e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float longest = (float) Math.sqrt(x * x + y * y) / 2;
51662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float minX = centerX - longest;
51762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float maxX = centerX + longest;
51862e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float minY = centerY - longest;
51962e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        float maxY = centerY + longest;
52062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        canvas.drawRect(minX, minY, innerBounds.right, innerBounds.top, p);
52162e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        canvas.drawRect(minX, innerBounds.top, innerBounds.left, maxY, p);
52262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        canvas.drawRect(innerBounds.left, innerBounds.bottom, maxX, maxY,
5238537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk                p);
52462e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        canvas.drawRect(innerBounds.right, minY, maxX,
5258537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk                innerBounds.bottom, p);
52662e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        canvas.rotate(-rotation, centerX, centerY);
52762e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk        canvas.restore();
5288537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
5298537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
5308537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    @Override
5318537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    public void onDraw(Canvas canvas) {
5328537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        if (getDirtyGeometryFlag()) {
5338537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            syncLocalToMasterGeometry();
5348537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            clearDirtyGeometryFlag();
5358537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
53631529940021b9a18611b1a3fb4a0317ab8c89618nicolasroard        requestFilteredImages();
53731529940021b9a18611b1a3fb4a0317ab8c89618nicolasroard        Bitmap image = getMaster().getFiltersOnlyImage();
5388537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        if (image == null) {
53931529940021b9a18611b1a3fb4a0317ab8c89618nicolasroard            invalidate();
5408537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk            return;
5418537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        }
5428537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        mHasDrawn = true;
5438537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        drawShape(canvas, image);
5448537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
5458537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk
5468537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    protected void drawShape(Canvas canvas, Bitmap image) {
5478537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk        // TODO: Override this stub.
5488537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk    }
549a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk
550eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk    protected RectF drawTransformed(Canvas canvas, Bitmap photo, Paint p) {
551a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        p.setARGB(255, 0, 0, 0);
552a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        RectF photoBounds = getLocalPhotoBounds();
553a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        RectF cropBounds = getLocalCropBounds();
554a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        float scale = computeScale(getWidth(), getHeight());
555eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk        // checks if local rotation is an odd multiple of 90.
556eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk        if (((int) (getLocalRotation() / 90)) % 2 != 0) {
557eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk            scale = computeScale(getHeight(), getWidth());
558eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk        }
559a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        // put in screen coordinates
560a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        RectF scaledCrop = GeometryMath.scaleRect(cropBounds, scale);
561a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        RectF scaledPhoto = GeometryMath.scaleRect(photoBounds, scale);
562eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk        float[] displayCenter = {
563eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk                getWidth() / 2f, getHeight() / 2f
564eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk        };
565a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        Matrix m = GeometryMetadata.buildCenteredPhotoMatrix(scaledPhoto, scaledCrop,
566a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk                getLocalRotation(), getLocalStraighten(), getLocalFlip(), displayCenter);
567a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk
568a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        Matrix m1 = GeometryMetadata.buildWanderingCropMatrix(scaledPhoto, scaledCrop,
569a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk                getLocalRotation(), getLocalStraighten(), getLocalFlip(), displayCenter);
570a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        m1.mapRect(scaledCrop);
571a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        Path path = new Path();
572a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        path.addRect(scaledCrop, Path.Direction.CCW);
573a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk
574a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        m.preScale(scale, scale);
575a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        canvas.save();
576a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        canvas.drawBitmap(photo, m, p);
577a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        canvas.restore();
578a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk
579a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        p.setColor(Color.WHITE);
580a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        p.setStyle(Style.STROKE);
581a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        p.setStrokeWidth(2);
582a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        canvas.drawPath(path, p);
583a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        return scaledCrop;
584a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk    }
585a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk
586eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk    protected void drawTransformedCropped(Canvas canvas, Bitmap photo, Paint p) {
587a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        RectF photoBounds = getLocalPhotoBounds();
588a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        RectF cropBounds = getLocalCropBounds();
589a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        float imageWidth = cropBounds.width();
590a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        float imageHeight = cropBounds.height();
591eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk        float scale = GeometryMath.scale(imageWidth, imageHeight, getWidth(), getHeight());
592eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk        // checks if local rotation is an odd multiple of 90.
593eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk        if (((int) (getLocalRotation() / 90)) % 2 != 0) {
594eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk            scale = GeometryMath.scale(imageWidth, imageHeight, getHeight(), getWidth());
595a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        }
596a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        // put in screen coordinates
597a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        RectF scaledCrop = GeometryMath.scaleRect(cropBounds, scale);
598a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        RectF scaledPhoto = GeometryMath.scaleRect(photoBounds, scale);
599eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk        float[] displayCenter = {
600eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk                getWidth() / 2f, getHeight() / 2f
601eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk        };
602a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        Matrix m1 = GeometryMetadata.buildWanderingCropMatrix(scaledPhoto, scaledCrop,
603a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk                getLocalRotation(), getLocalStraighten(), getLocalFlip(), displayCenter);
604eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk        float[] cropCenter = {
605eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk                scaledCrop.centerX(), scaledCrop.centerY()
606eb75699bcd5762a9ffd7ee0d4d14a4e5eb2e2389Ruben Brunk        };
607a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        m1.mapPoints(cropCenter);
608a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        GeometryMetadata.concatRecenterMatrix(m1, cropCenter, displayCenter);
609a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        m1.preRotate(getLocalStraighten(), scaledPhoto.centerX(), scaledPhoto.centerY());
610a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        m1.preScale(scale, scale);
611a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk
612a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        p.setARGB(255, 0, 0, 0);
613a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        canvas.save();
614a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        canvas.drawBitmap(photo, m1, p);
615a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        canvas.restore();
616a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk
6178d8cdf7f67db69309fd740ca301fbeaadac8a9f0nicolasroard        p.setColor(getDefaultBackgroundColor());
618a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        p.setStyle(Paint.Style.FILL);
619a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        scaledCrop.offset(displayCenter[0] - scaledCrop.centerX(), displayCenter[1]
620a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk                - scaledCrop.centerY());
621a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk        drawShadows(canvas, p, scaledCrop);
622a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk    }
6238537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk}
624