什么是XferMode
XferMode全称TransferMode,可翻译为像素转换模式,或色彩混合模式
它指定了,在一个已存在的目标图形上(DST),再绘制一个新图形(SRC),两个图形的像素如何进行混合,形成新的像素
XferMode目前只有一个实现类PorterDuffXfermode,它通过PorterDuff.Mode算法来决定像素如何进行混合
关于这个模式,我们前面在讲Shader时,有详细说明,各种Mode的效果如下
PorterDuff.Mode算法原理
XferMode的使用方法非常简单,一行代码即可
为Paint设置一个PorterDuffXfermode,之后用它绘制的图形,都会应用该效果
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
但是在实际应用中,PorterDuffXfermode的效果往往和我们预期的不一样
这是因为,很多人没有真正理由PorterDuff.Mode算法的工作原理
上面的示意图,只是一种最简单的情景,画SRC的时候,Canvas是全透明的,上面只有DST一个图形元素
实际上,SRC并不是和DST做像素运算,而是和Canvas上已经存在的所有像素做运算
当Canvas上除了SRC还有其它图形,或者Canvas不是全透明,SRC本身有透明度时,光靠上面的示意图就不够了
我们只有通过PorterDuff.Mode的具体算法公式,才能想象出最终的混合图形是什么样的
其实PorterDuff.Mode的算法公式非常简单,去源码里面看一下对应Mode的注释就知道了
以PorterDuff.Mode.SRC_IN为例,它的公式如下
float alpha_out = alpha_src * alpha_dst;
float color_out = color_src * alpha_dst;
第一行公式决定了最终像素的透明度,第二行公式决定了最终像素的色彩值
注意,这里的color公式,适用于RGB每个色彩分量,alpha和color的范围均为0-255
这里的乘法是一种简写,实际是要拿乘积再除以255,否则255*255,乘积就超过了255的范围了
PorterDuffXfermode与Layer的组合使用
很多时候,我们只希望SRC与DST进行运算,保持Canvas上其它的图形不变
这时我们就需要使用到Canvas的图层功能了
我们只需要新建一个Layer,在此Layer上面绘制DST和SRC,之前已存在的图形,就不会参与像素运算了
PorterDuffXfermode实现圆角图片
package com.android.architecture;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
public class RoundImageView_SrcIn extends View {
//圆角矩形
Bitmap dst;
//真实图像
Bitmap src;
public RoundImageView_SrcIn(Context context) {
this(context, null);
}
public RoundImageView_SrcIn(Context context, AttributeSet attrs) {
super(context, attrs);
src = BitmapFactory.decodeResource(getResources(), R.drawable.de_ma, null);
dst = BitmapFactory.decodeResource(getResources(), R.drawable.round_rect, null);
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.argb(255, 150, 150, 150));
canvas.saveLayer(0, 0, 9999, 9999, null);
Paint paint = new Paint();
canvas.drawBitmap(dst, new Rect(0, 0, dst.getWidth(), dst.getHeight()), new Rect(100, 100, 600, 500), paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(src, new Rect(0, 0, src.getWidth(), src.getHeight()), new Rect(100, 100, 600, 500), paint);
}
}
Canvas背景 DST图形
SRC图形
最终结果
PorterDuffXfermode的其它应用
PorterDuffXfermode的模式非常多,用法也非常多,同一种特效可能多种方式都可以实现
由于博客篇幅有限,就不再逐一列举了,想深入了解的,可以看Demo自己学习下
XferMode实现常见特效.zip
大家如果想要在UI方面深入发展的话,其实光看博客是不够的
一定要把常见的特效实现多抄抄,亲手写一遍,基础知识只有掌握透彻了,才能在实践中灵活运用
Demo里包含了圆角效果,倒影效果,心电图效果,波浪效果,裁边效果,橡皮擦效果,刮刮卡效果等
把这些效果都看懂了,对XferMode的理解基本也就透彻了