本篇要讲解一个比较难理解的部分:位图运算。
相信大家都见过下面这张图。
如果没有理解位图运算的逻辑,上来开始尝试各种模式,就很容易陷入误区,发现自己出来的效果跟这张图完全不相符。下面我们就来详细讲解下位图运算。
一,首先我们要来说一下 图层(layer)的概念。
Canvas在一般情况下可以看做是一张画布,所有的绘图操作如 位图、圆、直线等都在这张画布上绘制。但是如果需要实现一些比较复杂的绘图操作,需要Canvas提供图层的支持。缺省情况下可以看做只有一个图层,如果需要按层次来绘图,Canvas需要创建一些中间层,layer按照“栈结构”来管理。layer入栈时,后续的绘图操作都发生在这个layer上;layer出栈,将本图层绘制的图像绘制到Canvas上。
入栈:创建layer并入栈是通过Canvas的saveLayer方法来实现的。(此方法有比较多的重载类型,大家可根据需要选择)
public int saveLayer(float left, float top, float right, float bottom, Paint paint, int saveFlags)
出栈:需要调用 public void restoreToCount(int saveCount)方法。
二,位图运算技巧
- 准备好分别代表DST和SRC的位图,同时准备第三个位图,用于绘制DST和SRC的运算结果
- 创建大小合适的图层入栈
- 先将DST位图绘制在第三个位图上
- 调用paint的setXfermode()定义位图运算模式
- 再将SRC位图绘制在第三个位图上
- 清除位图模式
- 图层出栈
- 将第三个位图绘制在View的Canvas上以便显示出来
三,按照上面的位图运算技巧,我们来实现一个 圆形头像 的自定义view。
我们知道,ImageView是正方形的,但是现在我们看很多APP的用户图像是圆形的,我们可以使用位图运算的 DST_IN 模式来实现
public ComposeShaderView2(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
//1,头像Bitmap,是DST
bmpCat = BitmapFactory.decodeResource(getResources(), R.mipmap.header);//头像bitmap
//样式Bitmap,是SRC (我们这里准备一个五角星样式的,你可以根据需要改变这个样式)
bmpCircleMask2 = BitmapFactory.decodeResource(getResources(),R.mipmap.five_start);//五角星样式
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//2,按照SRC的大小创建layer,并保存图层ID
int layer = canvas.saveLayer(0,0,bmpCircleMask2.getWidth(),bmpCircleMask2.getHeight(),null);
//3,绘制DST
canvas.drawBitmap(bmpCat,0,0,null);
//4,设置画笔的模式 setXfermode,为DST_IN,根据实际需求选择模式
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
//5,绘制SRC
canvas.drawBitmap(bmpCircleMask2,0,0,paint);
//6,清除位图模式
paint.setXfermode(null);
//7,图层出栈(参数为创建涂层时返回的涂层ID)
canvas.restoreToCount(layer);
}
R.mipmap.header的原图是
经过上面的位图计算后效果如下:
怎么样,是不是将原本方形的图像变成了五角形,当然这里你可以改为任何其他你想要设置的样式。
大家可以根据需要修改步骤4中的位图模式,来完成自已想要的效果。
DONE
此系列后续持续更新。