java - Android 在 Canvas 上操作图像 - 使用触摸、移动、放大/缩小、缩放

标签 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

我有 2 个带有此 View 的功能...

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 个图像):

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);
        }

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

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

最佳答案

如果您想要这样的效果,您尝试使用 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;
        }
    }
}

编辑:当使用位图而不是路径时,代码会短几行:

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

关于java - Android 在 Canvas 上操作图像 - 使用触摸、移动、放大/缩小、缩放,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48900289/

相关文章:

javascript - HTML5游戏——如何防止变量被修改

打开 netbeans 时出现 java.lang.IndexOutOfBoundsException

java - 用手指画画

android - 有没有快速的方法来切换 Android 模拟器中的黑暗模式?

java - 在 android Studio 中,我希望当我单击按钮时,下一个 Activity/fragment 应该来自右侧

android - 异步任务不工作

javascript - requestAnimationFrame 在帧结束之前调用?

图像到 Canvas/HTML5 转换

java - @EventListener & IRequestCycle.getResponseBuilder.updateComponent 不更新组件

java - JPA 中黑白可空关键字和可空关键字有什么区别?