package com.remax.visualnovel.widget.imagepicker.widget.cropimage;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.ImageView;
import android.widget.OverScroller;
import android.widget.Scroller;

import com.bumptech.glide.load.resource.gif.GifDrawable;
import com.remax.visualnovel.widget.imagepicker.utils.PBitmapUtils;


/**
 * Description: 剪裁ImageView
 * <p>
 * Author: peixing.yang
 * Date: 2019/2/21
 */
@SuppressLint("AppCompatCustomView")
public class CropImageView extends ImageView {
    private final static int MIN_ROTATE = 35;
    private final static int ANIM_DURING = 340;
    private final static float MAX_SCALE = 2.5f;
    private static int loadMaxSize = 0;

    private int mMinRotate;
    private int mAnimDuring;
    private float mMaxScale;

    private int MAX_FLING_OVER_SCROLL = 0;
    private int MAX_OVER_RESISTANCE = 0;

    private Matrix mBaseMatrix = new Matrix();
    private Matrix mAnimMatrix = new Matrix();
    private Matrix mSynthesisMatrix = new Matrix();
    private Matrix mTmpMatrix = new Matrix();

    private RotateGestureDetector mRotateDetector;
    private GestureDetector mDetector;
    private ScaleGestureDetector mScaleDetector;
    private OnClickListener mClickListener;

    private ScaleType mScaleType = ScaleType.CENTER_INSIDE;

    private boolean hasMultiTouch;
    private boolean hasDrawable;
    private boolean isKnowSize;
    private boolean hasOverTranslate;
    private boolean isEnable = false;
    private boolean isRotateEnable = false;
    // 当前是否处于放大状态
    private boolean isZoomUp;
    private boolean canRotate;

    private boolean imgLargeWidth;
    private boolean imgLargeHeight;

    private float mRotateFlag;
    private float mDegrees;
    private float mScale = 1.0f;
    private int mTranslateX;
    private int mTranslateY;

    private RectF mCropRect = new RectF();
    private RectF mBaseRect = new RectF();
    private RectF mImgRect = new RectF();
    private RectF mTmpRect = new RectF();
    private RectF mCommonRect = new RectF();

    private PointF mScreenCenter = new PointF();
    private PointF mScaleCenter = new PointF();
    private PointF mRotateCenter = new PointF();

    private Paint linePaint;

    private Transform mTranslate = new Transform();

    private RectF mClip;
    private Runnable mCompleteCallBack;

    private OnLongClickListener mLongClick;

    private boolean isShowCropRect = true;

    public CropImageView(Context context) {
        super(context);
        init();
    }

    public CropImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CropImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        super.setScaleType(ScaleType.MATRIX);
        if (mScaleType == null) mScaleType = ScaleType.CENTER_CROP;
        mRotateDetector = new RotateGestureDetector(mRotateListener);
        mDetector = new GestureDetector(getContext(), mGestureListener);
        mScaleDetector = new ScaleGestureDetector(getContext(), mScaleListener);
        float density = getResources().getDisplayMetrics().density;
        MAX_FLING_OVER_SCROLL = (int) (density * 30);
        MAX_OVER_RESISTANCE = (int) (density * 140);

        mMinRotate = MIN_ROTATE;
        mAnimDuring = ANIM_DURING;
        mMaxScale = MAX_SCALE;

