[TOC]android
类android.graphics.PorterDuffXfermode继承自android.graphics.Xfermode。在用Android中的Canvas进行绘图时,能够经过使用PorterDuffXfermode将所绘制的图形的像素与Canvas中对应位置的像素按照必定规则进行混合,造成新的像素值,从而更新Canvas中最终的像素颜色值,这样会建立不少有趣的效果。当使用PorterDuffXfermode时,须要将将其做为参数传给Paint.setXfermode(Xfermode xfermode)方法,这样在用该画笔paint进行绘图时,Android就会使用传入的PorterDuffXfermode,若是不想再使用Xfermode,那么能够执行Paint.setXfermode(null)。
上面的概述中咱们提炼出两点:canvas
借用google官方demo中的图bash
不少同窗在测试过程当中发现和demo的实现有很大的出入,其实咱们仔细看官方demo,就会发现有很大不一样,我将核心代码抽取以下:ide
static Bitmap makeDst(int w, int h) {
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFFFFCC44);
c.drawOval(new RectF(0, 0, w * 3 / 4, h * 3 / 4), p);
return bm;
}
// create a bitmap with a rect, used for the "src" image
static Bitmap makeSrc(int w, int h) {
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFF66AAFF);
c.drawRect(w / 3, h / 3, w * 19 / 20, h * 19 / 20, p);
return bm;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GREEN);
int sc = canvas.saveLayer(0, 0, W, H, null, Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(makeDst(W, H), 0, 0, mPaint);
//mPaint.setXfermode(sModes[mIndex]);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(makeSrc(W, H), 0, 0, mPaint);
mPaint.setXfermode(null);
canvas.restoreToCount(sc);
}
复制代码
代码中有几个核心的地方咱们要注意:测试
这是由于在调用saveLayer时,会生成了一个全新的bitmap,这个bitmap的大小就是咱们指定的保存区域的大小,新生成的bitmap是全透明的,在调用saveLayer后全部的绘图操做都是在这个bitmap上进行的。ui
没有saveLayer的绘图流程google
因为咱们先把整个画布给染成了绿色,而后再画上了一个圆形,因此在应用xfermode来画源图像的时候,目标图像当前Bitmap上的全部图像了,也就是整个绿色的屏幕和一个圆形了。因此这时候源图像的相交区域是没有透明像素的,透明度全是100%,这也就不难解释结果是这样的缘由了。spa
调用saveLayer的目的就是让你的目标图像独立的存在于一个layer上,而不受原始画布图像的干扰。这样才能进行目标图像和源图像的对应位置的像素混合。3d
注:rest
测试一 使用saveLayer获得的正确图像以下
测试二 不使用saveLayer
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GREEN);
//int sc = canvas.saveLayer(0, 0, W, H, null, Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(makeDst(W, H), 0, 0, mPaint);
//mPaint.setXfermode(sModes[mIndex]);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(makeSrc(W, H), 0, 0, mPaint);
mPaint.setXfermode(null);
//canvas.restoreToCount(sc);
}
复制代码
获得的图像以下:
PorterDuff.Mode.SRC_IN的含义是在二者相交的地方绘制源图像,由于没有使用saveLayer因此咱们目标图像是绿色的背景+黄色的圆,源图像是蓝色正方形。
那么这里有两个错误点:
为何获得的是黑色图像,而不是透明颜色,其实我没弄明白明白,可是咱们知道了正确使用PorterDuffXfermode的代码以下:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
.......
int sc = canvas.saveLayer(0, 0, W, H, null, Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(makeDst(W, H), 0, 0, mPaint);
//绘制你的目标图像
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//绘制你的源图像
mPaint.setXfermode(null);
canvas.restoreToCount(sc);
.......
}
复制代码
测试三 你真的了解SRC_IN吗?
咱们在测试一的基础上修改一段代码
static Bitmap makeDst(int w, int h) {
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0x7fFFCC44);
c.drawOval(new RectF(0, 0, w * 3 / 4, h * 3 / 4), p);
return bm;
}
复制代码
将目标图像中0xFFFFCC44改成0x7fFFCC44,也就透明度改成0.5
右边是没有修改透明度的图像,左边是修改后的;咱们发现SRC_IN的结果发生了变化。让咱们来看坎SRC_IN完整的解释:
细节决定成败,小小的细节颇有可能成为你平常工做中的大坑,更多PorterDuff.Mode含义,请参考。
测试四 LAYER_TYPE_SOFTWARE使用
网上有很多说使用LAYER_TYPE_SOFTWARE的,咱们也来实践一下:
初始化时添加以下代码,使用软件加速
setLayerType(LAYER_TYPE_SOFTWARE, null);
复制代码
测试代码仍是使用调用了saveLayer的代码,可是使用CLEAR模式
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GREEN);
int sc = canvas.saveLayer(0, 0, W, H, null, Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(makeDst(W, H), 0, 0, mPaint);
//mPaint.setXfermode(sModes[mIndex]);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawBitmap(makeSrc(W, H), 0, 0, mPaint);
mPaint.setXfermode(null);
canvas.restoreToCount(sc);
}
复制代码
左边是没有设置setLayerType获得的混合图像,符合CLEAR模式的预期;右边是设置LAYER_TYPE_SOFTWARE后获得的图像,咱们发现混合后所有变成透明的了,这是为何呢?我给出这样一个猜测,在没有设置LAYER_TYPE_SOFTWARE时,只有有效图像区域参与像素混合,透明区域不参与像素混合;而设置LAYER_TYPE_SOFTWARE后透明区域也参与像素混合了。为了验证这一点,咱们进行测试五。
测试五
在测试四的基础上,目标bitmap和源bitmap是同样大的,咱们的测试方法是将源bitmap的大小修改成只有目标bitmap的1/4,这样一来源bitmap将只占据整个图像左上角1/4区域,获得的测试结果以下:
根据咱们的猜测,源bitmap的整个区域(透明区域和图像区域)都参与到与目标图像的像素混合中,致使左上角1/4区域变为了透明的。
以上分析根据我的经验得出,并不透彻,若有不对,望指正。