Canvas绘图之PorterDuffXfermode

本文介绍了在Android中使用Canvas绘图时PorterDuffXfermode的作用,通过实例展示了如何设置不同的Mode来改变两个bitmap重叠时的绘制效果,并探讨了硬件加速对Xfermode的影响,强调了只有在canvas.drawBitmap时才能看到正确的混合效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇文章中简单介绍了一下Canvas绘图相关知识,留下了setXferMode未作介绍。此篇文章针对使用setXferMode中碰到的问题,做一个总结。

很多开发者都知道,PorterDuffXfermode用来设置两个bitmap重叠时的绘制效果(据说Porter和Duff是最早提出图形混合概念的两个人),网上到处可以看到这张图~

这里写图片描述

(dst表示底图,src表示要绘制的图)
它对每种Mode的描述已经非常清楚了。比如SrcIn就是绘制两个bitmap重叠的部分,绘制时使用src bitmap的Paint效果。于是自己动手尝试写个demo来实现这些效果。
一、俗话说实践出真知,如果不亲自写一遍,我永远都不知道这里面有哪些坑。实践从下面这段代码开始:
@Override
    protected void onDraw(Canvas canvas) {
        // 绘制一个圆
        mPaint.setColor(getResources().getColor(R.color.colorBlue));
        canvas.drawCircle(200, 200, 200, mPaint);
        // 绘制一个矩形
        mPaint.setColor(getResources().getColor(R.color.colorYellow));
        canvas.drawRect(200, 200, 600, 600, mPaint);
    }

上面代码绘制一个圆形和一个矩形,运行效果如下:
这里写图片描述

二、现在让我们来设置一个PorterDuff.Mode.CLEAR:
    @Override
    protected void onDraw(Canvas canvas) {
        // 绘制一个圆
        mPaint.setColor(getResources().getColor(R.color.colorBlue));
        canvas.drawCircle(200, 200, 200, mPaint);

        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        // 绘制一个矩形
        mPaint.setColor(getResources().getColor(R.color.colorYellow));
        canvas.drawRect(200, 200, 600, 600, mPaint);
    }

按官方描述,应该是清空画布,显示空白。但是实际运行效果确是如下:
这里写图片描述
SRC_IN的运行效果甚至跟不加任何Mode一模一样。

瞬间蒙圈,是不是自己哪里错了呢,怎么跟官方效果差距那么大。各种搜索之后,发现要使用Xfermode,要关闭硬件加速:

// 构造函数中加上此项,可以关闭硬件加速功能
setLayerType(View.LAYER_TYPE_SOFTWARE, null);

关闭硬件加速功能之后,设置Mode.SRC_IN后再次尝试,效果似乎有点不同:
这里写图片描述
对比官方效果,还是不一样…

三、于是想到圆形ImageView的绘制过程
// 绘制一个圆
mPaint.setColor(getResources().getColor(R.color.colorBlue));
canvas.drawCircle(200, 200, 200, mPaint);

srcBitmap = ((BitmapDrawable)getDrawable()).getBitmap();
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(srcBitmap, 0, 0, mPaint);

此处先绘制了一个圆形,然后绘制了一个Bitmap,用SRC_IN模式来处理重叠。效果如下:
这里写图片描述
效果符合预期。
于是想会不会只有在绘制bitmap的时候才能正确实现PorterDuff的各种效果。于是那上面的代码作如下修改:

// 绘制一个圆
        mPaint.setColor(getResources().getColor(R.color.colorBlue));
        canvas.drawCircle(200, 200, 200, mPaint);

        mPaint.setColor(getResources().getColor(R.color.colorYellow));
        // 创建一个空白的bitmap
        Bitmap srcBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        // 将bimtap传入一个canvas中,可以理解为将bitmap附着在画布上
        Canvas srcCanvas = new Canvas(srcBitmap);
        // 在上面生成的画布上面绘制一个矩形,实际上是绘制在bitmap上面
        srcCanvas.drawRect(200, 200, 600, 600, mPaint);

        // 将上面的bitmap绘制出来并使用SRC_IN重叠模式
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(srcBitmap, 0, 0, mPaint);

运行效果:
这里写图片描述
此时运行效果就是我们想要的效果。
继续尝试其他的Mode,也都是正确的效果。

四、此时发觉,绘制在上层,也就是src图层,一定要是bitmap,即canvas.drawbitmap()时才可以出现正确的重叠效果。

看下图:
这里写图片描述
Xfermode是用来描述DstBitmap和SrcBitmap的混合模式。因此要使用drawBitmap来进行绘制才会出效果。

五、简单理解Mode

Mode.SRC_IN:表示 用SRC的画笔,绘制出SRC与DST相交的部分
Mode.DST_IN:表示 用DST的画笔,绘制出SRC与DST相交的部分。
Mode.SRC_OVER:SRC图在DST上方
Mode.DST_OVER:DST图在SRC上方
Mode.XOR:绘制出两图不相交部分
其他见顶部效果图。

Demo:https://github.com/ericzhaowei/Xfermode

### Android Canvas 类使用教程 #### 一、Canvas 基础介绍 `Canvas` 是 Android 绘图的核心组件之一,充当了一个虚拟的画布角色,在这个画布上能够执行各种各样的绘图操作。无论是简单的线条还是复杂的图形组合都可以通过 `Canvas` 来完成。 - **创建 Canvas** 通常情况下不需要手动实例化 `Canvas` 对象,因为当自定义视图时会自动获得它作为参数传递给 `onDraw()` 方法[^1]。 ```java @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); } ``` #### 二、基本绘图方法 以下是几种常见的绘图函数: - **drawRect(Rect r, Paint paint)**:绘制矩形。 - **drawCircle(float cx, float cy, float radius, Paint paint)**:以指定中心点(cx,cy),半径(radius)来绘制圆圈。 - **drawPath(Path path, Paint paint)**:按照路径对象(path)所描述的方式进行绘画。 - **drawText(String text,float x ,float y ,Paint paint)**:在坐标(x,y)处输出字符串text的内容[^2]。 #### 三、配合 Paint 使用 为了使绘制出来的效果更加美观多样,还需要搭配另一个重要工具——`Paint` 。它可以用来设定笔触的颜色、宽度以及填充风格等属性。 ```java // 创建一个新的 Paint 实例 Paint myPaint = new Paint(); myPaint.setColor(Color.RED); // 设置颜色为红色 myPaint.setStrokeWidth(5f); // 定义线宽大小 myPaint.setStyle(Paint.Style.STROKE); // 只描边不填满内部区域 ``` #### 四、案例实践 - 圆形头像展示 这里给出一个具体的例子说明如何利用上述提到的技术要点制作一张圆形图片并将其放置于ImageView内显示出来。 ```java public static Bitmap getCroppedBitmap(Bitmap bmp){ int diameter = Math.min(bmp.getWidth(),bmp.getHeight()); Bitmap output = Bitmap.createBitmap(diameter,diameter,Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(output); final int color = 0xffa19774; final Paint paint = new Paint(); final Rect rect = new Rect(0,0,diameter,diameter); paint.setAntiAlias(true); canvas.drawARGB(0,0,0,0); canvas.drawCircle(diameter/2+diameter*0.1f, diameter/2-diameter*0.1f, diameter/2,paint); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); canvas.drawBitmap(bmp,rect,rect,paint); return output; } ``` 这段代码实现了将传入的位图转换成具有相同尺寸但形状被裁剪成为圆形的新位图功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值