五、Android绘制知识总结(Xfermode和硬件加速)

2 阅读3分钟

一、Xfermode

Xfermode表示图层的混合模式,用于描述两个图层之间进行融合时,像素点进行计算的规则。

在API16之前,Xfermode有3个子类:AvoidXfermode、PixelXorXfermode、PorterDuffXfermode。但在API16以后,前两个已经过时,甚至从源码里移除,所以我们只需学习PorterDuffXfermode即可。

1.1、PorterDuffXfermode

PorterDuffXfermode最早是在1984年由Porter和Duff两人发表的论文《Compositing Digital Images》中出现,所以该混合模式也根据作者来命名。

PorterDuffXfermode构造函数需要指定一个PorterDuff.Mode,而PorterDuff.Mode在以下地方都会涉及:

  • 1、ComposeShader
  • 2、Paint.setXfermode()
  • 3、PorterDuffColorFilter

它提供18种模式可选项:

PorterDuff.Mode公式
PorterDuff.Mode.CLEARalphaout=0alpha_{out} = 0
Cout=0C_{out} = 0
PorterDuff.Mode.SRCalphaout=alphasrcalpha_{out} = alpha_{src}
Cout=CsrcC_{out} = C_{src}
PorterDuff.Mode.DSTalphaout=alphadstalpha_{out} = alpha_{dst}
Cout=CdstC_{out} = C_{dst}
PorterDuff.Mode.SRC_OVERalphaout=alphasrc+(1alphasrc)alphadstalpha_{out} = alpha_{src} + (1 - alpha_{src}) * alpha_{dst}
Cout=Csrc+(1alphasrc)CdstC_{out} = C_{src} + (1 - alpha_{src}) * C_{dst}
PorterDuff.Mode.DST_OVERalphaout=alphadst+(1alphadst)alphasrcalpha_{out} = alpha_{dst} + (1 - alpha_{dst}) * alpha_{src}
Cout=Cdst+(1alphadst)CsrcC_{out} = C_{dst} + (1 - alpha_{dst}) * C_{src}
PorterDuff.Mode.SRC_INalphaout=alphasrcalphadstalpha_{out} = alpha_{src} * alpha_{dst}
Cout=CsrcalphadstC_{out} = C_{src} * alpha_{dst}
PorterDuff.Mode.DST_INalphaout=alphasrcalphadstalpha_{out} = alpha_{src} * alpha_{dst}
Cout=CdstalphasrcC_{out} = C_{dst} * alpha_{src}
PorterDuff.Mode.SRC_OUTalphaout=(1alphadst)alphasrcalpha_{out} = (1 - alpha_{dst}) * alpha_{src}
Cout=(1alphadst)CsrcC_{out} = (1 - alpha_{dst}) * C_{src}
PorterDuff.Mode.DST_OUTalphaout=(1alphasrc)alphadstalpha_{out} = (1 - alpha_{src}) * alpha_{dst}
Cout=(1alphasrc)CdstC_{out} = (1 - alpha_{src}) * C_{dst}
PorterDuff.Mode.SRC_ATOPalphaout=alphadstalpha_{out} = alpha_{dst}
Cout=alphadstCsrc+(1alphasrc)CdstC_{out} = alpha_{dst} * C_{src} + (1 - alpha_{src}) * C_{dst}
PorterDuff.Mode.DST_ATOPalphaout=alphasrcalpha_{out} = alpha_{src}
Cout=alphasrcCdst+(1alphadst)CsrcC_{out} = alpha_{src} * C_{dst} + (1 - alpha_{dst}) * C_{src}
PorterDuff.Mode.XORalphaout=(1alphadst)alphasrc+(1alphasrc)alphadstalpha_{out} = (1 - alpha_{dst}) * alpha_{src} + (1 - alpha_{src}) * alpha_{dst}
Cout=(1alphadst)Csrc+(1alphasrc)CdstC_{out} = (1 - alpha_{dst}) * C_{src} + (1 - alpha_{src}) * C_{dst}
PorterDuff.Mode.DARKENalphaout=alphasrc+alphadstalphasrcalphadstalpha_{out} = alpha_{src} + alpha_{dst} - alpha_{src} * alpha_{dst}
Cout=(1alphadst)Csrc+(1alphasrc)Cdst+min(Csrc,Cdst)C_{out} = (1 - alpha_{dst}) * C_{src} + (1 - alpha_{src}) * C_{dst} + min(C_{src}, C_{dst})
PorterDuff.Mode.LIGHTENalphaout=alphasrc+alphadstalphasrcalphadstalpha_{out} = alpha_{src} + alpha_{dst} - alpha_{src} * alpha_{dst}
Cout=(1alphadst)Csrc+(1alphasrc)Cdst+max(Csrc,Cdst)C_{out} = (1 - alpha_{dst}) * C_{src} + (1 - alpha_{src}) * C_{dst} + max(C_{src}, C_{dst})
PorterDuff.Mode.MULTIPLYalphaout=alphasrcalphadstalpha_{out} = alpha_{src} * alpha_{dst}
Cout=CsrcCdstC_{out} = C_{src} * C_{dst}
PorterDuff.Mode.SCREENalphaout=alphasrc+alphadstalphasrcalphadstalpha_{out} = alpha_{src} + alpha_{dst} - alpha_{src} * alpha_{dst}
Cout=Csrc+CdstCsrcCdstC_{out} = C_{src} + C_{dst} - C_{src} * C_{dst}
PorterDuff.Mode.ADDalphaout=max(0,min(alphasrc+alphadst,1))alpha_{out} = max(0, min(alpha_{src} + alpha_{dst}, 1))
Cout=max(0,min(Csrc+Cdst,1)C_{out} = max(0, min(C_{src} + C_{dst}, 1)
PorterDuff.Mode.OVERLAYalphaout=alphasrc+alphadstalphasrcalphadstalpha_{out} = alpha_{src} + alpha_{dst} - alpha_{src} * alpha_{dst}
Cout={2CsrcCdst2Cdst<αdstalphasrcαdst2(αdstCsrc)(αsrcCdst)otherwiseC_{out} = \begin{cases} 2 * C_{src} * C_{dst} & 2 * C_{dst} \lt \alpha_{dst} \\ alpha_{src} * \alpha_{dst} - 2 (\alpha_{dst} - C_{src}) (\alpha_{src} - C_{dst}) & otherwise \end{cases}

各种模式下的效果如下图所示:

  • 1、如果原图和目标图大小一致(图中,两者大小均占满整个小方格区域): image.png

  • 2、如果原图和目标图大小不一致(图中,两者大小为可见大小): image.png

这里可以发现,两种效果是不一样的,谷歌官方给的是第一种,但是,通常情况应该是第二种,具体原因可参考该文章

比如我们画一个矩形,应该按第二种效果来考虑,因为源图和目标图大小不一致;如果画相同大小的Bitmap,则按第一种做。

在实际应用中,我们可以从以下三个方面来决定使用哪种模式:

1、目标图像和源图像混合,需不需要生成颜色的叠加特效。如果需要,则从颜色叠加相关模式中选择,有Mode.ADD(饱和度相加)、Mode.DARKEN(变暗)、Mode.LIGHTEN(变亮)、Mode.MULTIPLY (正片叠底)、Mode.OVERLAY(叠加)、Mode.SCREEN(滤色)。
2、当不需要特效,而需要根据某张图片的透明像素来裁剪时,就需要使用SRC相关模式或DST相关模式了。而SRC相关模式与DST相关模式是相通的,唯一不同的是决定当前哪个图像是目标图像和源图像。
3、当需要清空图像时,使用Mode.CLEAR模式。

1.1、实例:

二、硬件加速

2.1、View的绘制模型

1、没有硬件加速:

invalidate the view hierarchy ------> draw the view hierarchy

2、有硬件加速:

invalidate the view hierarchy ------> record and update the display list ------> draw the display list

2.2、开启硬件加速后的异常

1、绘制不正确:可能使用了不支持硬件加速的操作, 需要关闭硬件加速或者绕过该操作

2、抛出异常:可能使用了不支持硬件加速的操作, 需要关闭硬件加速或者绕过该操作

2.3、禁用GPU硬件加速

在Android系统中,有4个不同级别的打开或者关闭硬件加速操作:

1、Application级别:

<application android:hardwareAccelerated="false">

默认为true,用于控制这个app是否开启硬件加速。

2、Activity级别:

<activity android:hardwareAccelerated="false">

3、Window级别:(只支持开启操作)

getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);

4、View级别:(只支持关闭操作)

view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
LAYER_TYPE_HARDWARE,使用硬件加速(GPU)进行绘制
LAYER_TYPE_SOFTWARE,使用CPU进行绘制

或者布局文件中,指定以下属性:

android:layerType="software"

2.4、检测是否开启硬件加速

1、view.isHardwareAccelerated()

如果返回true,表示view挂在一个开启了硬件加速的Window之下,也就意味着,它在绘制时,并不一定开启了硬件加速。

2、canvas.isHardwareAccelerated()

如果返回true,因为着canvas在绘制的时候启用了硬件加速,尽量采用此方法来判断是否开启了硬件加速。