        initCropLineRect();
        initCropRect();
    }

    @Override
    public void setOnClickListener(OnClickListener l) {
        super.setOnClickListener(l);
        mClickListener = l;
    }

    @Override
    public void setScaleType(ScaleType scaleType) {
        if (scaleType == ScaleType.MATRIX) return;

        if (scaleType != mScaleType) {
            mScaleType = scaleType;
            initBase();
        }
    }

    public ScaleType getNewScaleType() {
        return mScaleType;
    }

    @Override
    public void setOnLongClickListener(OnLongClickListener l) {
        mLongClick = l;
    }


    /**
     * 设置最大可以缩放的倍数
     */
    public void setMaxScale(float maxScale) {
        mMaxScale = maxScale;
    }

    /**
     * 启用缩放功能
     */
    public void enable() {
        isEnable = true;
    }

    @Override
    public void setImageResource(int resId) {
        Drawable drawable = null;
        try {
            drawable = getResources().getDrawable(resId);
        } catch (Exception ignored) {
        }

        setImageDrawable(drawable);
    }

    private Bitmap originalBitmap;

    public Bitmap getOriginalBitmap() {
        return originalBitmap;
    }

    @Override
    public void setImageBitmap(Bitmap bm) {
        if (bm == null || bm.getWidth() == 0 || bm.getHeight() == 0) {
            return;
        }

        originalBitmap = bm;
        if (loadMaxSize == 0) {
            loadMaxSize = Math.max(bm.getWidth(), bm.getHeight());
        }

        float ratio = bm.getWidth() * 1.00f / bm.getHeight() * 1.00f;
        if (bm.getWidth() > loadMaxSize) {
            bm = Bitmap.createScaledBitmap(bm, loadMaxSize, (int) (loadMaxSize / ratio), false);
        }

        if (bm.getHeight() > loadMaxSize) {
            bm = Bitmap.createScaledBitmap(bm, (int) (loadMaxSize * ratio), loadMaxSize, false);
        }

        super.setImageBitmap(bm);
    }

    @Override
    public void setImageDrawable(Drawable drawable) {
        super.setImageDrawable(drawable);
        if (drawable == null) {
            hasDrawable = false;
            return;
        }
        if (!hasSize(drawable)) {
            return;
        }

        hasDrawable = true;
        if (originalBitmap == null) {
            if (drawable instanceof BitmapDrawable) {
                originalBitmap = ((BitmapDrawable) drawable).getBitmap();
            } else if (drawable instanceof AnimationDrawable) {
                AnimationDrawable drawable1 = (AnimationDrawable) drawable;
                Drawable drawable2 = drawable1.getFrame(0);
                if (drawable2 instanceof BitmapDrawable) {
                    originalBitmap = ((BitmapDrawable) drawable2).getBitmap();
                }
            } else if (drawable instanceof GifDrawable) {
                originalBitmap = ((GifDrawable) drawable).getFirstFrame();
            }
        }

        if (onImageLoadListener != null) {
            onImageLoadListener.onImageLoaded(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
            onImageLoadListener = null;
        }

        if (restoreInfo != null) {
            mScaleType = restoreInfo.getScaleType();
            mCropRect = restoreInfo.mWidgetRect;
            aspectX = (int) restoreInfo.mCropX;
            aspectY = (int) restoreInfo.mCropY;
            initBase();
            post(new Runnable() {
                @Override
                public void run() {
                    restoreCrop();
                }
            });
        } else {
            initBase();
        }
    }

    private Info restoreInfo;

    public void setRestoreInfo(Info restoreInfo) {
        this.restoreInfo = restoreInfo;
    }

    /**
     * 恢复状态
     */
    private void restoreCrop() {
        Info info = restoreInfo;

        mTranslateX = 0;
        mTranslateY = 0;

        if (info == null || info.mImgRect == null) {
            return;
        }

        float tcx = info.mImgRect.left + info.mImgRect.width() / 2;
        float tcy = info.mImgRect.top + info.mImgRect.height() / 2;

        mScaleCenter.set(mImgRect.left + mImgRect.width() / 2, mImgRect.top + mImgRect.height() / 2);
        mRotateCenter.set(mScaleCenter);

        // 将图片旋转回正常位置，用以计算
        mAnimMatrix.postRotate(-mDegrees, mScaleCenter.x, mScaleCenter.y);
        mAnimMatrix.mapRect(mImgRect, mBaseRect);

        // 缩放
        float scaleX = info.mImgRect.width() / mBaseRect.width();
        float scaleY = info.mImgRect.height() / mBaseRect.height();
        float scale = scaleX > scaleY ? scaleX : scaleY;

        mAnimMatrix.postRotate(mDegrees, mScaleCenter.x, mScaleCenter.y);
        mAnimMatrix.mapRect(mImgRect, mBaseRect);

        mDegrees = mDegrees % 360;

        mTranslate.withTranslate(0, 0, (int) (tcx - mScaleCenter.x), (int) (tcy - mScaleCenter.y));
        mTranslate.withScale(mScale, scale);
        mTranslate.withRotate((int) mDegrees, (int) info.mDegrees, mAnimDuring * 2 / 3);
        mTranslate.start();

        restoreInfo = null;
    }

    onImageLoadListener onImageLoadListener;

    public interface onImageLoadListener {
        void onImageLoaded(float w, float h);
    }

    public void setOnImageLoadListener(onImageLoadListener onImageLoadListener) {
        this.onImageLoadListener = onImageLoadListener;
    }

    private boolean hasSize(Drawable d) {
        return (d.getIntrinsicHeight() > 0 && d.getIntrinsicWidth() > 0)
                || (d.getMinimumWidth() > 0 && d.getMinimumHeight() > 0)
                || (d.getBounds().width() > 0 && d.getBounds().height() > 0);
    }

    private static int getDrawableWidth(Drawable d) {
        int width = d.getIntrinsicWidth();
        if (width <= 0) width = d.getMinimumWidth();
        if (width <= 0) width = d.getBounds().width();
        return width;
    }

    private static int getDrawableHeight(Drawable d) {
        int height = d.getIntrinsicHeight();
        if (height <= 0) height = d.getMinimumHeight();
        if (height <= 0) height = d.getBounds().height();
        return height;
    }

    float baseScale;

    public void initBase() {
        if (!hasDrawable) return;
        if (!isKnowSize) return;

        mBaseMatrix.reset();
        mAnimMatrix.reset();
        isZoomUp = false;

        Drawable img = getDrawable();

        int w = getWidth();
        int h = getHeight();
        int drawableWidth = getDrawableWidth(img);
        int drawableHeight = getDrawableHeight(img);

        mBaseRect.set(0, 0, drawableWidth, drawableHeight);

        // 以图片中心点居中位移
        int tx = (w - drawableWidth) / 2;
        int ty = (h - drawableHeight) / 2;

        float sx = 1;
        float sy = 1;

        // 缩放，默认不超过屏幕大小
        if (drawableWidth > w) {
            sx = (float) w / drawableWidth;
        }

        if (drawableHeight > h) {
            sy = (float) h / drawableHeight;
        }

        baseScale = Math.min(sx, sy);

        mBaseMatrix.reset();
        mBaseMatrix.postTranslate(tx, ty);
        mBaseMatrix.postScale(baseScale, baseScale, mScreenCenter.x, mScreenCenter.y);
        mBaseMatrix.mapRect(mBaseRect);

        mScaleCenter.set(mScreenCenter);
        mRotateCenter.set(mScaleCenter);

        executeTranslate();

        switch (mScaleType) {
            case CENTER:
                initCenter();
                break;
            case CENTER_CROP:
                initCenterCrop();
                break;
            case CENTER_INSIDE:
                initCenterInside();
                break;
            case FIT_CENTER:
                initFitCenter();
                break;
            case FIT_START:
                initFitStart();
                break;
            case FIT_END:
                initFitEnd();
                break;
            case FIT_XY:
                initFitXY();
                break;
        }
    }

    private void initCenter() {
        mAnimMatrix.postScale(1, 1, mScreenCenter.x, mScreenCenter.y);
        executeTranslate();
        resetBase();
    }

    private void initCenterCrop() {
        float widthScale = mCropRect.width() / mImgRect.width();
        float heightScale = mCropRect.height() / mImgRect.height();
        mScale = Math.max(widthScale, heightScale);
        mAnimMatrix.postScale(mScale, mScale, mScreenCenter.x, mScreenCenter.y);
        executeTranslate();
        resetBase();
    }

    private void initCenterInside() {
        //控件大于图片，即可完全显示图片，相当于Center，反之，相当于FitCenter
        if (mCropRect.width() > mImgRect.width()) {
            initCenter();
        } else {
            initFitCenter();
        }
        float widthScale = mCropRect.width() / mImgRect.width();
        if (widthScale > mMaxScale) {
            mMaxScale = widthScale;
        }
    }

    private void initFitCenter() {
        float widthScale = mCropRect.width() / mImgRect.width();
        float heightScale = mCropRect.height() / mImgRect.height();
        mScale = Math.min(widthScale, heightScale);
        mAnimMatrix.postScale(mScale, mScale, mScreenCenter.x, mScreenCenter.y);
        executeTranslate();
        resetBase();

        if (widthScale > mMaxScale) {
            mMaxScale = widthScale;
        }
    }

    private void initFitStart() {
        initFitCenter();
        float ty = -mImgRect.top;
        mAnimMatrix.postTranslate(0, ty);
        executeTranslate();
        resetBase();
        mTranslateY += ty;
    }

    private void initFitEnd() {
        initFitCenter();
        float ty = (mCropRect.bottom - mImgRect.bottom);
        mTranslateY += ty;
        mAnimMatrix.postTranslate(0, ty);
        executeTranslate();
        resetBase();
    }

    private void initFitXY() {
        float widthScale = mCropRect.width() / mImgRect.width();
        float heightScale = mCropRect.height() / mImgRect.height();
        mAnimMatrix.postScale(widthScale, heightScale, mScreenCenter.x, mScreenCenter.y);
        executeTranslate();
        resetBase();
    }

    private void resetBase() {
        Drawable img = getDrawable();
        mBaseRect.set(0, 0, getDrawableWidth(img), getDrawableHeight(img));
        mBaseMatrix.set(mSynthesisMatrix);
        mBaseMatrix.mapRect(mBaseRect);
        mScale = 1;
        mTranslateX = 0;
        mTranslateY = 0;
        mAnimMatrix.reset();
    }

    private void executeTranslate() {
        mSynthesisMatrix.set(mBaseMatrix);
        mSynthesisMatrix.postConcat(mAnimMatrix);
        setImageMatrix(mSynthesisMatrix);
        mAnimMatrix.mapRect(mImgRect, mBaseRect);
        imgLargeWidth = mImgRect.width() >= mCropRect.width();
        imgLargeHeight = mImgRect.height() >= mCropRect.height();
    }

    private int aspectX = -1, aspectY = -1;
    private int cropMargin = 0;

    public void setCropRatio(int aspectX, int aspectY) {
        this.aspectX = aspectX;
        this.aspectY = aspectY;
        if (cropAnim != null && cropAnim.isRunning()) {
            cropAnim.cancel();
        }
        if (aspectX <= 0 || aspectY <= 0) {
            mCropRect.set(0, 0, getWidth(), getHeight());
            mScaleType = ScaleType.CENTER_INSIDE;
            initBase();
            invalidate();
            return;
        }

        mScaleType = ScaleType.CENTER_CROP;
        resetCropSize(getWidth(), getHeight());
    }

    public boolean isEditing() {
        return isShowLine;
    }

    public void setCropMargin(int cropMargin) {
        this.cropMargin = cropMargin;
    }

    public int getCropWidth() {
        return (int) mCropRect.width();
    }

    public int getCropHeight() {
        return (int) mCropRect.height();
    }

    private void resetCropSize(int w, int h) {
        float left = 0, top = 0, right = w, bottom = h;
        if (aspectY != -1 && aspectX != -1) {
            float cropRatio = aspectX * 1.00f / aspectY;
            float viewRatio = w * 1.00f / h;
            if (h > w) {//view的高>宽
                float top1 = (h - (w - cropMargin * 2) * 1.00f / cropRatio) * 1.00f / 2;
                if (cropRatio >= 1) {//宽比例剪裁
                    left = cropMargin;
                    right = w - left;
                    top = top1;
                    bottom = h - top;
                } else if (cropRatio < 1) {//高比例剪裁
                    if (cropRatio > viewRatio) {//剪裁比例大于view宽高比，说明以宽充满，剪裁的高肯定不会超出view的高
                        left = cropMargin;
                        right = w - left;
                        top = top1;
                        bottom = h - top;
                    } else {//剪裁比例小于view宽高比,说明以高充满，宽度肯定不会超过view的宽度
                        top = cropMargin;
                        bottom = h - top;
                        left = (w - (h - cropMargin * 2) * cropRatio) / 2;
                        right = w - left;

                    }
                }
            }
            anim(left, top, right, bottom);
        } else {
            mCropRect.set(left, top, right, bottom);
            initBase();
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        isKnowSize = true;
        mScreenCenter.set(w / 2.0f, h / 2.0f);
        resetCropSize(w, h);
        setImageDrawable(getDrawable());
    }

    public void setRotateEnable(boolean rotateEnable) {
        isRotateEnable = rotateEnable;
    }

    private boolean isShowLine = false;
    private Paint cropRectPaint;
    private Paint maskPaint;
    private Paint cropStrokePaint;

    private void initCropRect() {
        cropRectPaint = new Paint();
        cropRectPaint.setStrokeWidth(dp(2f));
        cropRectPaint.setColor(Color.WHITE);
        cropRectPaint.setAntiAlias(true);
        cropRectPaint.setStyle(Paint.Style.STROKE);
        cropRectPaint.setDither(true);
        initMaskPaint();
    }

    private void initCropLineRect() {
        linePaint = new Paint();
        linePaint.setColor(Color.WHITE);
        linePaint.setAntiAlias(true);
        linePaint.setStrokeWidth(dp(0.5f));
        linePaint.setStyle(Paint.Style.FILL);

        cropStrokePaint = new Paint();
        cropStrokePaint.setColor(Color.WHITE);
        cropStrokePaint.setAntiAlias(true);
        cropStrokePaint.setStrokeCap(Paint.Cap.ROUND);
        cropStrokePaint.setStrokeWidth(dp(4));
        cropStrokePaint.setStyle(Paint.Style.STROKE);
    }

    private void initMaskPaint() {
        maskPaint = new Paint();
        maskPaint.setColor(Color.parseColor("#a0000000"));
        maskPaint.setAntiAlias(true);
        maskPaint.setStyle(Paint.Style.FILL);
    }

    private Rect viewDrawingRect = new Rect();
    private Path path = new Path();

    private void drawStrokeLine(Canvas canvas) {
        int lineWidth = dp(30);
        float x = mCropRect.left;
        float y = mCropRect.top + dp(1);
        float w = mCropRect.width();
        float h = mCropRect.height() - dp(2);
        canvas.drawLine(x, y, lineWidth + x, y, cropStrokePaint);
        canvas.drawLine(x, y, x, y + lineWidth, cropStrokePaint);
        canvas.drawLine(x, y + h, x, y + h - lineWidth, cropStrokePaint);
        canvas.drawLine(x, y + h, x + lineWidth, y + h, cropStrokePaint);
        canvas.drawLine(x + w, y, x + w - lineWidth, y, cropStrokePaint);
        canvas.drawLine(x + w, y, x + w, y + lineWidth, cropStrokePaint);
        canvas.drawLine(x + w, y + h, x + w - lineWidth, y + h, cropStrokePaint);
        canvas.drawLine(x + w, y + h, x + w, y + h - lineWidth, cropStrokePaint);
    }

    private boolean isShowImageRectLine = false;
    private boolean canShowTouchLine = true;
    private boolean isCircle = false;

    public void setCircle(boolean circle) {
        isCircle = circle;
        invalidate();
    }

    public void setShowImageRectLine(boolean showImageRectLine) {
        isShowImageRectLine = showImageRectLine;
        invalidate();
    }

    public void setCanShowTouchLine(boolean canShowTouchLine) {
        this.canShowTouchLine = canShowTouchLine;
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        try {
            super.onDraw(canvas);
        } catch (Exception ignored) {
            loadMaxSize = (int) (loadMaxSize * 0.8);
            setImageBitmap(originalBitmap);
            return;
        }

        if (isShowLine && canShowTouchLine && !isCircle) {
            int left, top, right, bottom, w, h;
            if (isShowImageRectLine) {
                left = mImgRect.left > mCropRect.left ? (int) mImgRect.left : (int) mCropRect.left;
                top = (int) mImgRect.top > mCropRect.top ? (int) mImgRect.top : (int) mCropRect.top;
                right = mImgRect.right < mCropRect.right ? (int) mImgRect.right : (int) mCropRect.right;
                bottom = mImgRect.bottom < mCropRect.bottom ? (int) mImgRect.bottom : (int) mCropRect.bottom;
                w = right - left;
                h = bottom - top;
            } else {
                w = (int) mCropRect.width();
                h = (int) mCropRect.height();
                left = (int) mCropRect.left;
                top = (int) mCropRect.top;
            }
            canvas.drawLine(left + w / 3.0f, top, left + w / 3.0f, h + top, linePaint);
            canvas.drawLine(left + w * 2 / 3.0f, top, left + w * 2 / 3.0f, h + top, linePaint);
            canvas.drawLine(left, top + h / 3.0f, left + w, top + h / 3.0f, linePaint);
            canvas.drawLine(left, top + h * 2 / 3.0f, left + w, top + h * 2 / 3.0f, linePaint);
        }

        if (!isShowCropRect || aspectY <= 0 || aspectX <= 0) {
            return;
        }

        getDrawingRect(viewDrawingRect);
        path.reset();
        if (isCircle) {
            path.addCircle(mCropRect.left + mCropRect.width() / 2, mCropRect.top + mCropRect.height() / 2, mCropRect.width() / 2, Path.Direction.CW);
        } else {
            drawStrokeLine(canvas);
            path.addRect(mCropRect.left, mCropRect.top, mCropRect.right, mCropRect.bottom, Path.Direction.CW);
        }
        canvas.clipPath(path, android.graphics.Region.Op.DIFFERENCE);
        canvas.drawRect(viewDrawingRect, maskPaint);
        canvas.drawPath(path, cropRectPaint);
    }

    @Override
    public void draw(Canvas canvas) {
        if (mClip != null) {
            canvas.clipRect(mClip);
            mClip = null;
        }
        super.draw(canvas);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        if (isEnable) {
            final int Action = event.getActionMasked();
            if (event.getPointerCount() >= 2) hasMultiTouch = true;

            mDetector.onTouchEvent(event);
            if (isRotateEnable) {
                mRotateDetector.onTouchEvent(event);
            }
            mScaleDetector.onTouchEvent(event);
            if (Action == MotionEvent.ACTION_DOWN) {
                isShowLine = true;
                invalidate();
            } else if (Action == MotionEvent.ACTION_UP || Action == MotionEvent.ACTION_CANCEL) {
                onUp();
                isShowLine = false;
                invalidate();
            }

            return true;
        } else {
            return super.dispatchTouchEvent(event);
        }
    }

    private void onUp() {
        if (mTranslate.isRunning)
            return;

        if (canRotate || mDegrees % 90 != 0) {
            float toDegrees = (int) (mDegrees / 90) * 90;
            float remainder = mDegrees % 90;

            if (remainder > 45)
                toDegrees += 90;
            else if (remainder < -45)
                toDegrees -= 90;

            mTranslate.withRotate((int) mDegrees, (int) toDegrees);

            mDegrees = toDegrees;
        }

        if (!isBounceEnable) {
            return;
        }

        float cx = mImgRect.left * 1.00f + mImgRect.width() / 2;
        float cy = mImgRect.top * 1.00f + mImgRect.height() / 2;

        mRotateCenter.set(cx, cy);

        if (mScale < 1) {
            mTranslate.withScale(mScale, 1);
            mScale = 1;
        } else if (mScale > mMaxScale) {
            mTranslate.withScale(mScale, mMaxScale);
            mScale = mMaxScale;
        }

        mScaleCenter.set(cx, cy);
        mTranslateX = 0;
        mTranslateY = 0;

        mTmpMatrix.reset();
        mTmpMatrix.postTranslate(-mBaseRect.left, -mBaseRect.top);
        mTmpMatrix.postTranslate(cx - mBaseRect.width() / 2, cy - mBaseRect.height() / 2);
        mTmpMatrix.postScale(mScale, mScale, mScaleCenter.x, mScaleCenter.y);
        mTmpMatrix.postRotate(mDegrees, cx, cy);
        mTmpMatrix.mapRect(mTmpRect, mBaseRect);

        doTranslateReset(mTmpRect);
        mTranslate.start();
    }

    private boolean isBounceEnable = true;

    public void setBounceEnable(boolean isBounceEnable) {
        this.isBounceEnable = isBounceEnable;
    }

    private void doTranslateReset(RectF imgRect) {
        int tx = 0;
        int ty = 0;

        int width = (int) (mCropRect.width());
        int height = (int) (mCropRect.height());

        if (imgRect.width() <= width) {
            if (!isImageCenterWidth(imgRect)) {
                if (aspectX > 0 && aspectY > 0) {
                    tx = (int) (imgRect.left - mCropRect.left);
                } else {
                    tx = -(int) ((mCropRect.width() - imgRect.width()) / 2 - imgRect.left);
                }
            }
        } else {
            if (imgRect.left > mCropRect.left) {
                tx = (int) (imgRect.left - mCropRect.left);
            } else if (imgRect.right < mCropRect.right) {
                tx = (int) (imgRect.right - mCropRect.right);
            }
        }

        if (imgRect.height() <= height) {
            if (!isImageCenterHeight(imgRect))
                if (aspectX > 0 && aspectY > 0) {
                    ty = (int) (imgRect.top - mCropRect.top);
                } else {
                    ty = -(int) ((mCropRect.height() - imgRect.height()) / 2 - imgRect.top);
                }
        } else {
            if (imgRect.top > mCropRect.top) {
                ty = (int) (imgRect.top - mCropRect.top);
            } else if (imgRect.bottom < mCropRect.bottom) {
                ty = (int) (imgRect.bottom - mCropRect.bottom);
            }
        }

        if (tx != 0 || ty != 0) {
            if (!mTranslate.mFlingScroller.isFinished()) mTranslate.mFlingScroller.abortAnimation();
            mTranslate.withTranslate(mTranslateX, mTranslateY, -tx, -ty);
        }
    }

    private boolean isImageCenterHeight(RectF rect) {
        return Math.abs(Math.round(rect.top) - (mCropRect.height() - rect.height()) / 2) < 1;
    }

    private boolean isImageCenterWidth(RectF rect) {
        return Math.abs(Math.round(rect.left) - (mCropRect.width() - rect.width()) / 2) < 1;
    }

    private RotateGestureDetector.OnRotateListener mRotateListener = new RotateGestureDetector.OnRotateListener() {
        @Override
        public void onRotate(float degrees, float focusX, float focusY) {
            mRotateFlag += degrees;
            if (canRotate) {
                mDegrees += degrees;
                mAnimMatrix.postRotate(degrees, focusX, focusY);
            } else {
                if (Math.abs(mRotateFlag) >= mMinRotate) {
                    canRotate = true;
                    mRotateFlag = 0;
                }
            }
        }
    };

    private ScaleGestureDetector.OnScaleGestureListener mScaleListener = new ScaleGestureDetector.OnScaleGestureListener() {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            float scaleFactor = detector.getScaleFactor();

            if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor))
                return false;

            if (mScale > mMaxScale) {
                return true;
            }

            mScale *= scaleFactor;
            mScaleCenter.set(detector.getFocusX(), detector.getFocusY());
            mAnimMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());
            executeTranslate();
            return true;
        }

        public boolean onScaleBegin(ScaleGestureDetector detector) {
            return true;
        }

        public void onScaleEnd(ScaleGestureDetector detector) {
        }
    };

    private float resistanceScrollByX(float overScroll, float detalX) {
        return detalX * (Math.abs(Math.abs(overScroll) - MAX_OVER_RESISTANCE) / (float) MAX_OVER_RESISTANCE);
    }

    private float resistanceScrollByY(float overScroll, float detalY) {
        return detalY * (Math.abs(Math.abs(overScroll) - MAX_OVER_RESISTANCE) / (float) MAX_OVER_RESISTANCE);
    }

    /**
     * 匹配两个Rect的共同部分输出到out，若无共同部分则输出0，0，0，0
     */
    private void mapRect(RectF r1, RectF r2, RectF out) {
        float l, r, t, b;

        l = r1.left > r2.left ? r1.left : r2.left;
        r = r1.right < r2.right ? r1.right : r2.right;

        if (l > r) {
            out.set(0, 0, 0, 0);
            return;
        }

        t = r1.top > r2.top ? r1.top : r2.top;
        b = r1.bottom < r2.bottom ? r1.bottom : r2.bottom;

        if (t > b) {
            out.set(0, 0, 0, 0);
            return;
        }

        out.set(l, t, r, b);
    }

    private void checkRect() {
        if (!hasOverTranslate) {
            mapRect(mCropRect, mImgRect, mCommonRect);
        }
    }

    private Runnable mClickRunnable = new Runnable() {
        @Override
        public void run() {
            if (mClickListener != null) {
                mClickListener.onClick(CropImageView.this);
            }
        }
    };

    private GestureDetector.OnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() {
        @Override
        public void onLongPress(MotionEvent e) {
            if (mLongClick != null) {
                mLongClick.onLongClick(CropImageView.this);
            }
        }

        @Override
        public boolean onDown(MotionEvent e) {
            hasOverTranslate = false;
            hasMultiTouch = false;
            canRotate = false;
            removeCallbacks(mClickRunnable);
            return false;
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            if (hasMultiTouch) return false;
            if (!imgLargeWidth && !imgLargeHeight) return false;
            if (mTranslate.isRunning) return false;

            float vx = velocityX;
            float vy = velocityY;

            if (Math.round(mImgRect.left) >= mCropRect.left || Math.round(mImgRect.right) <= mCropRect.right) {
                vx = 0;
            }

            if (Math.round(mImgRect.top) >= mCropRect.top || Math.round(mImgRect.bottom) <= mCropRect.bottom) {
                vy = 0;
            }

            if (canRotate || mDegrees % 90 != 0) {
                float toDegrees = (int) (mDegrees / 90) * 90;
                float remainder = mDegrees % 90;

                if (remainder > 45)
                    toDegrees += 90;
                else if (remainder < -45)
                    toDegrees -= 90;

                mTranslate.withRotate((int) mDegrees, (int) toDegrees);
                mDegrees = toDegrees;
            }
            mTranslate.withFling(vx, vy);
            return super.onFling(e1, e2, velocityX, velocityY);
        }


        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            if (mTranslate.isRunning) {
                mTranslate.stop();
            }
            if (canScrollHorizontallySelf(distanceX)) {
                if (distanceX < 0 && mImgRect.left - distanceX > mCropRect.left)
                    distanceX = mImgRect.left;
                if (distanceX > 0 && mImgRect.right - distanceX < mCropRect.right)
                    distanceX = mImgRect.right - mCropRect.right;

                mAnimMatrix.postTranslate(-distanceX, 0);
                mTranslateX -= distanceX;
            } else if (imgLargeWidth || hasMultiTouch || hasOverTranslate || !isBounceEnable) {
                checkRect();
                if (!hasMultiTouch || !isBounceEnable) {
                    if (distanceX < 0 && mImgRect.left - distanceX > mCommonRect.left)
                        distanceX = resistanceScrollByX(mImgRect.left - mCommonRect.left, distanceX);
                    if (distanceX > 0 && mImgRect.right - distanceX < mCommonRect.right)
                        distanceX = resistanceScrollByX(mImgRect.right - mCommonRect.right, distanceX);
                }

                mTranslateX -= distanceX;
                mAnimMatrix.postTranslate(-distanceX, 0);
                hasOverTranslate = true;
            }

            if (canScrollVerticallySelf(distanceY)) {
                if (distanceY < 0 && mImgRect.top - distanceY > mCropRect.top)
                    distanceY = mImgRect.top;
                if (distanceY > 0 && mImgRect.bottom - distanceY < mCropRect.bottom)
                    distanceY = mImgRect.bottom - mCropRect.bottom;

                mAnimMatrix.postTranslate(0, -distanceY);
                mTranslateY -= distanceY;
            } else if (imgLargeHeight || hasOverTranslate || hasMultiTouch || !isBounceEnable) {
                checkRect();
                if (!hasMultiTouch || !isBounceEnable) {
                    if (distanceY < 0 && mImgRect.top - distanceY > mCommonRect.top)
                        distanceY = resistanceScrollByY(mImgRect.top - mCommonRect.top, distanceY);
                    if (distanceY > 0 && mImgRect.bottom - distanceY < mCommonRect.bottom)
                        distanceY = resistanceScrollByY(mImgRect.bottom - mCommonRect.bottom, distanceY);
                }

                mAnimMatrix.postTranslate(0, -distanceY);
                mTranslateY -= distanceY;
                hasOverTranslate = true;
            }

            executeTranslate();
            return true;
        }

        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            postDelayed(mClickRunnable, 250);
            return false;
        }

        @Override
        public boolean onDoubleTap(MotionEvent e) {
            mTranslate.stop();

            float from;
            float to;

            float imageCenterX = mImgRect.left + mImgRect.width() / 2;
            float imageCenterY = mImgRect.top + mImgRect.height() / 2;

            mScaleCenter.set(imageCenterX, imageCenterY);
            mRotateCenter.set(imageCenterX, imageCenterY);
            mTranslateX = 0;
            mTranslateY = 0;

            if (mScale > 1) {
                from = mScale;
                to = 1;
            } else {
                from = mScale;
                to = mMaxScale;
                mScaleCenter.set(e.getX(), e.getY());
            }

            mTmpMatrix.reset();
            mTmpMatrix.postTranslate(-mBaseRect.left, -mBaseRect.top);
            mTmpMatrix.postTranslate(mRotateCenter.x, mRotateCenter.y);
            mTmpMatrix.postTranslate(-mBaseRect.width() / 2, -mBaseRect.height() / 2);
            mTmpMatrix.postRotate(mDegrees, mRotateCenter.x, mRotateCenter.y);
            mTmpMatrix.postScale(to, to, mScaleCenter.x, mScaleCenter.y);
            mTmpMatrix.postTranslate(mTranslateX, mTranslateY);
            mTmpMatrix.mapRect(mTmpRect, mBaseRect);
            doTranslateReset(mTmpRect);

            isZoomUp = !isZoomUp;
            mTranslate.withScale(from, to);
            mTranslate.start();

            return false;
        }
    };

    public boolean canScrollHorizontallySelf(float direction) {
        if (mImgRect.width() <= mCropRect.width()) return false;
        if (direction < 0 && Math.round(mImgRect.left) - direction >= mCropRect.left)
            return false;
        return !(direction > 0) || !(Math.round(mImgRect.right) - direction <= mCropRect.right);
    }

    public boolean canScrollVerticallySelf(float direction) {
        if (mImgRect.height() <= mCropRect.height()) return false;
        if (direction < 0 && Math.round(mImgRect.top) - direction >= mCropRect.top)
            return false;
        return !(direction > 0) || !(Math.round(mImgRect.bottom) - direction <= mCropRect.bottom);
    }

    @Override
    public boolean canScrollHorizontally(int direction) {
        if (!isEnable) {
            return super.canScrollHorizontally(direction);
        }
        if (hasMultiTouch) return true;
        return canScrollHorizontallySelf(direction);
    }

    @Override
    public boolean canScrollVertically(int direction) {
        if (!isEnable) {
            return super.canScrollVertically(direction);
        }
        if (hasMultiTouch) return true;
        return canScrollVerticallySelf(direction);
    }

    private class InterpolatorProxy implements Interpolator {

        private Interpolator mTarget;

        private InterpolatorProxy() {
            mTarget = new DecelerateInterpolator();
        }

        void setTargetInterpolator(Interpolator interpolator) {
            mTarget = interpolator;
        }

        @Override
        public float getInterpolation(float input) {
            if (mTarget != null) {
                return mTarget.getInterpolation(input);
            }
            return input;
        }
    }

    private class Transform implements Runnable {

        boolean isRunning;

        OverScroller mTranslateScroller;
        OverScroller mFlingScroller;
        Scroller mScaleScroller;
        Scroller mClipScroller;
        Scroller mRotateScroller;

        ClipCalculate C;

        int mLastFlingX;
        int mLastFlingY;

        int mLastTranslateX;
        int mLastTranslateY;

        RectF mClipRect = new RectF();

        InterpolatorProxy mInterpolatorProxy = new InterpolatorProxy();

        Transform() {
            Context ctx = getContext();
            mTranslateScroller = new OverScroller(ctx, mInterpolatorProxy);
            mScaleScroller = new Scroller(ctx, mInterpolatorProxy);
            mFlingScroller = new OverScroller(ctx, mInterpolatorProxy);
            mClipScroller = new Scroller(ctx, mInterpolatorProxy);
            mRotateScroller = new Scroller(ctx, mInterpolatorProxy);
        }

        public void setInterpolator(Interpolator interpolator) {
            mInterpolatorProxy.setTargetInterpolator(interpolator);
        }

        void withTranslate(int startX, int startY, int deltaX, int deltaY) {
            mLastTranslateX = 0;
            mLastTranslateY = 0;
            mTranslateScroller.startScroll(startX, startY, deltaX, deltaY, mAnimDuring);
        }

        void withScale(float form, float to) {
            mScaleScroller.startScroll((int) (form * 10000), 0, (int) ((to - form) * 10000), 0, mAnimDuring);
        }

        void withRotate(int fromDegrees, int toDegrees) {
            mRotateScroller.startScroll(fromDegrees, 0, toDegrees - fromDegrees, 0, mAnimDuring);
        }

        void withRotate(int fromDegrees, int toDegrees, int during) {
            mRotateScroller.startScroll(fromDegrees, 0, toDegrees - fromDegrees, 0, during);
        }

        void withFling(float velocityX, float velocityY) {
            mLastFlingX = velocityX < 0 ? Integer.MAX_VALUE : 0;
            int distanceX = (int) (velocityX > 0 ? Math.abs(mImgRect.left) : mImgRect.right - mCropRect.right);
            distanceX = velocityX < 0 ? Integer.MAX_VALUE - distanceX : distanceX;
            int minX = velocityX < 0 ? distanceX : 0;
            int maxX = velocityX < 0 ? Integer.MAX_VALUE : distanceX;
            int overX = velocityX < 0 ? Integer.MAX_VALUE - minX : distanceX;

            mLastFlingY = velocityY < 0 ? Integer.MAX_VALUE : 0;
            int distanceY = (int) (velocityY > 0 ? Math.abs(mImgRect.top - mCropRect.top) : mImgRect.bottom - mCropRect.bottom);
            distanceY = velocityY < 0 ? Integer.MAX_VALUE - distanceY : distanceY;
            int minY = velocityY < 0 ? distanceY : 0;
            int maxY = velocityY < 0 ? Integer.MAX_VALUE : distanceY;
            int overY = velocityY < 0 ? Integer.MAX_VALUE - minY : distanceY;

            if (velocityX == 0) {
                maxX = 0;
                minX = 0;
            }

            if (velocityY == 0) {
                maxY = 0;
                minY = 0;
            }

            mFlingScroller.fling(mLastFlingX, mLastFlingY, (int) velocityX, (int) velocityY, minX, maxX, minY, maxY,
                    Math.abs(overX) < MAX_FLING_OVER_SCROLL * 2 ? 0 : MAX_FLING_OVER_SCROLL,
                    Math.abs(overY) < MAX_FLING_OVER_SCROLL * 2 ? 0 : MAX_FLING_OVER_SCROLL);
        }

        void start() {
            isRunning = true;
            postExecute();
        }

        void stop() {
            removeCallbacks(this);
            mTranslateScroller.abortAnimation();
            mScaleScroller.abortAnimation();
            mFlingScroller.abortAnimation();
            mRotateScroller.abortAnimation();
            isRunning = false;
        }

        @Override
        public void run() {
            if (!isRunning) return;

            boolean endAnim = true;

            if (mScaleScroller.computeScrollOffset()) {
                mScale = mScaleScroller.getCurrX() / 10000f;
                endAnim = false;
            }

            if (mTranslateScroller.computeScrollOffset()) {
                int tx = mTranslateScroller.getCurrX() - mLastTranslateX;
                int ty = mTranslateScroller.getCurrY() - mLastTranslateY;
                mTranslateX += tx;
                mTranslateY += ty;
                mLastTranslateX = mTranslateScroller.getCurrX();
                mLastTranslateY = mTranslateScroller.getCurrY();
                endAnim = false;
            }

            if (mFlingScroller.computeScrollOffset()) {
                int x = mFlingScroller.getCurrX() - mLastFlingX;
                int y = mFlingScroller.getCurrY() - mLastFlingY;

                mLastFlingX = mFlingScroller.getCurrX();
                mLastFlingY = mFlingScroller.getCurrY();

                mTranslateX += x;
                mTranslateY += y;
                endAnim = false;
            }

            if (mRotateScroller.computeScrollOffset()) {
                mDegrees = mRotateScroller.getCurrX();
                endAnim = false;
            }

            if (mClipScroller.computeScrollOffset() || mClip != null) {
                float sx = mClipScroller.getCurrX() / 10000f;
                float sy = mClipScroller.getCurrY() / 10000f;
                mTmpMatrix.setScale(sx, sy, (mImgRect.left + mImgRect.right) / 2, C.calculateTop());
                mTmpMatrix.mapRect(mClipRect, mImgRect);

                if (sx == 1) {
                    mClipRect.left = mCropRect.left;
                    mClipRect.right = mCropRect.right;
                }

                if (sy == 1) {
                    mClipRect.top = mCropRect.top;
                    mClipRect.bottom = mCropRect.bottom;
                }

                mClip = mClipRect;
            }
            if (!endAnim) {
                applyAnim();
                postExecute();
            } else {
                isRunning = false;
                if (aspectX > 0 && aspectY > 0) {
                    return;
                }
                // 修复动画结束后边距有些空隙，
                boolean needFix = false;
                if (imgLargeWidth) {
                    if (mImgRect.left > 0) {
                        mTranslateX -= mCropRect.left;
                    } else if (mImgRect.right < mCropRect.width()) {
                        mTranslateX -= (int) (mCropRect.width() - mImgRect.right);
                    }
                    needFix = true;
                }

                if (imgLargeHeight) {
                    if (mImgRect.top > 0) {
                        mTranslateY -= mCropRect.top;
                    } else if (mImgRect.bottom < mCropRect.height()) {
                        mTranslateY -= (int) (mCropRect.height() - mImgRect.bottom);
                    }
                    needFix = true;
                }

                if (needFix) {
                    applyAnim();
                }

                invalidate();
            }
            if (mCompleteCallBack != null) {
                mCompleteCallBack.run();
                mCompleteCallBack = null;
            }
        }

        private void applyAnim() {
            mAnimMatrix.reset();
            mAnimMatrix.postTranslate(-mBaseRect.left, -mBaseRect.top);
            mAnimMatrix.postTranslate(mRotateCenter.x, mRotateCenter.y);
            mAnimMatrix.postTranslate(-mBaseRect.width() / 2, -mBaseRect.height() / 2);
            mAnimMatrix.postRotate(mDegrees, mRotateCenter.x, mRotateCenter.y);
            mAnimMatrix.postScale(mScale, mScale, mScaleCenter.x, mScaleCenter.y);
            mAnimMatrix.postTranslate(mTranslateX, mTranslateY);
            executeTranslate();
        }

        private void postExecute() {
            if (isRunning) post(this);
        }
    }

    public Info getInfo() {
        return new Info(mImgRect, mCropRect, mDegrees, mScaleType.name(), aspectX, aspectY, getTranslateX(), getTranslateY(), getScale());
    }

    public interface ClipCalculate {
        float calculateTop();
    }

    public void rotate(float degrees) {
        mDegrees += degrees;
        int centerX = (int) (mCropRect.left + mCropRect.width() / 2);
        int centerY = (int) (mCropRect.top + mCropRect.height() / 2);

        mAnimMatrix.postRotate(degrees, centerX, centerY);
        executeTranslate();
    }

    public Bitmap generateCropBitmapFromView(final int backgroundColor) {
        ((Activity) getContext()).runOnUiThread(new Runnable() {
            @Override
            public void run() {
                setShowImageRectLine(false);
                isShowCropRect = false;
                invalidate();
            }
        });

        Bitmap bitmap = PBitmapUtils.getViewBitmap(CropImageView.this);
        try {
            bitmap = Bitmap.createBitmap(bitmap, (int) mCropRect.left, (int) mCropRect.top,
                    (int) mCropRect.width(), (int) mCropRect.height());
            if (isCircle) {
                bitmap = createCircleBitmap(bitmap, backgroundColor);
            }
        } catch (Exception ignored) {
        }
        return bitmap;
    }

    /**
     * 生成剪裁图片
     *
     * @return bitmap
     */
    public Bitmap generateCropBitmap() {
        if (originalBitmap == null) {
            return null;
        }
        //水平平移像素点
        float x = Math.abs(getTranslateX());
        //垂直平移像素点
        float y = Math.abs(getTranslateY());
        //缩放比例
        float scale = mScale;
        //原图宽度(Glide压缩过的，Glide默认加载会减小大图的宽高)
        int bw = originalBitmap.getWidth();
        //原图高度(Glide压缩过的)
        int bh = originalBitmap.getHeight();
        //图片宽高比
        float bRatio = bw * 1.00f / (bh * 1.00f);

        float endW;
        float endH;
        float endX;
        float endY;

        float cropWidth = mCropRect.width();
        float cropHeight = mCropRect.height();
        float cropRatio = (cropWidth * 1.00f / (cropHeight * 1.00f));

        //图片比例小于剪裁比例，以宽填满，高自适应，计算高
        if (bRatio < cropRatio) {
            endW = bw / scale;
            endH = endW / cropRatio;
            endX = bw * x / (cropWidth * scale * 1.00f);
            endY = bw * y / (cropWidth * scale * 1.00f);
        } else {
            endH = bh / scale;
            endW = cropRatio * endH;
            endX = bh * x / (cropHeight * scale * 1.00f);
            endY = bh * y / (cropHeight * scale * 1.00f);
        }

        if (endX + endW > bw) {
            endX = bw - endW;
            if (endX < 0) {
                endX = 0;
            }
        }

        if (endY + endH > bh) {
            endY = bh - endH;
            if (endY < 0) {
                endY = 0;
            }
        }

        Bitmap bitmap1;
        try {
            bitmap1 = Bitmap.createBitmap(originalBitmap, (int) endX, (int) endY, (int) endW, (int) endH);
//            if (isCircle) {
//                bitmap1 = createCircleBitmap(bitmap1, Color.TRANSPARENT);
//            }
        } catch (Exception ignored) {
            bitmap1 = generateCropBitmapFromView(Color.BLACK);
        }
        return bitmap1;
    }

    public void setShowCropRect(boolean showCropRect) {
        isShowCropRect = showCropRect;
        invalidate();
    }

    private Bitmap createCircleBitmap(Bitmap resource, int backgroundColor) {
        int width = resource.getWidth();
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        Bitmap circleBitmap = Bitmap.createBitmap(resource.getWidth(), resource.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(circleBitmap);
        if (backgroundColor != Color.TRANSPARENT) {
            paint.setColor(backgroundColor);
        }
        canvas.drawCircle(width / 2, width / 2, width / 2, paint);
        //设置画笔为取交集模式
        if (backgroundColor == Color.TRANSPARENT) {
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        } else {
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
        }

        //裁剪图片
        canvas.drawBitmap(resource, 0, 0, paint);
        return circleBitmap;
    }


    public float getTranslateX() {
        return mImgRect.left - mCropRect.left;
    }

    public float getTranslateY() {
        return mImgRect.top - mCropRect.top;
    }

    public float getScale() {
        if (mScale <= 1) {
            return 1;
        }
        return mScale;
    }

    public int dp(float dp) {
        float density = getContext().getResources().getDisplayMetrics().density;
        return (int) (dp * density + 0.5);
    }


    private ValueAnimator cropAnim;

    private void anim(float left, float top, float right, float bottom) {
        final float oldLeft = mCropRect.left;
        final float oldTop = mCropRect.top;
        final float oldRight = mCropRect.right;
        final float oldBottom = mCropRect.bottom;
        final float finalLeft = left;
        final float finalTop = top;
        final float finalRight = right;
        final float finalBottom = bottom;

        if ((oldRight == 0 || oldBottom == 0) || (oldLeft == left && oldBottom == bottom
                && oldRight == right && oldTop == top)) {
            mCropRect.set(finalLeft, finalTop, finalRight, finalBottom);
            initBase();
            invalidate();
            return;
        }

        if (cropAnim == null) {
            cropAnim = ObjectAnimator.ofFloat(0.0F, 1.0F).setDuration(400);
            cropAnim.setInterpolator(new DecelerateInterpolator());
        }
        cropAnim.removeAllUpdateListeners();
        cropAnim.removeAllListeners();
        cropAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float) animation.getAnimatedValue();
                mCropRect.left = (finalLeft - oldLeft) * value + oldLeft;
                mCropRect.top = (finalTop - oldTop) * value + oldTop;
                mCropRect.right = (finalRight - oldRight) * value + oldRight;
                mCropRect.bottom = (finalBottom - oldBottom) * value + oldBottom;
                isShowLine = value < 1.0f;
                initBase();
                invalidate();
            }
        });
        cropAnim.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                initBase();
                invalidate();
            }
        });
        cropAnim.start();
    }

    public void changeSize(boolean isAnim, final int endWidth, final int endHeight) {
        if (isAnim) {
            final int startWidth = getWidth();
            final int startHeight = getHeight();
            ValueAnimator anim = ValueAnimator.ofFloat(0.0f, 1.0f).setDuration(200);
            anim.setInterpolator(new DecelerateInterpolator());
            anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float ratio = (Float) animation.getAnimatedValue();
                    ViewGroup.LayoutParams params = getLayoutParams();
                    params.width = (int) ((endWidth - startWidth) * ratio + startWidth);
                    params.height = (int) ((endHeight - startHeight) * ratio + startHeight);
                    setLayoutParams(params);
                    setImageDrawable(getDrawable());
                }
            });
            anim.start();
        } else {
            ViewGroup.LayoutParams params = getLayoutParams();
            params.width = endWidth;
            params.height = endHeight;
            setLayoutParams(params);
        }
    }
}