Skip to content

Commit

Permalink
Add magnifying effect
Browse files Browse the repository at this point in the history
  • Loading branch information
pqpo committed Aug 8, 2017
1 parent 48e0333 commit c5fc3c5
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 35 deletions.
26 changes: 21 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
## 支持特性

- 使用智能算法识别图片中的边框
- 支持拖动锚点,手动调节选区
- 支持拖动锚点,手动调节选区(支持放大镜效果)
- 使用透视变换裁剪并矫正选区

## 例子
Expand All @@ -20,7 +20,7 @@
![](art/cropped.png)


### 拖动锚点,手动调节选区:
### 拖动锚点,手动调节选区(右上角放大镜效果方便拖拽)

![](art/advance_crop.png)

Expand Down Expand Up @@ -87,7 +87,7 @@ public static Bitmap crop(Bitmap srcBmp, Point[] cropPoints)

### CropImageView 类:

#### 1. 设置裁剪边框锚点
#### 1. 设置裁剪边框锚点,必须先设置图片
```java
public void setCropPoints(Point[] cropPoints)
```
Expand All @@ -98,7 +98,7 @@ cropPoints 的大小必须为4,依次为左上,右上,右下,左下,**
public void setMaskAlpha(int mMaskAlpha)
```

#### 3. 设置是否显示辅助线
#### 3. 设置是否显示辅助线,默认开启
```java
public void setShowGuideLine(boolean showGuideLine)
```
Expand All @@ -124,11 +124,27 @@ public Bitmap crop()
public Bitmap crop(Point[] points)
```

#### 8. 设置开启放大镜效果,默认开启
```java
public void setmShowMagnifier(boolean mShowMagnifier)
```

#### 9. 判断选区是否为凸四边形
```java
public boolean canRightCrop()
```

#### 10. 设置选区为全图,必须先设置图片
```java
public void setFullImgCrop()
```


## TODOS

1. 优化智能选区算法
2. ~~优化点排序算法~~
3. CropImageView 选区放大镜效果
3. ~~CropImageView 选区放大镜效果~~
4. CropImageView xml属性配置
5. ...

Expand Down
Binary file modified aar/smartcropperlib.aar
Binary file not shown.
2 changes: 1 addition & 1 deletion app/src/main/res/layout/activity_crop.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<me.pqpo.smartcropperlib.view.CropImageView
android:id="@+id/iv_crop"
android:layout_width="match_parent"
android:layout_margin="20dp"
android:padding="20dp"
android:layout_height="0dp"
android:layout_weight="1"/>

