Android在画布上操纵图像 - 使用触摸,移动,放大/缩小,缩放

时间:2018-02-21 07:29:17

标签: java android canvas paint android-photoview

我正在研究这样的摄影应用: https://play.google.com/store/apps/details?id=com.photo.editor.collage.maker.photoblender&hl=en

我必须实现这样的功能: enter image description here

我有这种观点的两个功能......

1)具有初始背景

enter image description here enter image description here enter image description here

2)背景模糊

enter image description here enter image description here enter image description here

两者都使用2个形状.... 现在,当你使用路径绘制形状时....我想像上面那样绘制图像位图形状。

我有更多形状如下(所有有2张图片如上):

3)

enter image description here enter image description here

我想替换您正在使用的这部分代码:

private void createPath() {
            path.reset();
            path.moveTo(114, 156);
            float[] points = {68, 138, 19, 136, 21, 87, 8, 39, 56, 26, 97, -2, 123, 40, 163, 71, 131, 109};
            for (int i = 0; i < points.length; i += 2) {
                path.lineTo(points[i], points[i + 1]);
            }
            path.close();
            Matrix m = new Matrix();
            m.setRectToRect(new RectF(0, 0, 160, 160), clip, Matrix.ScaleToFit.CENTER);
            path.transform(m);
            transformedPath.set(path);
        }

我通过添加形状作为路径来实现上述功能,但是当我尝试使用形状作为位图时,我没有得到完整的结果....这是我的视图代码。

class MotionImageView extends View implements MatrixGestureDetector.OnMatrixChangeListener {
    private final Bitmap shapeMaskBitmap;
    private final Bitmap shapeShadowBitmap;
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Paint monoPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Paint borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Bitmap bitmap, blur;
    Matrix matrix = new Matrix();
    Matrix pathMatrix = new Matrix();
//    Path path = new Path();
//    Path transformedPath = new Path();
    MatrixGestureDetector detector;
    RectF clip = new RectF();

    public MotionImageView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        Log.e("~~~~", "1111");
        bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.model);
        blur = blur(context, bitmap, 8);
        ColorMatrix cm = new ColorMatrix();
        cm.setSaturation(0.25f);
        monoPaint.setColorFilter(new ColorMatrixColorFilter(cm));
        borderPaint.setStyle(Paint.Style.STROKE);
        borderPaint.setColor(0xccffffff);
        borderPaint.setStrokeWidth(4);
        borderPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        detector = new MatrixGestureDetector(pathMatrix, this);

        shapeMaskBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.shape_mask);
        shapeShadowBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.shape_shadow);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        Shader shader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
        RectF src = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
        RectF dst = new RectF(0, 0, w, h);
        matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
        shader.setLocalMatrix(matrix);
        paint.setShader(shader);
        matrix.mapRect(clip, src);
//        createPath();
    }

    /*private void createPath() {
        path.reset();
        path.moveTo(114, 156);
        float[] points = {68, 138, 19, 136, 21, 87, 8, 39, 56, 26, 97, -2, 123, 40, 163, 71, 131, 109};
        for (int i = 0; i < points.length; i += 2) {
            path.lineTo(points[i], points[i + 1]);
        }
        path.close();
        Matrix m = new Matrix();
        m.setRectToRect(new RectF(0, 0, 160, 160), clip, Matrix.ScaleToFit.CENTER);
        path.transform(m);
        transformedPath.set(path);
    }*/

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(blur, matrix, monoPaint);
        canvas.save();
        canvas.clipRect(clip);
//        canvas.drawPath(transformedPath, paint);
        canvas.drawBitmap(shapeMaskBitmap, pathMatrix, paint);
        canvas.restore();
//        canvas.drawPath(transformedPath, borderPaint);
        canvas.drawBitmap(shapeShadowBitmap, pathMatrix, borderPaint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        detector.onTouchEvent(event);
        return true;
    }

    @Override
    public void onChange(Matrix matrix) {
//        path.transform(matrix, transformedPath);
        pathMatrix.set(matrix);
        invalidate();
    }

    Bitmap blur(Context ctx, Bitmap src, float radius) {
        Bitmap bitmap = src.copy(src.getConfig(), true);

        RenderScript renderScript = RenderScript.create(ctx);
        Allocation blurInput = Allocation.createFromBitmap(renderScript, src);
        Allocation blurOutput = Allocation.createFromBitmap(renderScript, bitmap);

        ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript));
        blur.setInput(blurInput);
        blur.setRadius(radius);
        blur.forEach(blurOutput);

        blurOutput.copyTo(bitmap);
        renderScript.destroy();
        return bitmap;
    }
}

我的输出是这样的:

enter image description here

1 个答案:

答案 0 :(得分:2)

如果您想要这样的效果,那么您尝试使用PorterDuff.Mode.CLEARBlurMaskFilter无法正常工作:

enter image description here

或者这个:

enter image description here

您必须使用BitmapShaderScriptIntrinsicBlur,请参阅此示例自定义View

class V extends View implements MatrixGestureDetector.OnMatrixChangeListener {
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Paint monoPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Paint borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Bitmap bitmap, blur;
    Matrix matrix = new Matrix();
    Matrix pathMatrix = new Matrix();
    Path path = new Path();
    Path transformedPath = new Path();
    MatrixGestureDetector detector;
    RectF clip = new RectF();

    public V(Context context) {
        super(context);
        bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.forest);
        blur = blur(context, bitmap, 8);
        ColorMatrix cm = new ColorMatrix();
        cm.setSaturation(0.25f);
        monoPaint.setColorFilter(new ColorMatrixColorFilter(cm));
        borderPaint.setStyle(Paint.Style.STROKE);
        borderPaint.setColor(0xccffffff);
        borderPaint.setStrokeWidth(4);
        detector = new MatrixGestureDetector(pathMatrix, this);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        Shader shader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
        RectF src = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
        RectF dst = new RectF(0, 0, w, h);
        matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
        shader.setLocalMatrix(matrix);
        paint.setShader(shader);
        matrix.mapRect(clip, src);
        createPath();
    }

    private void createPath() {
        path.reset();
        path.moveTo(114, 156);
        float[] points = {68, 138, 19, 136, 21, 87, 8, 39, 56, 26, 97, -2, 123, 40, 163, 71, 131, 109};
        for (int i = 0; i < points.length; i += 2) {
            path.lineTo(points[i], points[i + 1]);
        }
        path.close();
        Matrix m = new Matrix();
        m.setRectToRect(new RectF(0, 0, 160, 160), clip, Matrix.ScaleToFit.CENTER);
        path.transform(m);
        transformedPath.set(path);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(blur, matrix, monoPaint);
        canvas.save();
        canvas.clipRect(clip);
        canvas.drawPath(transformedPath, paint);
        canvas.restore();
        canvas.drawPath(transformedPath, borderPaint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        detector.onTouchEvent(event);
        return true;
    }

    @Override
    public void onChange(Matrix matrix) {
        path.transform(matrix, transformedPath);
        invalidate();
    }

    Bitmap blur(Context ctx, Bitmap src, float radius) {
        Bitmap bitmap = src.copy(src.getConfig(), true);

        RenderScript renderScript = RenderScript.create(ctx);
        Allocation blurInput = Allocation.createFromBitmap(renderScript, src);
        Allocation blurOutput = Allocation.createFromBitmap(renderScript, bitmap);

        ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript));
        blur.setInput(blurInput);
        blur.setRadius(radius);
        blur.forEach(blurOutput);

        blurOutput.copyTo(bitmap);
        renderScript.destroy();
        return bitmap;
    }
}

class MatrixGestureDetector {
    private static final String TAG = "MatrixGestureDetector";

    interface OnMatrixChangeListener {
        void onChange(Matrix matrix);
    }

    private int ptpIdx = 0;
    private Matrix mTempMatrix = new Matrix();
    private Matrix mMatrix;
    private OnMatrixChangeListener mListener;
    private float[] mSrc = new float[4];
    private float[] mDst = new float[4];
    private int mCount;

