package com.rec.photoeditor.editoractivity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.Region; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.ImageView; import com.rec.photoeditor.R; import com.rec.photoeditor.utils.ScreenCoordinatesCalculator; public class CropImageView extends ImageView { // Constants private static final int UPPER_LEFT = 1; private static final int UPPER_RIGHT = 2; private static final int LOWER_LEFT = 3; private static final int LOWER_RIGHT = 4; private static final int CENTER = 5; private static final int MAX_DISTANCE_FROM_CORNER_TO_DRAG = 75; private static final int MIN_WIDTH_PX = 40; private static final int MIN_HEIGHT_PX = 40; // Region of Interest variables private int roiX = 100; private int roiY = 100; private int roiWidth = 100; private int roiHeight = 100; private int selectedCorner; private boolean isDragging; private Bitmap cornerMarker; // constructors public CropImageView(Context context) { super(context); init(); } public CropImageView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public CropImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { cornerMarker = BitmapFactory.decodeResource(getResources(), R.drawable.conrer_48); } public Rect getRegionOfInterest() { int lu[] = ScreenCoordinatesCalculator.screenToImage(new int[] { roiX, roiY }, getImageMatrix()); int rb[] = ScreenCoordinatesCalculator.screenToImage(new int[] { roiX + roiWidth, roiY + roiHeight }, getImageMatrix()); return new Rect(lu[0], lu[1], rb[0], rb[1]); } Rect notDrawedRoi; public void setRegionOfInterest(Rect rect) { notDrawedRoi = rect; saveRoi(rect); this.invalidate(); } private void saveRoi(Rect rect) { int lu[] = ScreenCoordinatesCalculator.imageToScreen(new int[] { rect.left, rect.top }, getImageMatrix()); int rb[] = ScreenCoordinatesCalculator.imageToScreen(new int[] { rect.right, rect.bottom }, getImageMatrix()); roiX = lu[0]; roiY = lu[1]; roiWidth = rb[0] - roiX; roiHeight = rb[1] - roiY; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // looks weird, but it's neccessary: saveRoi must be after onDraw, // otherwise getImageMatrix did not return proper values if (notDrawedRoi != null) { saveRoi(notDrawedRoi); notDrawedRoi = null; } drawRoiRectangle(canvas); drawAllMarkers(canvas); } private void drawRoiRectangle(Canvas canvas) { canvas.save(); canvas.clipRect( new Rect(roiX, roiY, roiX + roiWidth, roiY + roiHeight), Region.Op.DIFFERENCE); canvas.drawARGB(140, 0, 0, 0); canvas.restore(); } private void drawAllMarkers(Canvas canvas) { Rect src = new Rect(0, 0, cornerMarker.getWidth(), cornerMarker.getHeight()); drawCornerMarker(canvas, src, roiX, roiY); drawCornerMarker(canvas, src, roiX, roiY + roiHeight); drawCornerMarker(canvas, src, roiX + roiWidth, roiY); drawCornerMarker(canvas, src, roiX + roiWidth, roiY + roiHeight); drawCornerMarker(canvas, src, roiX + roiWidth/2, roiY + roiHeight/2); } private void drawCornerMarker(Canvas canvas, Rect src, int x, int y) { Rect dst = new Rect(x - cornerMarker.getWidth() / 2, y - cornerMarker.getHeight() / 2, x + cornerMarker.getWidth() / 2, y + cornerMarker.getHeight() / 2); canvas.drawBitmap(cornerMarker, src, dst, null); } @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { isDragging = true; selectNearestCorner((int) event.getX(), (int) event.getY()); } if (event.getAction() == MotionEvent.ACTION_MOVE) { moveSelectedCorner((int) event.getX(), (int) event.getY()); } if (event.getAction() == MotionEvent.ACTION_UP) { if (isDragging) { isDragging = false; moveSelectedCorner((int) event.getX(), (int) event.getY()); } } return true; } private void moveSelectedCorner(int rawX, int rawY) { switch (selectedCorner) { case UPPER_LEFT: moveLeftBound(rawX); moveUpperBound(rawY); break; case LOWER_LEFT: moveLeftBound(rawX); moveLowerBound(rawY); break; case UPPER_RIGHT: moveRightBound(rawX); moveUpperBound(rawY); break; case LOWER_RIGHT: moveRightBound(rawX); moveLowerBound(rawY); break; case CENTER: moveSection(rawX, rawY); break; default: break; } invalidate(); } private void moveLowerBound(int rawY) { if (rawY - roiY < MIN_HEIGHT_PX) { roiHeight = MIN_HEIGHT_PX; } else { roiHeight = rawY - roiY; } } private void moveUpperBound(int rawY) { if (roiHeight - rawY + roiY < MIN_HEIGHT_PX) { roiY = roiY + roiHeight - MIN_HEIGHT_PX; roiHeight = MIN_HEIGHT_PX; } else { roiHeight -= rawY - roiY; roiY = rawY; } } private void moveRightBound(int rawX) { if (rawX - roiX < MIN_WIDTH_PX) { roiWidth = MIN_WIDTH_PX; } else { roiWidth = rawX - roiX; } } private void moveLeftBound(int rawX) { if (roiWidth - rawX + roiX < MIN_WIDTH_PX) { roiX = roiX + roiWidth - MIN_WIDTH_PX; roiWidth = MIN_WIDTH_PX; } else { roiWidth -= rawX - roiX; roiX = rawX; } } private void moveSection(int rawX, int rawY) { roiX += (rawX - (roiX + roiX / 2) ); roiY += (rawY - (roiY + roiY / 2) ); } private void selectNearestCorner(int rawX, int rawY) { int minDistance = MAX_DISTANCE_FROM_CORNER_TO_DRAG; selectedCorner = 0; minDistance = cornerDistanceAndSelection(rawX, rawY, roiX, roiY, minDistance, UPPER_LEFT); minDistance = cornerDistanceAndSelection(rawX, rawY, roiX, roiY + roiHeight, minDistance, LOWER_LEFT); minDistance = cornerDistanceAndSelection(rawX, rawY, roiX + roiWidth, roiY, minDistance, UPPER_RIGHT); minDistance = cornerDistanceAndSelection(rawX, rawY, roiX + roiWidth, roiY + roiHeight, minDistance, LOWER_RIGHT); minDistance = cornerDistanceAndSelection(rawX, rawY, roiX + roiWidth / 2, roiY + roiHeight / 2, minDistance, CENTER); } private int cornerDistanceAndSelection(int tapX, int rawY, int cornerX, int cornerY, int minDistance, int corner) { int distance; distance = calculateDistance(tapX, rawY, cornerX, cornerY); if (minDistance > distance) { minDistance = distance; selectedCorner = corner; } return minDistance; } /** * Euclidean distance between two points (x1, y1) and (x2, y2) */ private int calculateDistance(int x1, int y1, int x2, int y2) { return (int) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); } }