最近接触到一个新东西,Xfermode,这个东西不知道大家有没有听过,他到底是干啥的呢:
使用Xfermode,可以将我们绘制的图形与Canvas画板上对应点的像素按照一些规则来进行回合,得到最终的像素,然后在更新到canvas上,得到最后需要的东西。(这是我个人大致概括的,作为自己对Xfermode的一个作用理解,希望大家能看明白吧,哈哈哈)
其使用方法为:通过Paint来设置Xfermode的模式,如
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
下面,我先来大概介绍下一些基础知识,这样能更容易理解后面说说的Xfermode的像素混合规则。
首先,在我们手机屏幕中,一个像素所显示的颜色是由4个分量组成的,分别是A,R,G,B。A代表的是透明度,R,G,B代表的三原色
在Xfermode中,S代表的是源像素,其可表示为[Sa,Sc]:
Sa代表的是源像素的透明度的值,Sc代表的是源像素的颜色值
D代表的是目标像素,其可表示为[Da,Dc]:
Da代表的是目标像素的透明度值,Dc代表的是目标像素的颜色颜色值
下面,引入一张谷歌官方的介绍图,来具体说明其混合规则下对应的效果:(图中蓝色矩形代表的是源图片,黄色圆形代表的是目标图片,对应于各种规则下的不同呈现形态)
(上面说了一大堆,我自己看了都觉得是懵逼的)
下面,我们以一个例子来看看Xfermode的作用
首先,我们准备了两张图片:
第一张为矩形图,第二张为一张圆角图片,下面,我们是有Xfermode的混合效果,来处理得到图一的圆角图
public class CustomRoundImageView extends View {
private Bitmap mSrcBitmap;
private Bitmap mDstBitmap;
private Paint mPaint;
public CustomRoundImageView(Context context) {
this(context,null);
}
public CustomRoundImageView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public CustomRoundImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//关闭硬件加速
setLayerType(View.LAYER_TYPE_SOFTWARE,null);
mPaint = new Paint();
//原图片 上文的图一
mSrcBitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.adidas);
//目标图片 上文的图二(即圆角图片)
mDstBitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.shade);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//先在画板上绘制原图片
canvas.drawBitmap(mDstBitmap,0,0,mPaint);
//设置画笔的Xfermode模式为SRC_IN
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//在设置完画笔的混合模式之后,绘制目标图片
canvas.drawBitmap(mSrcBitmap,0,0,mPaint);
//将画笔的Xfermode设置为null
mPaint.setXfermode(null);
}
}
然后我们在Activity布局中引用下该自定义的圆形图片控件,效果如下:
很清晰的,我们可以发现,先在显示的图片已经是圆角的了。
这其实是因为Xfermode起了作用:
一起来看下Mode.SRC_IN的混合公式时怎么样子的:
SRC_IN [Sa * Da, Sc * Da] —- 处理图片相交区域时,受到目标图片的Alpha值影响
当我们的目标图片为空白像素的时候,目标图片也会变成空白
简单的来说就是用目标图片的透明度来改变源图片的透明度和饱和度,当目标图片的透明度为0时,所以,当我们目标图片的四个角哪里是圆角时,其实有一部分为全透明状态,所以会导致我们的源图片也为全透明,最后展示出来的,就是一个带圆角的原图片了。
———————————例子就像介绍到这里—————————————-
接下来,来具体看一下混合模式的分类:
1:SRC类型:该类型优先显示的是源图片
1.1:SRC_IN [Sa * Da, Sc * Da] 该混合模式,在例子中已经介绍过了,这里就不再多及介绍了,毕竟本人也是小白一个,哈哈哈。
1.2:SRC_OUT[Sa (1- Da), Sc (1- Da)] 该混合模式的计算原理与SRC_IN类似,当不同的是,原图片使用目标图片的透明度的补值(1-Da)来改变原图片的透明度,我们可以用一个例子来示范下
先看下效果图;(一个橡皮擦效果)
这里,以黑色背景的Adidas为原图,背景图初始化为一张全透明,和adidas等宽高的图,话不多说,贴代码:
private Bitmap mSrcBitmap;
private Bitmap mDstBitmap;
private Paint mPaint;
private Path mPath ;
public CustomRoundImageView(Context context) {
this(context,null);
}
public CustomRoundImageView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public CustomRoundImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//关闭硬件加速
setLayerType(View.LAYER_TYPE_SOFTWARE,null);
mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(50);
mPath = new Path();
//原图片 上文的图一
mSrcBitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.adidas);
//目标图片
mDstBitmap = Bitmap.createBitmap(mSrcBitmap.getWidth(),mSrcBitmap.getHeight(), Bitmap.Config.ARGB_8888);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
//将手势轨迹画到目标图片上 这是 画过的地方为不透明,没画的地方且全透明
Canvas mCanvas = new Canvas(mDstBitmap);
mCanvas.drawPath(mPath,mPaint);
//将目标图片画到画板上
canvas.drawBitmap(mDstBitmap,0,0,mPaint);
//设置混合方式 将原图片画到画板上
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
canvas.drawBitmap(mSrcBitmap,0,0,mPaint);
mPaint.setXfermode(null);
canvas.restoreToCount(layerId);
}
private int x1,x2,y1,y2;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
x1 = (int) event.getRawX();
y1 = (int) event.getRawY();
mPath.moveTo(x1,y1);
break;
case MotionEvent.ACTION_MOVE:
float x2 = (x1+event.getX())/2;
float y2 = (y1+event.getY())/2;
//mPath.quadTo(x1,y1,x2,y2);
mPath.lineTo(x2,y2);
x1 = (int) event.getX();
y1 = (int) event.getY();
break;
}
postInvalidate();
return true;
}
这里使用到了触摸事件,并且使用Path记录下手指触摸的滑动轨迹,该滑动轨迹就相当于是橡皮擦擦过的地方
下面使用该种混合规则,写一个抽奖的刮刮卡的效果,效果如下:
双手奉上代码,这里画文字写的过于简洁,只为了个效果而已,如果要使用到项目中,还需要根据基线来绘制文字,具体的可以看:http://blog.csdn.net/JUXINHAU/article/details/77609990
private Bitmap mSrcBitmap;
private Bitmap mDstBitmap;
private Paint mPaint;
private Path mPath ;
private Paint mTextPaint;
public CustomRoundImageView(Context context) {
this(context,null);
}
public CustomRoundImageView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public CustomRoundImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//关闭硬件加速
setLayerType(View.LAYER_TYPE_SOFTWARE,null);
mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(50);
mPath = new Path();
mTextPaint = new TextPaint();
mTextPaint.setColor(Color.DKGRAY);
mTextPaint.setTextSize(30);
//原图片 上文的图一
mSrcBitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.adidas);
//目标图片
mDstBitmap = Bitmap.createBitmap(mSrcBitmap.getWidth(),mSrcBitmap.getHeight(), Bitmap.Config.ARGB_8888);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawText("抱歉,您没有中奖",mSrcBitmap.getWidth()/4,mSrcBitmap.getHeight()/2,mTextPaint);
int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
//将手势轨迹画到目标图片上 这是 画过的地方为不透明,没画的地方且全透明
Canvas mCanvas = new Canvas(mDstBitmap);
mCanvas.drawPath(mPath,mPaint);
//将目标图片画到画板上
canvas.drawBitmap(mDstBitmap,0,0,mPaint);
//设置混合方式 将原图片画到画板上
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
canvas.drawBitmap(mSrcBitmap,0,0,mPaint);
mPaint.setXfermode(null);
canvas.restoreToCount(layerId);
}
private int x1,x2,y1,y2;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
x1 = (int) event.getRawX();
y1 = (int) event.getRawY();
mPath.moveTo(x1,y1);
break;
case MotionEvent.ACTION_MOVE:
float x2 = (x1+event.getX())/2;
float y2 = (y1+event.getY())/2;
//mPath.quadTo(x1,y1,x2,y2);
mPath.lineTo(x2,y2);
x1 = (int) event.getX();
y1 = (int) event.getY();
break;
}
postInvalidate();
return true;
}
2:DST类型:该类型优先显示的是目标图片
2.1:DST_IN [Sa * Da, Sa * Dc] 其正好与SRC_IN相反
。。。。
3:其他的一些叠加效果,这里就不再多说(偷个懒,哈哈哈),需要用到的时候,可以去查看下混合规则。
———————————————-END——————————————