    public MatrixGestureDetector(Matrix matrix, MatrixGestureDetector.OnMatrixChangeListener listener) {
        if (matrix == null) throw new RuntimeException("Matrix cannot be null");
        if (listener == null) throw new RuntimeException("OnMatrixChangeListener cannot be null");
        mMatrix = matrix;
        mListener = listener;
    }

    public void onTouchEvent(MotionEvent event) {
        if (event.getPointerCount() > 2) {
            return;
        }

        int action = event.getActionMasked();
        int index = event.getActionIndex();

        switch (action) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_POINTER_DOWN:
                int idx = index * 2;
                mSrc[idx] = event.getX(index);
                mSrc[idx + 1] = event.getY(index);
                mCount++;
                ptpIdx = 0;
                break;

            case MotionEvent.ACTION_MOVE:
                for (int i = 0; i < mCount; i++) {
                    idx = ptpIdx + i * 2;
                    mDst[idx] = event.getX(i);
                    mDst[idx + 1] = event.getY(i);
                }
                mTempMatrix.setPolyToPoly(mSrc, ptpIdx, mDst, ptpIdx, mCount);
                mMatrix.postConcat(mTempMatrix);
                mListener.onChange(mMatrix);
                System.arraycopy(mDst, 0, mSrc, 0, mDst.length);
                break;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                if (event.getPointerId(index) == 0) ptpIdx = 2;
                mCount--;
                break;
        }
    }
}

编辑:使用Bitmap代替Path时,代码会缩短几行:

class V extends View implements MatrixGestureDetector.OnMatrixChangeListener {
    Paint monoPaint = new Paint();
    Paint srcInPaint = new Paint();
    Bitmap mask, maskShadow, bitmap, blur;
    Matrix matrix = new Matrix();
    Matrix maskMatrix = new Matrix();
    MatrixGestureDetector detector;
    RectF clip = new RectF();

    public V(Context context) {
        super(context);
        mask = BitmapFactory.decodeResource(context.getResources(), R.drawable.mask).extractAlpha();
        maskShadow = BitmapFactory.decodeResource(context.getResources(), R.drawable.mask_shadow);
        bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.forest);
        blur = blur(context, bitmap, 8);
        ColorMatrix cm = new ColorMatrix();
        cm.setSaturation(0.25f);
        monoPaint.setColorFilter(new ColorMatrixColorFilter(cm));
        srcInPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        detector = new MatrixGestureDetector(maskMatrix, this);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        RectF src = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
        RectF dst = new RectF(0, 0, w, h);
        matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
        matrix.mapRect(dst, src);

        src.set(0, 0, mask.getWidth(), mask.getHeight());
        maskMatrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
        setupClip();
    }

    private void setupClip() {
        clip.set(0, 0, mask.getWidth(), mask.getHeight());
        maskMatrix.mapRect(clip);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(blur, matrix, monoPaint);
        drawMask(canvas);
    }

    private void drawMask(Canvas canvas) {
        canvas.clipRect(clip);
        canvas.saveLayer(clip, null, 0);
        canvas.drawBitmap(mask, maskMatrix, null);
        canvas.drawBitmap(bitmap, matrix, srcInPaint);
        canvas.drawBitmap(maskShadow, maskMatrix, null);
        canvas.restore();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        detector.onTouchEvent(event);
        return true;
    }

    @Override
    public void onChange(Matrix matrix) {
        setupClip();
        invalidate();
    }

    Bitmap blur(Context ctx, Bitmap src, float radius) {
        Bitmap bitmap = src.copy(src.getConfig(), true);

        RenderScript renderScript = RenderScript.create(ctx);
        Allocation blurInput = Allocation.createFromBitmap(renderScript, src);
        Allocation blurOutput = Allocation.createFromBitmap(renderScript, bitmap);

        ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript));
        blur.setInput(blurInput);
        blur.setRadius(radius);
        blur.forEach(blurOutput);

        blurOutput.copyTo(bitmap);
        renderScript.destroy();
        return bitmap;
    }
}

结果是这样的:

enter image description here