此文已由作者游葳授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
写在开头
随着应用开发的深入,视觉同学在完成了页面的基本设计后,再也按耐不住心中的寂寞,开始对各种细节不满意,于是乎就会提出各种视觉优化的方案。作为开发人员,啥也别说了,你懂的,有困难要上,没困难,制造困难也要上。既然是优化提升的方案,那很多时候只使用系统提供的各种控件,或者只是简单的用Paint去进行图形颜色的绘制,已经满足不了视觉同志的胃口了,这就要求我们必须掌握Paint的进阶技巧,比如本文介绍的图像混合技术 - PorterDuffXfermode。
PorterDuffXfermode 简介
相信很多android开发同学和我一样,第一次看到这个ProterDuff单词都会觉得奇怪,这是个啥子意思呢。作为一个猪场员工,我当然是立刻马上用有道词典翻译了一下,结果啥也没搜出来。后来上网查了才知道,ProterDuff是两个人名的组合: Tomas Proter和 Tom Duff. 这两个人在1984年一起写了一篇名为《Compositing Digital Images》的论文。我们知道,一个像素是由ARGB四个分量组成的,该论文就论述了如何实现不同数字图像的像素之间是如何进行混合的,并提出了多种像素混合的模式。PorterDuffXfermode支持以下十几种像素颜色的混合模式,分别为:
CLEAR 计算方式:[0, 0],效果:清除
SRC 计算方式:[Sa, Sc];效果:只绘制源图像
DST 计算方式:[Da, Dc];效果:只绘制目标图像
SRC_OVER 计算方式:[Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] 说明:在目标图像的上方绘制源图像
DST_OVER 计算方式:[Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc];说明:在源图像的上方绘制目标图像
SRC_IN 计算方式:[Sa * Da, Sc * Da];说明:只在源图像和目标图像相交的地方绘制目标图像
DST_IN 计算方式:[Sa * Da, Sa * Dc];说明:只在源图像和目标图像相交的地方绘制目标图像
SRC_OUT 计算方式:[Sa * (1 - Da), Sc * (1 - Da)];说明:只在目标图像和源图像不相交的地方绘制目标图像
DST_OUT 计算方式:[Da * (1 - Sa), Dc * (1 - Sa)];说明:只在源图像和目标图像不相交的地方绘制源图像
SRC_ATOP 计算方式:[Da, Sc * Da + (1 - Sa) * Dc];效果:在目标图像和源图像相交的地方绘制源图像而在不相交的地方绘制目标图像
DST_ATOP 计算方式:[Sa, Sa * Dc + Sc * (1 - Da)];效果:在源图像和目标图像相交的地方绘制目标图像而在不相交的地方绘制源图像
XOR 计算方式:[Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc];说明:在源图像和目标图像不相交的地方各自绘制,在重叠的地方不绘制任何内容
DARKEN 计算方式:[Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)];说明:变暗
LIGHTEN 计算方式:[Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)];说明:变亮
MULTIPLY 计算方式:[Sa * Da, Sc * Dc];说明:混合
ADD 计算方式:Saturate(S + D);说明:饱和度相加
S代表源像素,源像素的颜色值表示为[Sa, Sc],Sa中的a是alpha的缩写,Sa表示源像素的Alpha值,Sc中的c是颜色color的缩写,Sc表示源像素的RGB。D代表目标像素,目标像素的颜色值表示为[Da, Dc],Da表示目标像素的Alpha值,Dc表示目标像素的RGB。
合成后[]逗号前面的这一部分的值代表计算后的Alpha通道,而逗号后的这一部分的值代表计算后的颜色值,图形混合后的图片依靠这个矢量来计算ARGB的值。
一张容易被误解的神图
相信很多人在用到PorterDuffXfermode的时候都有看过这张图吧,这张图是Android的sdk下自带的API的Demo示例。但是如果按照这张图的示例进行开发的话,有时可能会达不到预期效果。比如第一种的CLEAR效果,乍一看该图,CLEAR达到的效果应该是把dst和src的图片全部都清空了,但这个其实是不对的,因为PorterDuffXfermode 的机制就是src与dst进行各种混合变化,在超出src范围内的区域是不起作用的,所以CLEAR只是把src所包含部分清除了,但是在图上看了,却是整个图层上啥都没有了,这个又是为什么呢?
这个秘密就藏在Demo的源码中,打开位于/Users/netease/Library/Android/sdk/samples/android-19/legacy/ApiDemos/src/com/example/android/apis/graphics目录下的Xfermodes.java文件,示例中创建dst和src图片的源码如下:
    // create a bitmap with a circle, used for the "dst" image    static Bitmap makeDst(int w, int 
                    