Xfermode 图层混合

972 阅读5分钟

PorterDuff.Mode是什么

public Xfermode setXfermode(Xfermode xfermode) {
    long xfermodeNative = 0;
    if (xfermode != null)
        xfermodeNative = xfermode.native_instance;
    native_setXfermode(mNativePaint, xfermodeNative);
    mXfermode = xfermode;
    return xfermode;
}

在android SDK Paint类中有一个很重要的方法setXfermode(源码如上),这个方法用于设置图像的过渡模式,所谓过渡是指图像的饱和度、颜色值等参数的计算结果的图像表现。在SDK中Xfermode有三个子类:AvoidXfermode, PixelXorXfermode和PorterDuffXfermode,前两个类在API 16被遗弃了,而且不是本文的主题内容,所以这里不作介绍。PorterDuffXfermode类主要用于图形合成时的图像过渡模式计算,其概念来自于1984年在ACM SIGGRAPH计算机图形学出版物上发表了“Compositing digital images(合成数字图像)”的Tomas Porter和Tom Duff,合成图像的概念极大地推动了图形图像学的发展,PorterDuffXfermode类名就来源于这俩人的名字组合PorterDuff。下面是android SDK中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);
    Mode(int nativeInt) {
        this.nativeInt = nativeInt;
    }
    /**
     * @hide
     */
    public final int nativeInt;
}

上面代码中每种模式的注释都说明了该模式的alpha通道和颜色值的计算方式,要理解各个模式的计算方式需要先弄明白公式中各个元素的具体含义:

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

当Alpha通道的值为1时,图像完全可见;当Alpha通道值为0时,图像完全不可见;当Alpha通道的值介于0和1之间时,图像只有一部分可见。Alpha通道描述的是图像的形状,而不是透明度。
以SCREEN的计算方式为例:[Sa + Da - Sa * Da, Sc + Dc - Sc * Dc],“[……]”里分为两部分,其中“,”前的部分“Sa + Da - Sa * Da”计算的值代表SCREEN模式的Alpha通道,而“,”后的部分“Sc + Dc - Sc * Dc”计算SCREEN模式的颜色值,图形混合后的图片依靠这个矢量来计算ARGB的值。关于图像Alpha合成的知识详见维基百科Alpha compositing


1.PorterDuff.Mode.CLEAR

所绘制不会提交到画布上。


2.PorterDuff.Mode.SRC

显示上层绘制图片


3.PorterDuff.Mode.DST

显示下层绘制图片


4.PorterDuff.Mode.SRC_OVER

正常绘制显示,上下层绘制叠盖。


5.PorterDuff.Mode.DST_OVER

上下层都显示。下层居上显示。


6.PorterDuff.Mode.SRC_IN

取两层绘制交集。显示上层。


7.PorterDuff.Mode.DST_IN

取两层绘制交集。显示下层。


8.PorterDuff.Mode.SRC_OUT

取上层绘制非交集部分。


9.PorterDuff.Mode.DST_OUT

取下层绘制非交集部分。


10.PorterDuff.Mode.SRC_ATOP

取下层非交集部分与上层交集部分


11.PorterDuff.Mode.DST_ATOP

取上层非交集部分与下层交集部分


12.PorterDuff.Mode.XOR

异或:去除两图层交集部分


13.PorterDuff.Mode.DARKEN

取两图层全部区域,交集部分颜色加深


14.PorterDuff.Mode.LIGHTEN

取两图层全部,点亮交集部分颜色


15.PorterDuff.Mode.MULTIPLY

取两图层交集部分叠加后颜色


16.PorterDuff.Mode.SCREEN

 取两图层全部区域,交集部分变为透明色

模式1

PorterDuff.Mode.DST_IN

在不使用图层混合模式时,先绘制一个矩形 和 一个圆形

public class XfermodeView extends View {

    private Paint mPaint;
    private int mWidth, mHeight;

    public XfermodeView(Context context) {
        this(context, null);
    }

    public XfermodeView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public XfermodeView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }


    private void init() {
        //初始化画笔
        mPaint = new Paint();
        mPaint.setColor(Color.RED);
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = MeasureSpec.getSize(widthMeasureSpec);
        mHeight = MeasureSpec.getSize(heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //1.ComposeShader
        //2.画笔Paint.setXfermode()
        //3.PorterDuffColorFilter

        //禁止硬件加速 某些图层模式的APi不支持加速操作
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);

        setBackgroundColor(Color.WHITE);

        //离屏绘制 这里将图像合成的处理放到离屏缓存中进行
        int layerId = canvas.saveLayer(0,0, getWidth(), getHeight(), mPaint, Canvas.ALL_SAVE_FLAG);

        //目标图
        canvas.drawBitmap(createRectBitmap(mWidth, mHeight), 0, 0, mPaint);
        //在SDK中Xfermode有三个子类:AvoidXfermode, PixelXorXfermode和PorterDuffXfermode,前两个类在API 16被遗弃了,
        // 而且不是本文的主题内容,所以这里不作介绍。PorterDuffXfermode
        //设置混合模式
        //mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        //源图,重叠区域右下角部分
        canvas.drawBitmap(createCircleBitmap(mWidth, mHeight), 0, 0, mPaint);
        //清除混合模式
        mPaint.setXfermode(null);

        canvas.restoreToCount(layerId);

    }

    //画矩形Dst
    public Bitmap createRectBitmap(int width, int height) {
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        Paint dstPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        dstPaint.setColor(0xFF66AAFF);
        canvas.drawRect(new Rect(width / 20, height / 3, 2 * width / 3, 19 * height / 20), dstPaint);
        return bitmap;
    }

    //画圆src
    public Bitmap createCircleBitmap(int width, int height) {
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        Paint scrPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        scrPaint.setColor(0xFFFFCC44);
        canvas.drawCircle(width * 2 / 4, height / 3, height / 4, scrPaint);
        return bitmap;
    }


}


正常状态




2设置混合模式 PorterDuff.Mode.SRC

显示上层绘制图片




3 设置混合模式 PorterDuff.Mode.DST

显示下层绘制图片



4. PorterDuff.Mode.SRC_OVER

正常绘制显示,上下层绘制叠盖。




5.PorterDuff.Mode.DST_OVER

上下层都显示。下层居上显示。





 6. 设置混合模式 PorterDuff.Mode.DST_IN
    PorterDuff.Mode.DST_IN 取两层绘制交集 显示下层。
    mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));



7.PorterDuff.Mode.SRC_IN

取两层绘制交集。显示上层。




8.PorterDuff.Mode.SRC_OUT

取上层绘制非交集部分。



9.PorterDuff.Mode.DST_OUT

取下层绘制非交集部分。



10.PorterDuff.Mode.SRC_ATOP

取下层非交集部分与上层交集部分



11.PorterDuff.Mode.DST_ATOP

取上层非交集部分与下层交集部分



12.PorterDuff.Mode.XOR

异或:去除两图层交集部分




13.PorterDuff.Mode.DARKEN

取两图层全部区域,交集部分颜色加深



14.PorterDuff.Mode.LIGHTEN

取两图层全部,点亮交集部分颜色





15.PorterDuff.Mode.MULTIPLY

取两图层交集部分叠加后颜色



16.PorterDuff.Mode.SCREEN

取两图层全部区域,交集部分变为透明色