PorterDuff.Mode及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。


   利用ProterBuff.Mode我们可以完成任意2D图像测操作, 比如涂鸦画板应用中的橡皮擦效果,绘制各种自定义的进度,等等很强大的效果,下面请看具体的介绍:

    1.  Xfermode:

 

  PorterDuff.Mode及Xfermode详解_第1张图片

Xfermode有三个子类 :

AvoidXfermode  指定了一个颜色和容差,强制Paint避免在它上面绘图(或者只在它上面绘图)。


PixelXorXfermode  当覆盖已有的颜色时,应用一个简单的像素异或操作。


PorterDuffXfermode  这是一个非常强大的转换模式,使用它,可以使用图像合成的16条Porter-Duff规则的任意一条来控制Paint如何与已有的Canvas图像进行交互。


要应用转换模式,可以使用setXferMode方法,如下所示:

AvoidXfermode avoid = new AvoidXfermode(Color.BLUE, 10, AvoidXfermode.Mode. AVOID);   
 borderPen.setXfermode(avoid);  


2.PorterDuff:

PorterDuff.Mode及Xfermode详解_第2张图片



首先看一下效果图(来自ApiDemos/Graphics/XferModes)


PorterDuff.Mode及Xfermode详解_第3张图片

从上面我们可以看到PorterDuff.Mode为枚举类,一共有16个枚举值:

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

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



为了更清楚地理解各个Mode的作用效果,我自己写了一个Demo,逐一验证上面的Mode。
/**
 * Created by Alex Pang on 2016/8/20.
 * 自定义View,使用PorterDuff.Mode验证图像合成效果
 */
public class PorterDuffXfermodeView extends View {
    private Paint mPaint;
    private Bitmap dstBmp, srcBmp;
    private RectF dstRect, srcRect;

    private Xfermode mXfermode;
    private PorterDuff.Mode mPorterDuffMode = PorterDuff.Mode.MULTIPLY;

    public PorterDuffXfermodeView(Context context) {
        super(context);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG);
        dstBmp = BitmapFactory.decodeResource(getResources(), R.mipmap.destination);
        srcBmp = BitmapFactory.decodeResource(getResources(), R.mipmap.source);
        mXfermode = new PorterDuffXfermode(mPorterDuffMode);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //背景色设为白色,方便比较效果
        canvas.drawColor(Color.WHITE);
        //将绘制操作保存到新的图层,因为图像合成是很昂贵的操作,将用到硬件加速,这里将图像合成的处理放到离屏缓存中进行
        int saveCount = canvas.saveLayer(srcRect, mPaint, Canvas.ALL_SAVE_FLAG);
        //绘制目标图
        canvas.drawBitmap(dstBmp, null, dstRect, mPaint);
        //设置混合模式
        mPaint.setXfermode(mXfermode);
        //绘制源图
        canvas.drawBitmap(srcBmp, null, srcRect, mPaint);
        //清除混合模式
        mPaint.setXfermode(null);
        //还原画布
        canvas.restoreToCount(saveCount);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        int width = w <= h ? w : h;
        int centerX = w/2;
        int centerY = h/2;
        int quarterWidth = width /4;
        srcRect = new RectF(centerX-quarterWidth, centerY-quarterWidth, centerX+quarterWidth, centerY+quarterWidth);
        dstRect = new RectF(centerX-quarterWidth, centerY-quarterWidth, centerX+quarterWidth, centerY+quarterWidth);
    }
}


其中用到的图片资源分别如下,destination沿图片左上到右下的对角线将图片分成两部分,左下部分是完全不透明的,右上部分是半透明的;source沿图片左下到右上的对角线将图片分成两部分,左上部分是半透明的,右下部分是完全不透明的。图中有颜色的区域外都是完全透明的,这样当两张图片合成时可以让各种情况交叉呈现,便于分析理解。

PorterDuff.Mode及Xfermode详解_第4张图片
destination
PorterDuff.Mode及Xfermode详解_第5张图片
source
CLEAR

清除模式,[0, 0],即图像中所有像素点的alpha和颜色值均为0,Demo中的实际效果就是白色背景,图略。

SRC

[Sa, Sc],只保留源图像的 alpha 和 color ,所以绘制出来只有源图,如source。有时候会感觉分不清先绘制的是源图还是后绘制的是源图,这个时候可以这么记,先绘制的是目标图。

DST

[Da, Dc],只保留了目标图像的alpha和color值,所以绘制出来的只有目标图,如destination。

SRC_OVER

[Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc],在目标图像上层绘制源图像

PorterDuff.Mode及Xfermode详解_第6张图片
PorterDuff.Mode.SRC_OVER
DST_OVER

[Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc],与SRC_OVER相反,此模式是目标图像被绘制在源图像的上方

PorterDuff.Mode及Xfermode详解_第7张图片
PorterDuff.Mode.DST_OVER
SRC_IN

