xfermode的基本用法

作者: AxeChen | 来源:发表于2018-01-14 23:59 被阅读329次

    1、Xfermode是什么

    在Android自定义控件或者对图片等进行处理时需要做一些图像混合的操作时,会用到xfermode。利用xfermode可以做出许多有趣的UI效果时。比如做不同形状的头像,刮刮卡。
    在做这些效果之前需要先了解xfermode的使用。

    2、Xfermode的基本用法

    2.1、Xfermode的API

    xfermode有三个子类:AvoidXfermode, PixelXorXfermode和PorterDuffXfermode。其中AvoidXfermode, PixelXorXfermode已经过时不推荐使用。那么PorterDuffXfermode则是需要了解的东西。

    2.2、Xfermode的基本概念

    一下的一张图和一段伪代码可以理解PorterDuffXfermode的基本概念。

    [图片上传失败...(image-912ba6-1515977399270)]

    Xfermode理解起来并不是很难,根据上面的图可以理解为,两个不同的像素点。通过Xfermode的不同的混合模式混合之后展示出来的新的像素点效果。(注意这里是针对每一个像素的混合效果。而且这两个像素点需要是在画布上的同一位置,可以理解为重叠)

    伪代码可以这样表示:

    // 初始化PorterDuffXfermode
    private PorterDuffXfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
    
    // 在ondraw中使用PorterDuffXfermode
     protected void onDraw(Canvas canvas) {
    
            // DstBitmap SRCBitmap 为两个不同的bitmap
            canvas.drawBitmap(DstBitmap,0,0,mPaint);
            // PorterDuffXfermode和paint联用
            mPaint.setXfermode(xfermode);
            canvas.drawBitmap(SRCBitmap,0,0,mPaint);
            
            // 将xfermode制空
            mPaint.setXfermode(null);
        }
    

    以上的代码也比较简单理解:
    先draw一个bitmap,然后设置paint的xfermode,然后在画第二个bitmap。这样他们重叠的部分就会出现不通过的UI效果了。

    2.3、Xfermode的多种混合模式

    PorterDuffXfermode的构造函数:

    public PorterDuffXfermode(PorterDuff.Mode mode) {}
    

    参数传入了PorterDuff.Mode mode,以下列出PorterDuff的所以的Mode。

    public enum Mode {
        /** [0, 0] */
        CLEAR       (0),
        /** [Sa, Sc] */
        SRC         (1),
        /** [Da, Dc] */
        DST         (2),
        /** [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] */
        SRC_OVER    (3),
        /** [Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc] */
        DST_OVER    (4),
        /** [Sa * Da, Sc * Da] */
        SRC_IN      (5),
        /** [Sa * Da, Sa * Dc] */
        DST_IN      (6),
        /** [Sa * (1 - Da), Sc * (1 - Da)] */
        SRC_OUT     (7),
        /** [Da * (1 - Sa), Dc * (1 - Sa)] */
        DST_OUT     (8),
        /** [Da, Sc * Da + (1 - Sa) * Dc] */
        SRC_ATOP    (9),
        /** [Sa, Sa * Dc + Sc * (1 - Da)] */
        DST_ATOP    (10),
        /** [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] */
        XOR         (11),
        /** [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)] */
        DARKEN      (12),
        /** [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)] */
        LIGHTEN     (13),
        /** [Sa * Da, Sc * Dc] */
        MULTIPLY    (14),
        /** [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] */
        SCREEN      (15),
        /** Saturate(S + D) */
        ADD         (16),
        OVERLAY     (17);
    }
    
    

    注释中表明了这些模式的算法。
    在了解这些算法之前需要先了解像素颜色通道。

    一个像素的颜色是由四个分量组成即:ARGB
    A为透明度通道。RGB为颜色通道。

    A : 像素点的透明度通道 值为0-1f
    R : 像素点红色通道 值为0-250f
    G :像素点绿色通道 值为0-250f
    B :像素点蓝色通道 值为0-250f
    

    如果某个像素点的通道值越大则改像素点所占颜色比例越多。比如A(透明通道)A值越小就越透明。A为0就完全透明,A为1f就是完全不透明。当然这个概念在xfermode中只要了解就行了。xfermode不会去改变这些值。

    那么注释中的sa、sc、da、 dc可以如下理解

    S为源图 D为目标图
    Sa:全称为Source alpha,表示源图的Alpha通道;
    Sc:全称为Source color,表示源图的颜色;
    Da:全称为Destination alpha,表示目标图的Alpha通道;
    Dc:全称为Destination color,表示目标图的颜色.
    

    表示一个源图片的像素点: [Sa , Sc]
    表示一个目标图片的像素点: [Da , Dc]
    然后根据不同的模式的算法就可以生成混合后的像素点了。当然混合后的像素点也是用[A , B]这种格式表示。

    2.4、xfermode模式算法的示例

    不同的模式有不同的算法。算不同最后的结果也不同。

    image

    官方的贴图非常形象的展示出各种混合模式使用后展示的效果。

    接下来挑出一个常用的例子SRC_IN来解释下这些算法的基本应用。
    圆形头像实现的方式可能有很多。比如用bitmapshader等等。使用xfermode同样能实现。

    看下SRC_IN注释的算法方式。

    SRC_IN      (5),
    /** [Sa * Da, Sa * Dc] */
    

    SRC_IN的算法是这样的:

    (a)、Sa * Da:源图(S)像素透明度和目标图片(D)像素的透明的决定混合后像素的透明度
    (b)、Sa * Dc:源图(S)像素透明度和目标图片(D)像素的颜色决定混合后像素的颜色

    那么混合的图解:

    SRC_IN图解

    从(a)(b)可以看出,源图片只采用了透明度的变化。混合后图像像素的透明度和颜色都和源图的像素的透明度的有关。如果源图的像素是透明的,那么混合后的像素为透明。反之不透明。所以源图为:

    源图片(S)

    从(b)可以看出,决定混合后图像素颜色是由目标图片(D)决定的。所以目标图片是:

    目标图片(D)

    这里主要是理解算法:[Sa * Da, Sa * Dc]
    最后效果:

    TIM截图20180122152840.png

    示例代码:

    public class CustomHeadView extends View {
    
        private Bitmap DBitmap;
    
        private Bitmap SBitmap;
    
        private Paint mPaint;
    
        private PorterDuffXfermode xfermode;
    
        public CustomHeadView(Context context) {
            this(context, null);
        }
    
        public CustomHeadView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public CustomHeadView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init(){
            mPaint = new Paint();
            // 这个图片是正常的头像
            DBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.head_d,null);
            // 这个图片是中间一个圆,四个角透明的图片
            SBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.head_s,null);
            xfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
    
            canvas.drawBitmap(SBitmap,0,0,mPaint);
            mPaint.setXfermode(xfermode);
            canvas.drawBitmap(DBitmap,0,0,mPaint);
    
            mPaint.setXfermode(null);
            canvas.restoreToCount(layerId);
        }
    }
    
    

    上述例子已经非常清晰的说明了xfermode的算法:

    源图(S)和目标图(D)像素的透明度和颜色,通过特定的算法来算出混合后新图的透明度和颜色。(注意这里是对每个像素进行操作)

    这里只是举了一个例子,如果需要了解更多的xfermode的效果可以看下这个博客:
    https://www.jianshu.com/p/d11892bbe055

    2.5、关闭硬件加速

    有时候会发现,显示出来的东西就是一个纯色,没有图片。可能是没有关闭硬件加速。
    这个API因为不支持硬件加速在API 16已经过时了(大家可以在HardwareAccel查看那些方法不支持硬件加速)如果想在高于API 16的机子上进行测试,必须现在应用或手机设置中关闭硬件加速。
    这里我就不多做介绍了,看下这篇博客吧: https://www.cnblogs.com/libertycode/p/6290497.html

    3、一些常用PorterDuffXfermode的例子

    3.1、各种形状的图形

    使用xfermode来完成圆形头像只是其中之一。如果有特殊要求,想弄成其他的形状都是可以的。
    如果我上面写的圆形图片的例子能够理解,那么其他的各种形状的例子使用的方法是一样的。

    各种形状的图形
    3.2、刮刮卡效果

    实际上实现一个效果并不是说只能采用一种叠加模式。用不同的模式也能做到相同的效果。
    这里展示的刮刮卡效果,采用DST_OUT模式。
    还有一个撕掉美女的衣服和刮刮卡类似,但是用的是SRC_OUT模式。

    这里的源图和目标图有点不同,源图或者目标图是手指滑动的路径。毕竟划过的路径变成透明了。

    刮刮卡效果

    这里撕掉美女衣服太过底图太“火爆”,动图就不截取了。

    3.3、xfermode不仅仅以上的效果。其他的就不一一介绍。

    4、这个不知道起个什么标题好,就记录下我学习xfermode遇到的问题把。

    1、 使用xfermode可能会纠结源图和目标图的问题。

     这个确实是这样的。具体哪个做源图和目标图要根据具体
     实现的效果和不同混合模式的算法去确定。 
    

    2、使用xfermode是针对图片的像素的。

     前面提到多次,操作的是像素。
    

    3、要调试多次才能达到效果。

    这个问题我遇到多次,觉得xfermode有毒。   
    

    4、这篇文章只是基础,而且写的还有点烂。

     好的文章应该是下一篇!
    

    源码地址:https://github.com/AxeChen/XfermodeSimple

    原文来自掘金:https://juejin.im/post/5a5aca3a6fb9a01cbf384ea7
    作者:尘封的落叶(就是我自己。)

    相关文章

      网友评论

      • 小鸟看天空:大佬 AvoidXfermode api 26之后找不到了,求解
        小鸟看天空:@尘封的落叶 好的,我测试了一下,只有在23的时候才能使用,26的时候有其他方法替代了
        AxeChen:@小鸟看天空 老铁 ,这个我也没有去研究过,我写这个的时候估计我还没用到26的api。你好好研究下吧。:smile:
      • 5v_:求番号
        AxeChen:ADSDFJ-FDFD23424 老铁,拿去吧

      本文标题:xfermode的基本用法

      本文链接:https://www.haomeiwen.com/subject/msaloxtx.html