18537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk/* 2b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk * Copyright (C) 2013 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; 238537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.graphics.Matrix; 248537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.graphics.Paint; 258537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.graphics.RectF; 268537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.graphics.drawable.Drawable; 278537d097f8827caedc8c39564de54d36eae8b16fRuben Brunkimport android.util.AttributeSet; 28b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport android.util.Log; 29b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport android.view.MotionEvent; 30c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard 31e533f65961ed601ded1803caeab6cef0a778d2f2nicolasroardimport com.android.gallery3d.R; 32b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport com.android.gallery3d.filtershow.crop.CropDrawingUtils; 336fe165b7d28299d5b2f97deb135b233d84eb300fRuben Brunkimport com.android.gallery3d.filtershow.crop.CropMath; 34b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport com.android.gallery3d.filtershow.crop.CropObject; 35d61a2f9fcaad0309132b6b9b666c3dc6df62fed7John Hofordimport com.android.gallery3d.filtershow.editors.EditorCrop; 36b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport com.android.gallery3d.filtershow.filters.FilterCropRepresentation; 37b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkimport com.android.gallery3d.filtershow.imageshow.GeometryMathUtils.GeometryHolder; 38b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk 39b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunkpublic class ImageCrop extends ImageShow { 40b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private static final String TAG = ImageCrop.class.getSimpleName(); 41b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private RectF mImageBounds = new RectF(); 42b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private RectF mScreenCropBounds = new RectF(); 43b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private Paint mPaint = new Paint(); 44b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private CropObject mCropObj = null; 45b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private GeometryHolder mGeometry = new GeometryHolder(); 46b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private GeometryHolder mUpdateHolder = new GeometryHolder(); 47b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private Drawable mCropIndicator; 48b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private int mIndicatorSize; 49b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private boolean mMovingBlock = false; 50b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private Matrix mDisplayMatrix = null; 51b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private Matrix mDisplayCropMatrix = null; 52b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private Matrix mDisplayMatrixInverse = null; 53b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private float mPrevX = 0; 54b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private float mPrevY = 0; 55b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private int mMinSideSize = 90; 56b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private int mTouchTolerance = 40; 57b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private enum Mode { 58b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk NONE, MOVE 59b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk } 60b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private Mode mState = Mode.NONE; 61b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private boolean mValidDraw = false; 62b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk FilterCropRepresentation mLocalRep = new FilterCropRepresentation(); 63b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk EditorCrop mEditorCrop; 648537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk 658537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk public ImageCrop(Context context) { 668537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk super(context); 67b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk setup(context); 688537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk } 698537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk 708537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk public ImageCrop(Context context, AttributeSet attrs) { 718537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk super(context, attrs); 72b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk setup(context); 738537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk } 748537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk 75b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk public ImageCrop(Context context, AttributeSet attrs, int defStyle) { 76b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk super(context, attrs, defStyle); 77b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk setup(context); 78a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk } 79a41224997ef9be9c0d04534f7b6b9c6b933bfe05Ruben Brunk 80b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private void setup(Context context) { 81b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk Resources rsc = context.getResources(); 82b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mCropIndicator = rsc.getDrawable(R.drawable.camera_crop); 83b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mIndicatorSize = (int) rsc.getDimension(R.dimen.crop_indicator_size); 84b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mMinSideSize = (int) rsc.getDimension(R.dimen.crop_min_side); 85b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mTouchTolerance = (int) rsc.getDimension(R.dimen.crop_touch_tolerance); 86f46da69aefd9afe0b4326a2fcea8e33c294136bbRuben Brunk } 87f46da69aefd9afe0b4326a2fcea8e33c294136bbRuben Brunk 88b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk public void setFilterCropRepresentation(FilterCropRepresentation crop) { 89b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mLocalRep = (crop == null) ? new FilterCropRepresentation() : crop; 90b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk GeometryMathUtils.initializeHolder(mUpdateHolder, mLocalRep); 91b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mValidDraw = true; 920f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk } 930f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk 94b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk public FilterCropRepresentation getFinalRepresentation() { 95b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk return mLocalRep; 966416dd59687768d4152d5d954dd0e8c3617b9d97Ruben Brunk } 976416dd59687768d4152d5d954dd0e8c3617b9d97Ruben Brunk 98b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private void internallyUpdateLocalRep(RectF crop, RectF image) { 99b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk FilterCropRepresentation 100b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk .findNormalizedCrop(crop, (int) image.width(), (int) image.height()); 101b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mGeometry.crop.set(crop); 102b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mUpdateHolder.set(mGeometry); 103b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mLocalRep.setCrop(crop); 1046416dd59687768d4152d5d954dd0e8c3617b9d97Ruben Brunk } 1056416dd59687768d4152d5d954dd0e8c3617b9d97Ruben Brunk 106b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk @Override 107b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk public boolean onTouchEvent(MotionEvent event) { 108b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk float x = event.getX(); 109b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk float y = event.getY(); 110b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk if (mDisplayMatrix == null || mDisplayMatrixInverse == null) { 111b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk return true; 112b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk } 113b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk float[] touchPoint = { 114b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk x, y 115b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk }; 116b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mDisplayMatrixInverse.mapPoints(touchPoint); 117b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk x = touchPoint[0]; 118b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk y = touchPoint[1]; 119b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk switch (event.getActionMasked()) { 120b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk case (MotionEvent.ACTION_DOWN): 121b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk if (mState == Mode.NONE) { 122b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk if (!mCropObj.selectEdge(x, y)) { 123b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mMovingBlock = mCropObj.selectEdge(CropObject.MOVE_BLOCK); 124b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk } 125b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mPrevX = x; 126b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mPrevY = y; 127b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mState = Mode.MOVE; 128b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk } 129b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk break; 130b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk case (MotionEvent.ACTION_UP): 131b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk if (mState == Mode.MOVE) { 132b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mCropObj.selectEdge(CropObject.MOVE_NONE); 133b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mMovingBlock = false; 134b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mPrevX = x; 135b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mPrevY = y; 136b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mState = Mode.NONE; 137b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds()); 138b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk } 139b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk break; 140b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk case (MotionEvent.ACTION_MOVE): 141b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk if (mState == Mode.MOVE) { 142b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk float dx = x - mPrevX; 143b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk float dy = y - mPrevY; 144b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mCropObj.moveCurrentSelection(dx, dy); 145b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mPrevX = x; 146b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mPrevY = y; 147b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk } 148b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk break; 149b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk default: 150b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk break; 151b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk } 1520f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk invalidate(); 153b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk return true; 1540f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk } 1550f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk 156b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private void clearDisplay() { 157b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mDisplayMatrix = null; 158b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mDisplayMatrixInverse = null; 1590f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk invalidate(); 1600f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk } 1610f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk 162b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk public void applyFreeAspect() { 163b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mCropObj.unsetAspectRatio(); 1640f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk invalidate(); 1650f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk } 1660f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk 167b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk public void applyOriginalAspect() { 168b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk RectF outer = mCropObj.getOuterBounds(); 169b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk float w = outer.width(); 170b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk float h = outer.height(); 171b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk if (w > 0 && h > 0) { 172b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk applyAspect(w, h); 173b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mCropObj.resetBoundsTo(outer, outer); 174b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds()); 1756416dd59687768d4152d5d954dd0e8c3617b9d97Ruben Brunk } else { 176b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk Log.w(TAG, "failed to set aspect ratio original"); 1776416dd59687768d4152d5d954dd0e8c3617b9d97Ruben Brunk } 1788537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk invalidate(); 1798537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk } 1808537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk 181b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk public void applyAspect(float x, float y) { 182b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk if (x <= 0 || y <= 0) { 183b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk throw new IllegalArgumentException("Bad arguments to applyAspect"); 1848537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk } 185b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk // If we are rotated by 90 degrees from horizontal, swap x and y 186b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk if (GeometryMathUtils.needsDimensionSwap(mGeometry.rotation)) { 187b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk float tmp = x; 188b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk x = y; 189b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk y = tmp; 19062e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk } 191b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk if (!mCropObj.setInnerAspectRatio(x, y)) { 192b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk Log.w(TAG, "failed to set aspect ratio"); 1930f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk } 194b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds()); 1958537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk invalidate(); 1968537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk } 1978537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk 198b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk /** 199b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk * Rotates first d bits in integer x to the left some number of times. 200b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk */ 201b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private int bitCycleLeft(int x, int times, int d) { 202b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk int mask = (1 << d) - 1; 203b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk int mout = x & mask; 204b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk times %= d; 205b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk int hi = mout >> (d - times); 206b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk int low = (mout << times) & mask; 207b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk int ret = x & ~mask; 208b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk ret |= low; 209b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk ret |= hi; 210b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk return ret; 2110f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk } 2120f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk 213b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk /** 214b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk * Find the selected edge or corner in screen coordinates. 215b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk */ 216b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private int decode(int movingEdges, float rotation) { 217b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk int rot = CropMath.constrainedRotation(rotation); 218b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk switch (rot) { 219b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk case 90: 220b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk return bitCycleLeft(movingEdges, 1, 4); 221b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk case 180: 222b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk return bitCycleLeft(movingEdges, 2, 4); 223b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk case 270: 224b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk return bitCycleLeft(movingEdges, 3, 4); 225b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk default: 226b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk return movingEdges; 2278537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk } 2288537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk } 2298537d097f8827caedc8c39564de54d36eae8b16fRuben Brunk 230b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk private void forceStateConsistency() { 231b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk MasterImage master = MasterImage.getImage(); 232b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk Bitmap image = master.getFiltersOnlyImage(); 233b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk int width = image.getWidth(); 234b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk int height = image.getHeight(); 235b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk if (mCropObj == null || !mUpdateHolder.equals(mGeometry) 236b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk || mImageBounds.width() != width || mImageBounds.height() != height 237b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk || !mLocalRep.getCrop().equals(mUpdateHolder.crop)) { 238b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mImageBounds.set(0, 0, width, height); 239b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mGeometry.set(mUpdateHolder); 240b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mLocalRep.setCrop(mUpdateHolder.crop); 241b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk RectF scaledCrop = new RectF(mUpdateHolder.crop); 242b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk FilterCropRepresentation.findScaledCrop(scaledCrop, width, height); 243b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mCropObj = new CropObject(mImageBounds, scaledCrop, (int) mUpdateHolder.straighten); 244b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mState = Mode.NONE; 245b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk clearDisplay(); 2460f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk } 2476e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk } 2486e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk 2496e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk @Override 2506e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk protected void onSizeChanged(int w, int h, int oldw, int oldh) { 251b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk super.onSizeChanged(w, h, oldw, oldh); 252b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk clearDisplay(); 25313b2fd3ab7ff65ac5c18b4c9de69062f3a549669Ruben Brunk } 25413b2fd3ab7ff65ac5c18b4c9de69062f3a549669Ruben Brunk 25513b2fd3ab7ff65ac5c18b4c9de69062f3a549669Ruben Brunk @Override 256b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk public void onDraw(Canvas canvas) { 257b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk Bitmap bitmap = MasterImage.getImage().getFiltersOnlyImage(); 25880b216d5c8e17e0358cf1508a1e177ccada8383fnicolasroard if (bitmap == null) { 25980b216d5c8e17e0358cf1508a1e177ccada8383fnicolasroard MasterImage.getImage().invalidateFiltersOnly(); 26080b216d5c8e17e0358cf1508a1e177ccada8383fnicolasroard } 261b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk if (!mValidDraw || bitmap == null) { 262b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk return; 2635d834ce0c65f89cf3f249f586b360c1a6d7ab99bRuben Brunk } 264b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk forceStateConsistency(); 265b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mImageBounds.set(0, 0, bitmap.getWidth(), bitmap.getHeight()); 266b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk // If display matrix doesn't exist, create it and its dependencies 267b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk if (mDisplayCropMatrix == null || mDisplayMatrix == null || mDisplayMatrixInverse == null) { 2684a5fd0627e5c3f3094127dfbd782cd96080482d4John Hoford mCropObj.unsetAspectRatio(); 269b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mDisplayMatrix = GeometryMathUtils.getFullGeometryToScreenMatrix(mGeometry, 270b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk bitmap.getWidth(), bitmap.getHeight(), canvas.getWidth(), canvas.getHeight()); 271b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk float straighten = mGeometry.straighten; 272b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mGeometry.straighten = 0; 273b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mDisplayCropMatrix = GeometryMathUtils.getFullGeometryToScreenMatrix(mGeometry, 274b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk bitmap.getWidth(), bitmap.getHeight(), canvas.getWidth(), canvas.getHeight()); 275b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mGeometry.straighten = straighten; 276b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mDisplayMatrixInverse = new Matrix(); 277b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mDisplayMatrixInverse.reset(); 278b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk if (!mDisplayCropMatrix.invert(mDisplayMatrixInverse)) { 279b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk Log.w(TAG, "could not invert display matrix"); 280b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mDisplayMatrixInverse = null; 281b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk return; 2826e2dd284681a716c55e0937ef2e15a1c7507a1b2Ruben Brunk } 283b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk // Scale min side and tolerance by display matrix scale factor 284b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mCropObj.setMinInnerSideSize(mDisplayMatrixInverse.mapRadius(mMinSideSize)); 285b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mCropObj.setTouchTolerance(mDisplayMatrixInverse.mapRadius(mTouchTolerance)); 286ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford // drive Crop engine to clamp to crop bounds 287ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford int[] sides = {CropObject.MOVE_TOP, 288ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford CropObject.MOVE_BOTTOM, 289ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford CropObject.MOVE_LEFT, 290ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford CropObject.MOVE_RIGHT}; 291ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford int delta = Math.min(canvas.getWidth(), canvas.getHeight()) / 4; 292ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford int[] dy = {delta, -delta, 0, 0}; 293ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford int[] dx = {0, 0, delta, -delta}; 294ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford 295ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford for (int i = 0; i < sides.length; i++) { 296ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford mCropObj.selectEdge(sides[i]); 297ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford 298ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford mCropObj.moveCurrentSelection(dx[i], dy[i]); 299ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford mCropObj.moveCurrentSelection(-dx[i], -dy[i]); 300ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford } 301ec1f915006e5ec2db8be633b5d3d73e568951da4John Hoford mCropObj.selectEdge(CropObject.MOVE_NONE); 30262e962bcb9fc03f3cfeac5ece8d3e95fc2dd0718Ruben Brunk } 303b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk // Draw actual bitmap 304b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mPaint.reset(); 305b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mPaint.setAntiAlias(true); 306b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mPaint.setFilterBitmap(true); 307b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk canvas.drawBitmap(bitmap, mDisplayMatrix, mPaint); 308b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mCropObj.getInnerBounds(mScreenCropBounds); 309b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk RectF outer = mCropObj.getOuterBounds(); 310b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk FilterCropRepresentation.findNormalizedCrop(mScreenCropBounds, (int) outer.width(), 311b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk (int) outer.height()); 312b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk FilterCropRepresentation.findScaledCrop(mScreenCropBounds, bitmap.getWidth(), 313b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk bitmap.getHeight()); 314b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk if (mDisplayCropMatrix.mapRect(mScreenCropBounds)) { 315b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk // Draw crop rect and markers 316b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk CropDrawingUtils.drawCropRect(canvas, mScreenCropBounds); 317805a7fe57da2534462f4720abc7fe9cdb8f7ecc9John Hoford CropDrawingUtils.drawShade(canvas, mScreenCropBounds); 318b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk CropDrawingUtils.drawRuleOfThird(canvas, mScreenCropBounds); 319b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk CropDrawingUtils.drawIndicators(canvas, mCropIndicator, mIndicatorSize, 320b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk mScreenCropBounds, mCropObj.isFixedAspect(), 321b0f7a8f7f7d95ae12e92f529fd9a8a37f75b105cRuben Brunk decode(mCropObj.getSelectState(), mGeometry.rotation.value())); 3229820e7e753b7e1977ef3d2163605431769ce9165nicolasroard } 3239820e7e753b7e1977ef3d2163605431769ce9165nicolasroard } 3249820e7e753b7e1977ef3d2163605431769ce9165nicolasroard 325d61a2f9fcaad0309132b6b9b666c3dc6df62fed7John Hoford public void setEditor(EditorCrop editorCrop) { 326d61a2f9fcaad0309132b6b9b666c3dc6df62fed7John Hoford mEditorCrop = editorCrop; 327d61a2f9fcaad0309132b6b9b666c3dc6df62fed7John Hoford } 3280f7dc6ef6e736c0993240450b50b91721c79c43eRuben Brunk} 329