CropView.java revision c3178ca4daf7ed570f3432f3433959c208ecc105
1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.gallery3d.filtershow.crop; 18 19import android.content.Context; 20import android.content.res.Resources; 21import android.graphics.Bitmap; 22import android.graphics.Canvas; 23import android.graphics.Matrix; 24import android.graphics.Paint; 25import android.graphics.Rect; 26import android.graphics.RectF; 27import android.graphics.drawable.Drawable; 28import android.graphics.drawable.NinePatchDrawable; 29import android.util.AttributeSet; 30import android.util.Log; 31import android.view.MotionEvent; 32import android.view.View; 33 34import com.android.gallery3d.R; 35import com.android.gallery3d.filtershow.crop.CropDrawingUtils; 36 37 38public class CropView extends View { 39 private static final String LOGTAG = "CropView"; 40 41 private RectF mImageBounds = new RectF(); 42 private RectF mScreenBounds = new RectF(); 43 private RectF mScreenImageBounds = new RectF(); 44 private RectF mScreenCropBounds = new RectF(); 45 private Rect mShadowBounds = new Rect(); 46 47 private Bitmap mBitmap; 48 private Paint mPaint = new Paint(); 49 50 private NinePatchDrawable mShadow; 51 private CropObject mCropObj = null; 52 private final Drawable mCropIndicator; 53 private final int mIndicatorSize; 54 private int mRotation = 0; 55 private boolean mMovingBlock = false; 56 private Matrix mDisplayMatrix = null; 57 private Matrix mDisplayMatrixInverse = null; 58 private boolean mDirty = false; 59 60 private float mPrevX = 0; 61 private float mPrevY = 0; 62 63 private int mShadowMargin = 15; 64 private int mMargin = 32; 65 private int mOverlayShadowColor = 0xCF000000; 66 private int mMinSideSize = 90; 67 private int mTouchTolerance = 40; 68 69 private enum Mode { 70 NONE, MOVE 71 } 72 73 private Mode mState = Mode.NONE; 74 75 public CropView(Context context, AttributeSet attrs) { 76 super(context, attrs); 77 Resources rsc = context.getResources(); 78 mShadow = (NinePatchDrawable) rsc.getDrawable(R.drawable.geometry_shadow); 79 mCropIndicator = rsc.getDrawable(R.drawable.camera_crop); 80 mIndicatorSize = (int) rsc.getDimension(R.dimen.crop_indicator_size); 81 mShadowMargin = (int) rsc.getDimension(R.dimen.shadow_margin); 82 mMargin = (int) rsc.getDimension(R.dimen.preview_margin); 83 mMinSideSize = (int) rsc.getDimension(R.dimen.crop_min_side); 84 mTouchTolerance = (int) rsc.getDimension(R.dimen.crop_touch_tolerance); 85 mOverlayShadowColor = (int) rsc.getColor(R.color.crop_shadow_color); 86 } 87 88 public void initialize(Bitmap image, RectF newCropBounds, RectF newPhotoBounds, int rotation) { 89 mBitmap = image; 90 if (mCropObj != null) { 91 RectF crop = mCropObj.getInnerBounds(); 92 RectF containing = mCropObj.getOuterBounds(); 93 if (crop != newCropBounds || containing != newPhotoBounds 94 || mRotation != rotation) { 95 mRotation = rotation; 96 mCropObj.resetBoundsTo(newCropBounds, newPhotoBounds); 97 clearDisplay(); 98 } 99 } else { 100 mRotation = rotation; 101 mCropObj = new CropObject(newPhotoBounds, newCropBounds, 0); 102 clearDisplay(); 103 } 104 } 105 106 public RectF getCrop() { 107 return mCropObj.getInnerBounds(); 108 } 109 110 public RectF getPhoto() { 111 return mCropObj.getOuterBounds(); 112 } 113 114 @Override 115 public boolean onTouchEvent(MotionEvent event) { 116 float x = event.getX(); 117 float y = event.getY(); 118 if (mDisplayMatrix == null || mDisplayMatrixInverse == null) { 119 return true; 120 } 121 float[] touchPoint = { 122 x, y 123 }; 124 mDisplayMatrixInverse.mapPoints(touchPoint); 125 x = touchPoint[0]; 126 y = touchPoint[1]; 127 switch (event.getActionMasked()) { 128 case (MotionEvent.ACTION_DOWN): 129 if (mState == Mode.NONE) { 130 if (!mCropObj.selectEdge(x, y)) { 131 mMovingBlock = mCropObj.selectEdge(CropObject.MOVE_BLOCK); 132 } 133 mPrevX = x; 134 mPrevY = y; 135 mState = Mode.MOVE; 136 } 137 break; 138 case (MotionEvent.ACTION_UP): 139 if (mState == Mode.MOVE) { 140 mCropObj.selectEdge(CropObject.MOVE_NONE); 141 mMovingBlock = false; 142 mPrevX = x; 143 mPrevY = y; 144 mState = Mode.NONE; 145 } 146 break; 147 case (MotionEvent.ACTION_MOVE): 148 if (mState == Mode.MOVE) { 149 float dx = x - mPrevX; 150 float dy = y - mPrevY; 151 mCropObj.moveCurrentSelection(dx, dy); 152 mPrevX = x; 153 mPrevY = y; 154 } 155 break; 156 default: 157 break; 158 } 159 invalidate(); 160 return true; 161 } 162 163 private void reset() { 164 Log.w(LOGTAG, "crop reset called"); 165 mState = Mode.NONE; 166 mCropObj = null; 167 mRotation = 0; 168 mMovingBlock = false; 169 clearDisplay(); 170 } 171 172 private void clearDisplay() { 173 mDisplayMatrix = null; 174 mDisplayMatrixInverse = null; 175 invalidate(); 176 } 177 178 protected void configChanged() { 179 mDirty = true; 180 } 181 182 public void applyFreeAspect() { 183 mCropObj.unsetAspectRatio(); 184 invalidate(); 185 } 186 187 public void applyOriginalAspect() { 188 RectF outer = mCropObj.getOuterBounds(); 189 if (!mCropObj.setInnerAspectRatio((int) outer.width(), (int) outer.height())) { 190 Log.w(LOGTAG, "failed to set aspect ratio original"); 191 } 192 mCropObj.resetBoundsTo(outer, outer); 193 invalidate(); 194 } 195 196 public void applySquareAspect() { 197 if (!mCropObj.setInnerAspectRatio(1, 1)) { 198 Log.w(LOGTAG, "failed to set aspect ratio square"); 199 } 200 invalidate(); 201 } 202 203 @Override 204 public void onDraw(Canvas canvas) { 205 if (mBitmap == null) { 206 return; 207 } 208 if (mDirty) { 209 mDirty = false; 210 clearDisplay(); 211 } 212 213 mImageBounds = new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); 214 mScreenBounds = new RectF(0, 0, canvas.getWidth(), canvas.getHeight()); 215 mScreenBounds.inset(mMargin, mMargin); 216 217 // If crop object doesn't exist, create it and update it from master 218 // state 219 if (mCropObj == null) { 220 reset(); 221 mCropObj = new CropObject(mImageBounds, mImageBounds, 0); 222 } 223 224 // If display matrix doesn't exist, create it and its dependencies 225 if (mDisplayMatrix == null || mDisplayMatrixInverse == null) { 226 mDisplayMatrix = new Matrix(); 227 mDisplayMatrix.reset(); 228 if (!CropDrawingUtils.setImageToScreenMatrix(mDisplayMatrix, mImageBounds, mScreenBounds, 229 mRotation)) { 230 Log.w(LOGTAG, "failed to get screen matrix"); 231 mDisplayMatrix = null; 232 return; 233 } 234 mDisplayMatrixInverse = new Matrix(); 235 mDisplayMatrixInverse.reset(); 236 if (!mDisplayMatrix.invert(mDisplayMatrixInverse)) { 237 Log.w(LOGTAG, "could not invert display matrix"); 238 mDisplayMatrixInverse = null; 239 return; 240 } 241 // Scale min side and tolerance by display matrix scale factor 242 mCropObj.setMinInnerSideSize(mDisplayMatrixInverse.mapRadius(mMinSideSize)); 243 mCropObj.setTouchTolerance(mDisplayMatrixInverse.mapRadius(mTouchTolerance)); 244 } 245 246 mScreenImageBounds.set(mImageBounds); 247 248 // Draw background shadow 249 if (mDisplayMatrix.mapRect(mScreenImageBounds)) { 250 int margin = (int) mDisplayMatrix.mapRadius(mShadowMargin); 251 mScreenImageBounds.roundOut(mShadowBounds); 252 mShadowBounds.set(mShadowBounds.left - margin, mShadowBounds.top - 253 margin, mShadowBounds.right + margin, mShadowBounds.bottom + margin); 254 mShadow.setBounds(mShadowBounds); 255 mShadow.draw(canvas); 256 } 257 258 // Draw actual bitmap 259 canvas.drawBitmap(mBitmap, mDisplayMatrix, mPaint); 260 261 mCropObj.getInnerBounds(mScreenCropBounds); 262 263 if (mDisplayMatrix.mapRect(mScreenCropBounds)) { 264 265 // Draw overlay shadows 266 Paint p = new Paint(); 267 p.setColor(mOverlayShadowColor); 268 p.setStyle(Paint.Style.FILL); 269 CropDrawingUtils.drawShadows(canvas, p, mScreenCropBounds, mScreenImageBounds); 270 271 // Draw crop rect and markers 272 CropDrawingUtils.drawCropRect(canvas, mScreenCropBounds); 273 CropDrawingUtils.drawRuleOfThird(canvas, mScreenCropBounds); 274 CropDrawingUtils.drawIndicators(canvas, mCropIndicator, mIndicatorSize, 275 mScreenCropBounds, mCropObj.isFixedAspect(), mCropObj.getSelectState()); 276 } 277 278 } 279} 280