Expand Down
Binary file modified art/SmartCropperSample.apk
Binary file not shown.
Binary file modified art/advance_crop.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified art/cropped.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed art/sample_main.png
Binary file not shown.
Binary file modified art/smart_crop.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@
public class CropUtils {

public static double getPointsDistance(Point p1, Point p2) {
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
return getPointsDistance(p1.x, p1.y, p2.x, p2.y);
}

public static double getPointsDistance(int x1, int y1, int x2, int y2) {
return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
Expand All @@ -10,15 +11,20 @@
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.Xfermode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.ImageView;

import me.pqpo.smartcropperlib.SmartCropper;
import me.pqpo.smartcropperlib.utils.CropUtils;

/**
* Created by qiulinmin on 8/2/17.
Expand All @@ -35,18 +41,23 @@ public class CropImageView extends ImageView {
private Paint mLinePaint;
private Paint mMaskPaint;
private Paint mGuideLinePaint;
private float scaleX, scaleY;
private int actW, actH, actLeft, actTop;
private Xfermode maskXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
private Paint mMagnifierPaint;
private Paint mMagnifierCrossPaint;
private float mScaleX, mScaleY;
private int mActWidth, mActHeight, mActLeft, mActTop;
private Xfermode mMaskXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
private Path mPointLinePath = new Path();
private float density;
private Point mDraggingPoint = null;
private float mDensity;
private ShapeDrawable mMagnifierDrawable;
private Matrix mMagnifierMatrix = new Matrix();

Point[] mCropPoints;
int mMaskAlpha = 86;
boolean mShowGuideLine = true;
int mLineColor = 0xFF00FFFF;
int mLineWidth = 1;
boolean mShowMagnifier = true;

public CropImageView(Context context) {
this(context, null);
Expand All @@ -62,7 +73,7 @@ public CropImageView(Context context, AttributeSet attrs, int defStyleAttr) {
if (scaleType == ScaleType.FIT_END || scaleType == ScaleType.FIT_START || scaleType == ScaleType.MATRIX) {
throw new RuntimeException("Image in CropImageView must be in center");
}
density = getResources().getDisplayMetrics().density;
mDensity = getResources().getDisplayMetrics().density;
initPaints();
}

Expand All @@ -88,6 +99,12 @@ public void setFullImgCrop() {
invalidate();
}

@Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
mMagnifierDrawable = null;
}

public Point[] getCropPoints() {
return mCropPoints;
}
Expand All @@ -113,6 +130,10 @@ public void setLineWidth(int lineWidth) {
invalidate();
}

public void setmShowMagnifier(boolean mShowMagnifier) {
this.mShowMagnifier = mShowMagnifier;
}

public Bitmap crop() {
return crop(mCropPoints);
}
Expand Down Expand Up @@ -179,6 +200,26 @@ private void initPaints() {
mGuideLinePaint.setColor(Color.WHITE);
mGuideLinePaint.setStyle(Paint.Style.FILL);
mGuideLinePaint.setStrokeWidth(1);

mMagnifierPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mMagnifierPaint.setColor(Color.WHITE);
mMagnifierPaint.setStyle(Paint.Style.FILL);

mMagnifierCrossPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mMagnifierCrossPaint.setColor(mLineColor);
mMagnifierCrossPaint.setStyle(Paint.Style.FILL);
mMagnifierCrossPaint.setStrokeWidth(dp2px(mLineWidth));
}

private void initMagnifier() {
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(getBitmap(), null, new Rect(mActLeft, mActTop, mActWidth + mActLeft, mActHeight + mActTop), null);
canvas.save();
BitmapShader magnifierShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mMagnifierDrawable = new ShapeDrawable(new OvalShape());
mMagnifierDrawable.getPaint().setShader(magnifierShader);
}

@Override
Expand All @@ -193,18 +234,44 @@ protected void onDrawCropPoint(Canvas canvas) {
onDrawGuideLine(canvas);
onDrawPoints(canvas);
onDrawLines(canvas);
onDrawMagnifier(canvas);
}

protected void onDrawMagnifier(Canvas canvas) {
if (mShowMagnifier && mDraggingPoint != null) {
if (mMagnifierDrawable == null) {
initMagnifier();
}
int draggingX = getViewPointX(mDraggingPoint);
int draggingY = getViewPointY(mDraggingPoint);

float radius = getWidth() / 8;
float cx = radius;
int lineOffset = dp2px(mLineWidth);
mMagnifierDrawable.setBounds(lineOffset, lineOffset, (int)radius * 2 - lineOffset, (int)radius * 2 - lineOffset);
double pointsDistance = CropUtils.getPointsDistance(draggingX, draggingY, 0, 0);
if (pointsDistance < (radius * 2.5)) {
mMagnifierDrawable.setBounds(getWidth() - (int)radius * 2 + lineOffset, lineOffset, getWidth() - lineOffset, (int)radius * 2 - lineOffset);
cx = getWidth() - radius;
}
canvas.drawCircle(cx, radius, radius, mMagnifierPaint);
mMagnifierMatrix.setTranslate(radius - draggingX, radius - draggingY);
mMagnifierDrawable.getPaint().getShader().setLocalMatrix(mMagnifierMatrix);
mMagnifierDrawable.draw(canvas);
canvas.drawCircle(cx, radius, dp2px(1), mMagnifierCrossPaint);
}
}

protected void onDrawGuideLine(Canvas canvas) {
if (!mShowGuideLine) {
return;
}
int widthStep = actW / 3;
int heightStep = actH / 3;
canvas.drawLine(actLeft + widthStep, actTop, actLeft + widthStep, actTop + actH, mGuideLinePaint);
canvas.drawLine(actLeft + widthStep * 2, actTop, actLeft + widthStep * 2, actTop + actH, mGuideLinePaint);
canvas.drawLine(actLeft, actTop + heightStep, actLeft + actW, actTop + heightStep, mGuideLinePaint);
canvas.drawLine(actLeft, actTop + heightStep * 2, actLeft + actW, actTop + heightStep * 2, mGuideLinePaint);
int widthStep = mActWidth / 3;
int heightStep = mActHeight / 3;
canvas.drawLine(mActLeft + widthStep, mActTop, mActLeft + widthStep, mActTop + mActHeight, mGuideLinePaint);
canvas.drawLine(mActLeft + widthStep * 2, mActTop, mActLeft + widthStep * 2, mActTop + mActHeight, mGuideLinePaint);
canvas.drawLine(mActLeft, mActTop + heightStep, mActLeft + mActWidth, mActTop + heightStep, mGuideLinePaint);
canvas.drawLine(mActLeft, mActTop + heightStep * 2, mActLeft + mActWidth, mActTop + heightStep * 2, mGuideLinePaint);
}

protected void onDrawMask(Canvas canvas) {
Expand All @@ -213,10 +280,10 @@ protected void onDrawMask(Canvas canvas) {
}
Path path = resetPointPath();
if (path != null) {
int sc = canvas.saveLayer(actLeft, actTop, actLeft + actW, actTop + actH, mMaskPaint, Canvas.ALL_SAVE_FLAG);
int sc = canvas.saveLayer(mActLeft, mActTop, mActLeft + mActWidth, mActTop + mActHeight, mMaskPaint, Canvas.ALL_SAVE_FLAG);
mMaskPaint.setAlpha(mMaskAlpha);
canvas.drawRect(actLeft, actTop, actLeft + actW, actTop + actH, mMaskPaint);
mMaskPaint.setXfermode(maskXfermode);
canvas.drawRect(mActLeft, mActTop, mActLeft + mActWidth, mActTop + mActHeight, mMaskPaint);
mMaskPaint.setXfermode(mMaskXfermode);
mMaskPaint.setAlpha(255);
canvas.drawPath(path, mMaskPaint);
mMaskPaint.setXfermode(null);
Expand Down Expand Up @@ -245,14 +312,14 @@ private void getDrawablePosition() {
Drawable drawable = getDrawable();
if (drawable != null) {
getImageMatrix().getValues(matrixValue);
scaleX = matrixValue[Matrix.MSCALE_X];
scaleY = matrixValue[Matrix.MSCALE_Y];
mScaleX = matrixValue[Matrix.MSCALE_X];
mScaleY = matrixValue[Matrix.MSCALE_Y];
int origW = drawable.getIntrinsicWidth();
int origH = drawable.getIntrinsicHeight();
actW = Math.round(origW * scaleX);
actH = Math.round(origH * scaleY);
actLeft = (getWidth() - actW) / 2;
actTop = (getHeight() - actH) / 2;
mActWidth = Math.round(origW * mScaleX);
mActHeight = Math.round(origH * mScaleY);
mActLeft = (getWidth() - mActWidth) / 2;
mActTop = (getHeight() - mActHeight) / 2;
}
}

Expand Down Expand Up @@ -285,12 +352,12 @@ public boolean onTouchEvent(MotionEvent event) {
break;
case MotionEvent.ACTION_MOVE:
toImagePointSize(mDraggingPoint, event);
invalidate();
break;
case MotionEvent.ACTION_UP:
mDraggingPoint = null;
break;
}
invalidate();
return handle || super.onTouchEvent(event);
}

Expand All @@ -315,22 +382,22 @@ private void toImagePointSize(Point dragPoint, MotionEvent event) {
if (dragPoint == null) {
return;
}
float x = Math.min(Math.max(event.getX(), actLeft), actLeft + actW);
float y = Math.min(Math.max(event.getY(), actTop), actTop + actH);
dragPoint.x = (int) ((x - actLeft) / scaleX);
dragPoint.y = (int) ((y - actTop) / scaleY);
float x = Math.min(Math.max(event.getX(), mActLeft), mActLeft + mActWidth);
float y = Math.min(Math.max(event.getY(), mActTop), mActTop + mActHeight);
dragPoint.x = (int) ((x - mActLeft) / mScaleX);
dragPoint.y = (int) ((y - mActTop) / mScaleY);
}

private int getViewPointX(Point point){
return (int) (point.x * scaleX + actLeft);
return (int) (point.x * mScaleX + mActLeft);
}

private int getViewPointY(Point point){
return (int) (point.y * scaleY + actTop);
return (int) (point.y * mScaleY + mActTop);
}

private int dp2px(float dp) {
return (int) (dp * density + 0.5f);
return (int) (dp * mDensity + 0.5f);
}

private Point[] getFullImgCropPoints() {
Expand Down

0 comments on commit c5fc3c5

Please sign in to comment.