注意:在使用PorterDuffXfermode的时候,目标图像(DST)和源图像(SRC)混合的操作要在一个新的图层上进行,否则当前的Canvas上的像素会影响混合操作。
@Override
protected void onDraw(Canvas canvas) {
//注释1处,创建一个新的透明图层
int layerId = canvas.saveLayer(new RectF(), null, Canvas.ALL_SAVE_FLAG);
//...
//目标图像(DST)和图像(SRC)混合的操作
//先绘制目标bitmap
canvas.drawBitmap(mDstB, 0, 0, paint);
//设置混合模式
paint.setXfermode(sModes[i]);
//绘制源bitmap
canvas.drawBitmap(mSrcB, 0, 0, paint);
//绘制以后清空混合模式,避免影响其他绘制
paint.setXfermode(null);
//...
//将新的图层绘制到上一个图层或者屏幕上(如果没有上一个图层)。
canvas.restoreToCount(layerId);
}
注释1处,创建一个新的透明图层。
创建目标图像
public 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);
//设置的透明度是FF,完全不透明
p.setColor(0xFFFFCC44);
c.drawOval(new RectF(0, 0, w * 3 / 4f, h * 3 / 4f), p);
return bm;
}
注意:我们画笔设置的透明度是完全不透明,在Android中完全不透明用float类型的数表示为1f。
注意:我们创建的目标图像的大小是红色框的大小,我专门给标了出来。只不过我们只在圆形区域绘制了黄色内容,黄色圆形区域(0xFFFFCC44)的alpha是FF,color是FFCC44。剩余其他部分没有绘制(0x00000000),也就是说其他部分的alpha是0,color也是0,是透明的,可以理解为没有像素。这一点很重要,因为在这个例子中,目标图像和源图像大小是一样的,图像的每个区域的alpha和color都会参与混合运算。
目标图像黄色圆形区域(0xFFFFCC44),把ARGB转化成0到1的浮点数就是 [1, 1, 0.8 , 0.267]。
目标图像其他区域(0x00000000),把ARGB转化成0到1的浮点数就是 [0, 0, 0 , 0]。
创建源图像
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);
//设置的透明度是FF
p.setColor(0xFF66AAFF);
c.drawRect(w / 3f, h / 3f, w * 19 / 20f, h * 19 / 20f, p);
return bm;
}
注意:我们画笔设置的透明度是完全不透明,在Android中完全不透明用float类型的数表示为1f。
注意:我们创建的目标图像的大小是红色框的大小,我专门给标了出来。只不过我们只在矩形区域绘制了蓝色内容,蓝色矩形区域(0xFF66AAFF)的alpha是FF,color是66AAFF。剩余其他部分没有绘制(0x00000000),也就是说其他部分的alpha是0,color也是0,是透明的,可以理解为没有像素。这一点很重要,因为在这个例子中,目标图像和源图像大小是一样的,图像的每个区域的alpha和color都会参与混合运算。
源图像蓝色矩形区域(0xFF66AAFF),把ARGB转化成0到1的浮点数就是 [1, 0.4, 0.667 , 1]。
源图像其他区域(0x00000000),把ARGB转化成0到1的浮点数就是 [0, 0, 0 , 0]。
PorterDuffXfermode的构造函数
public PorterDuffXfermode(PorterDuff.Mode mode) {
porterDuffMode = mode.nativeInt;
}
PorterDuff.Mode类,表示混合模式,枚举值有18个。
public enum Mode {
CLEAR (0),
SRC (1),
DST (2),
SRC_OVER (3),
DST_OVER (4),
SRC_IN (5),
DST_IN (6),
SRC_OUT (7),
DST_OUT (8),
SRC_ATOP (9),
DST_ATOP (10),
XOR (11),
DARKEN (16),
LIGHTEN (17),
MULTIPLY (13),
SCREEN (14),
ADD (12),
OVERLAY (15);
Mode(int nativeInt) {
this.nativeInt = nativeInt;
}
public final int nativeInt;
}
定义:
α \alpha αdst :目标图像的透明度
C C Cdst:目标图像的色值
α \alpha αsrc :源图像的透明度
C C Csrc:源图像的色值
α \alpha αout:混合结果的透明度
C C Cout:混合结果的色值。
混合结果分为两部分, α \alpha αout 表示结果的透明度部分, C C Cout表示计算结果的色值部分。
注意:在这个例子中,目标图像和源图像的有颜色的区域alpha=FF,如果表示成浮点数就是1f。这一点要注意,透明度会影响我们的计算结果。
各种混合模式的计算
计算公式:
α \alpha αout = 0
C C Cout = 0
目标图像被源图像覆盖的区域像素清空到0。在这个例子中,源图像大小和目标图像是一样大的,就是红色框所示大小。最终显示结果是透明的。
源图像的像素替代目标图像的像素。注意是替代。
计算公式:
α \alpha αout = α \alpha αsrc
C C Cout = C C Csrc
源图像像素被丢弃,保留目标图像像素不变。
计算公式:
α \alpha αout = α \alpha αdst
C C Cout = C C Cdst
源像素会覆盖在目标像素之上。
计算公式:
α \alpha αout = α \alpha αsrc + (1 - α \alpha αsrc) * α \alpha αdst
C C Cout = C C Csrc + (1 - C C Csrc) * C C Cdst
在这个例子中:
源图像的蓝色矩形区域alpha=1,计算结果是:
α \alpha αout = α \alpha αsrc
C C Cout = C C Csrc
源图像的其他区域的alpha=0,其他区域和目标图像的黄色圆形部分相交的区域计算结果是:
α \alpha αout = α \alpha αdst
C C Cout = C C Cdst
源图像的其他区域的alpha=0,color=0x000000。目标图像的其他区域的alpha=0,color=0x000000,计算结果是:
α \alpha αout = 0
C C Cout = 0
源像素在目标像素下面绘制。
计算公式:
α \alpha αout = α \alpha αdst + (1 - α \alpha αdst) * α \alpha αsrc
C C Cout = C C Cdst + (1 - C C Cdst) * C C Csrc
在这个例子中:
目标图像的黄色圆形区域alpha=1,计算结果是:
α \alpha αout = α \alpha αdst
C C Cout = C C Cdst
目标图像的其他区域的alpha=0,color=0x000000,其他区域和源图像的蓝色矩形部分相交的区域计算结果是:
α \alpha αout = α \alpha αsrc
C C Cout = C C Csrc
目标图像的其他区域的alpha=0,color=0x000000,,源图像的其他区域的alpha=0,color=0x000000,计算结果是:
α \alpha αout = 0
C C Cout = 0
所以最终的显示效果如下所示:
保留源图像覆盖目标图像的区域的源图像的像素,其他区域的像素全部丢弃。
计算公式:
α \alpha αout = α \alpha αsrc * α \alpha αdst
C C Cout = C C Csrc * α \alpha αdst
在这个例子中:
在目标图像和源图像相交的区域,保留目标图像的像素,其他区域的像素全部丢弃。
计算公式:
α \alpha αout = α \alpha αsrc * α \alpha αdst
C C Cout = C C Cdst * α \alpha αsrc
在这个例子中:
保留源图像有像素的区域没有覆盖目标图像有像素的区域的像素。丢弃源图像有像素的区域覆盖目标图像有像素的区域的像素。丢弃所有的目标图像像素。
计算公式:
α \alpha αout = (1 - α \alpha αdst) * α \alpha αsrc
C C Cout = (1 - α \alpha αdst) * C C Csrc
在这个例子中:
保留目标图像没被源图像覆盖部分的像素。丢弃被源图像像素覆盖部分的目标图像像素。丢弃所有的源图像像素。
计算公式:
α \alpha αout = (1 - α \alpha αsrc) * α \alpha αdst
C C Cout = (1 - α \alpha αsrc) * C C Cdst
在这个例子中:
所以最终的显示效果如下所示:
丢弃源图像像素没有覆盖目标图像像素区域的源图像像素。在目标图像上绘制剩余的源图像的像素。目标图像其他区域不变。
计算公式:
α \alpha αout = α \alpha αdst
C C Cout = α \alpha αdst * C C Csrc + (1 - α \alpha αsrc) * C C Cdst
在这个例子中:
1.丢弃源图像像素没有覆盖目标图像像素区域的源图像像素。计算结果是:
α \alpha αout = 0
C C Cout = 0
2. 在目标图像上绘制剩余的源图像的像素。计算结果是:
α \alpha αout = α \alpha αdst
C C Cout = C C Csrc
3. 目标图像其他区域不变。计算结果是:
α \alpha αout = α \alpha αdst
C C Cout = C C Cdst
丢弃没有被源图像像素覆盖的目标图像的像素。在源图像的绘制剩余的目标图像的像素。源图像其他区域不变。
计算公式:
α \alpha αout = α \alpha αsrc
C C Cout = α \alpha αsrc * C C Cdst + (1 - α \alpha αdst) * C C Csrc
在这个例子中:
所以最终的显示效果如下所示:
计算结果:[ (1 - Da)*Sa + (1 - Sa)*Da, (1 - Da) *Sc + ( 1 - Sa ) * Dc],丢弃源图像像素覆盖的目标图像的像素的所有像素。绘制剩余的源图像的像素。目标图像其他区域不变。
计算公式:
α \alpha αout = (1 - α \alpha αdst) * α \alpha αsrc + (1 - α \alpha αsrc) * α \alpha αdst
C C Cout = (1 - α \alpha αdst) * C C Csrc + (1 - α \alpha αsrc) * C C Cdst
在这个例子中:
丢弃源图像像素覆盖的目标图像的像素的所有像素。计算结果:
α \alpha αout = 0
C C Cout = 0
绘制剩余的源图像的像素。计算结果:
α \alpha αout = α \alpha αsrc
C C Cout = C C Csrc
目标图像其他区域不变。计算结果:
α \alpha αout = α \alpha αdst
C C Cout = C C Cdst
保留源像素和目标像素的对应的RGB的最小分量。
计算公式:
α \alpha αout = α \alpha αsrc + α \alpha αdst - α \alpha αsrc * α \alpha αdst
C C Cout = (1 - α \alpha αdst) * C C Csrc + (1- α \alpha αsrc) * C C Cdst + min( C C Csrc, C C Cdst)
在这个例子中:
目标图像黄色区域和源图像透明区域叠加的部分。计算结果:
α \alpha αout = α \alpha αdst
C C Cout = C C Cdst
源图像蓝色区域和目标图像透明区域叠加的部分。计算结果:
α \alpha αout = α \alpha αsrc
C C Cout = C C Csrc
目标图像黄色区域和源图像蓝色区域叠加的部分。计算结果:
α \alpha αout = 1,转化成16进制就是FF
C C Cout = min( C C Csrc, C C Cdst)
这个min( C C Csrc, C C Cdst)怎么理解呢?我们看一看目标图像和源图像的色值。
目标图像黄色区域:0xFFCC44
源图像蓝色区域:0x66AAFF
RGB取各取两者之间小的那个值。是66AA44。对应的颜色如下所示:
所以最终显示结果如下所示:
保留源像素和目标像素的对应的RGB的最大分量。
计算公式:
α \alpha αout = α \alpha αsrc + α \alpha αdst - α \alpha αsrc * α \alpha αdst
C C Cout = (1 - α \alpha αdst) * C C Csrc + (1- α \alpha αsrc) * C C Cdst + max( C C Csrc, C C Cdst)
在这个例子中:
目标图像黄色区域和源图像透明区域叠加的部分。计算结果:
α \alpha αout = α \alpha αdst
C C Cout = C C Cdst
源图像蓝色区域和目标图像透明区域叠加的部分。计算结果:
α \alpha αout = α \alpha αsrc
C C Cout = C C Csrc
目标图像黄色区域和源图像蓝色区域叠加的部分。计算结果:
α \alpha αout = 1,转化成16进制就是FF
C C Cout = max( C C Csrc, C C Cdst)
这个max( C C Csrc, C C Cdst)怎么理解呢?我们看一看目标图像和源图像的色值。
目标图像:0xFFCC44
源图像:0x66AAFF
RGB取各取两者之间大的那个值。是FFCCFF。对应的颜色如下所示:
目标图像像素和源图像像素相乘。
计算公式:
α \alpha αout = α \alpha αsrc * α \alpha αdst
C C Cout = C C Csrc * C C Cdst
在这个例子中:
目标图像黄色区域和源图像透明区域叠加的部分。计算结果:
α \alpha αout = 0
C C Cout = 0
源图像黄色区域和目标图像透明区域叠加的部分。计算结果:
α \alpha αout = 0
C C Cout = 0
源图像黄色区域和目标图像蓝色区叠加的部分。计算结果:
α \alpha αout = 1
C C Cout = C C Csrc* C C Cdst
目标图像:0xFFCC44,把RGB看成0到1的浮点数就是 [1, 0.8 , 0.267]
源图像:0x66AAFF,把RGB看成0到1的浮点数就是 [0.4, 0.667 , 1]
RGB分别相乘,结果是 [0.4, 0.533 , 0.267],然后分别乘以255是[102, 136 , 68]。然后转化成RGB如下所示:
源像素和目标像素相加,然后减去源像素乘以目标像素。
计算公式:
α \alpha αout = α \alpha αsrc + α \alpha αdst - α \alpha αsrc * α \alpha αdst
C C Cout = C C Csrc + C C Cdst - C C Csrc * C C Cdst
在这个例子中:
目标图像黄色区域和源图像透明区域叠加的部分。计算结果:
α \alpha αout = α \alpha αdst
C C Cout = C C Cdst
源图像蓝色区域和目标图像透明区域叠加的部分。计算结果:
α \alpha αout = α \alpha αsrc
C C Cout = C C Csrc
源图像蓝色区域和目标图像黄色区域叠加的部分。计算结果:
α \alpha αout = 1
C C Cout = C C Csrc + C C Cdst - C C Csrc * C C Cdst
这个 C C Csrc + C C Cdst - C C Csrc * C C Cdst 怎么理解呢?我们看一看目标图像和源图像的色值。
目标图像:0xFFCC44转化成十进制是[255, 204, 68]
源图像:0x66AAFF转化成十进制是[102, 170, 255]
我们上面已经计算过 C C Csrc * C C Cdst的结果是[102, 136 , 68]。
[255, 204, 68] + [102, 170, 255] - [102, 136 , 68] = [255, 238, 255],对应的RGB如下所示:
源像素添和目标像素相加,并使结果饱和。
计算公式:
α \alpha αout = max(0, min( α \alpha αsrc + α \alpha αdst ,1))
C C Cout = max(0, min( C C Csrc + C C Cdst ,1))
在这个例子中:
1.目标图像黄色区域和源图像透明区域叠加的部分。计算结果:
α \alpha αout = 1
C C Cout = C C Cdst
2.目标图像黄色区域和源图像蓝色区域的叠的部分。计算结果:
α \alpha αout = 1,十六进制表示就是FF
C C Cout = 1,十六进制表示就是FFFFFF
3.源图像蓝色区域和目标图像透明区域叠加的部分。计算结果:
α \alpha αout = 1
C C Cout = C C Csrc
根据目标颜色对源和目标进行倍增或筛选。
计算公式:
α \alpha αout = α \alpha αsrc + α \alpha αdst - α \alpha αsrc * α \alpha αdst
C o u t = { 2 ∗ C s r c ∗ C d s t 2 ∗ C d s t < α d s t α s r c ∗ α d s t − 2 ( α d s t − C s r c ) ( α s r c − C d s t ) o t h e r w i s e C_{out}=\left\{ \begin{array}{lcl} 2*C_{src}*C_{dst} & & {2*C_{dst} < {\alpha}_{dst}}\\ {\alpha}_{src}*{\alpha}_{dst}- 2({\alpha}_{dst}-C_{src})({\alpha}_{src}-C_{dst}) & & otherwise\\ \end{array} \right. Cout={2∗Csrc∗Cdstαsrc∗αdst−2(αdst−Csrc)(αsrc−Cdst)2∗Cdst<αdstotherwise
目前还没搞明白这种模式。。。
所以最终的显示结果如下所示: