上一篇文章中简单介绍了一下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:绘制出两图不相交部分
其他见顶部效果图。