[Sa * Da, Sc * Da],在两者相交的地方绘制源图像,并且绘制的效果会受到目标图像对应地方透明度的影响

PorterDuff.Mode及Xfermode详解_第8张图片
PorterDuff.Mode.SRC_IN
DST_IN

[Sa * Da, Sa * Dc],可以和SRC_IN 进行类比,在两者相交的地方绘制目标图像,并且绘制的效果会受到源图像对应地方透明度的影响

PorterDuff.Mode及Xfermode详解_第9张图片
PorterDuff.Mode.DST_IN
SRC_OUT

[Sa * (1 - Da), Sc * (1 - Da)],从字面上可以理解为在不相交的地方绘制源图像,那么我们来看看效果是不是这样,如下图。实际上color 是 Sc * ( 1 - Da ) ,表示如果相交处的目标色的alpha是完全不透明的,这时候源图像会完全被过滤掉,否则会受到相交处目标色 alpha 影响,呈现出对应色值。

PorterDuff.Mode及Xfermode详解_第10张图片
PorterDuff.Mode.SRC_OUT
DST_OUT

[Da * (1 - Sa), Dc * (1 - Sa)],可以类比SRC_OUT , 在不相交的地方绘制目标图像,相交处根据源图像alpha进行过滤,完全不透明处则完全过滤,完全透明则不过滤

PorterDuff.Mode及Xfermode详解_第11张图片
PorterDuff.Mode.DST_OUT
SRC_ATOP

[Da, Sc * Da + (1 - Sa) * Dc],源图像和目标图像相交处绘制源图像,不相交的地方绘制目标图像,并且相交处的效果会受到源图像和目标图像alpha的影响

PorterDuff.Mode及Xfermode详解_第12张图片
PorterDuff.Mode.SRC_ATOP
DST_ATOP

[Sa, Sa * Dc + Sc * (1 - Da)],源图像和目标图像相交处绘制目标图像,不相交的地方绘制源图像,并且相交处的效果会受到源图像和目标图像alpha的影响

PorterDuff.Mode及Xfermode详解_第13张图片
PorterDuff.Mode.DST_ATOP
XOR

[Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc],在不相交的地方按原样绘制源图像和目标图像,相交的地方受到对应alpha和颜色值影响,按公式进行计算,如果都完全不透明则相交处完全不绘制

PorterDuff.Mode及Xfermode详解_第14张图片
PorterDuff.Mode.XOR
DARKEN

[Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)],该模式处理过后,会感觉效果变暗,即进行对应像素的比较,取较暗值,如果色值相同则进行混合;
从算法上看,alpha值变大,色值上如果都不透明则取较暗值,非完全不透明情况下使用上面算法进行计算,受到源图和目标图对应色值和alpha值影响。

PorterDuff.Mode及Xfermode详解_第15张图片
PorterDuff.Mode.DARKEN
LIGHTEN

[Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)],可以和 DARKEN 对比起来看,DARKEN 的目的是变暗,LIGHTEN 的目的则是变亮,如果在均完全不透明的情况下,色值取源色值和目标色值中的较大值,否则按上面算法进行计算。

PorterDuff.Mode及Xfermode详解_第16张图片
PorterDuff.Mode.LIGHTEN
MULTIPLY

[Sa * Da, Sc * Dc],正片叠底,即查看每个通道中的颜色信息,并将基色与混合色复合。结果色总是较暗的颜色,任何颜色与黑色复合产生黑色,任何颜色与白色复合保持不变,当用黑色或白色以外的颜色绘画时,绘画工具绘制的连续描边产生逐渐变暗的颜色。

PorterDuff.Mode及Xfermode详解_第17张图片
PorterDuff.Mode.MULTIPLY
SCREEN

[Sa + Da - Sa * Da, Sc + Dc - Sc * Dc],滤色,滤色模式与我们所用的显示屏原理相同,所以也有版本把它翻译成屏幕;简单的说就是保留两个图层中较白的部分,较暗的部分被遮盖;当一层使用了滤色(屏幕)模式时,图层中纯黑的部分变成完全透明,纯白部分完全不透明,其他的颜色根据颜色级别产生半透明的效果。

PorterDuff.Mode及Xfermode详解_第18张图片
PorterDuff.Mode.SCREEN
ADD

Saturate(S + D),饱和度叠加

PorterDuff.Mode及Xfermode详解_第19张图片
PorterDuff.Mode.ADD
OVERLAY

像素是进行 Multiply (正片叠底)混合还是 Screen (屏幕)混合,取决于底层颜色,但底层颜色的高光与阴影部分的亮度细节会被保留

PorterDuff.Mode及Xfermode详解_第20张图片
PorterDuff.Mode.OVERLAY


相关的文章:

各个击破搞明白PorterDuff.Mode

Android Paint之 setXfermode PorterDuffXfermode 讲解





你可能感兴趣的:(android)