Android自定義View之PorterDuff.Mode

在自定義View的時候,少不了遇到由兩個圖片(兩種顏色等)拼接、混合而成,這個時候就會用到PorterDuff.Mode 這個類。 如下爲Mode的源碼:android

package android.graphics;

public class PorterDuff {

    // these value must match their native equivalents. See SkXfermode.h
    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      (16),
        /** [Sa + Da - Sa*Da,
             Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)] */
        LIGHTEN     (17),
        /** [Sa * Da, Sc * Dc] */
        MULTIPLY    (13),
        /** [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] */
        SCREEN      (14),
        /** Saturate(S + D) */
        ADD         (12),
        OVERLAY     (15);

        Mode(int nativeInt) {
            this.nativeInt = nativeInt;
        }

        /**
         * @hide
         */
        public final int nativeInt;
    }

    /**
     * @hide
     */
    public static final int modeToInt(Mode mode) {
        return mode.nativeInt;
    }

    /**
     * @hide
     */
    public static final Mode intToMode(int val) {
        switch (val) {
            default:
            case  0: return Mode.CLEAR;
            case  1: return Mode.SRC;
            case  2: return Mode.DST;
            case  3: return Mode.SRC_OVER;
            case  4: return Mode.DST_OVER;
            case  5: return Mode.SRC_IN;
            case  6: return Mode.DST_IN;
            case  7: return Mode.SRC_OUT;
            case  8: return Mode.DST_OUT;
            case  9: return Mode.SRC_ATOP;
            case 10: return Mode.DST_ATOP;
            case 11: return Mode.XOR;
            case 16: return Mode.DARKEN;
            case 17: return Mode.LIGHTEN;
            case 13: return Mode.MULTIPLY;
            case 14: return Mode.SCREEN;
            case 12: return Mode.ADD;
            case 15: return Mode.OVERLAY;
        }
    }
}

從源碼上看各類類型都是由四個變量計算而來:算法

Sa:全稱爲Source alpha,表示源圖的Alpha通道;spring

Sc:全稱爲Source color,表示源圖的顏色;canvas

Da:全稱爲Destination alpha,表示目標圖的Alpha通道;ide

Dc:全稱爲Destination color,表示目標圖的顏色.工具

1)CLEARui

清除模式,[0, 0],即圖像中全部像素點的alpha和顏色值均爲0,Demo中的實際效果就是白色背景this

2)SRC.net

[Sa, Sc],只保留源圖像的 alpha 和 color ,因此繪製出來只有源圖,如source。有時候會感受分不清先繪製的是源圖仍是後繪製的是源圖,這個時候能夠這麼記,先繪製的是目標圖。翻譯

3)DST

[Da, Dc],只保留了目標圖像的alpha和color值,因此繪製出來的只有目標圖

4)SRC_OVER

[Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc],在目標圖像上層繪製源圖像

5)DST_OVER

[Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc],與SRC_OVER相反,此模式是目標圖像被繪製在源圖像的上方

6)SRC_IN

[Sa * Da, Sc * Da],在二者相交的地方繪製源圖像,而且繪製的效果會受到目標圖像對應地方透明度的影響

7)DST_IN

[Sa * Da, Sa * Dc],能夠和SRC_IN 進行類比,在二者相交的地方繪製目標圖像,而且繪製的效果會受到源圖像對應地方透明度的影響

8)SRC_OUT

[Sa * (1 - Da), Sc * (1 - Da)],從字面上能夠理解爲在不相交的地方繪製源圖像,那麼咱們來看看效果是否是這樣。實際上color 是 Sc * ( 1 - Da ) ,表示若是相交處的目標色的alpha是徹底不透明的,這時候源圖像會徹底被過濾掉,不然會受到相交處目標色 alpha 影響,呈現出對應色值。

9)DST_OUT

[Da * (1 - Sa), Dc * (1 - Sa)],能夠類比SRC_OUT , 在不相交的地方繪製目標圖像,相交處根據源圖像alpha進行過濾,徹底不透明處則徹底過濾,徹底透明則不過濾

10)SRC_ATOP

[Da, Sc * Da + (1 - Sa) * Dc],源圖像和目標圖像相交處繪製源圖像,不相交的地方繪製目標圖像,而且相交處的效果會受到源圖像和目標圖像alpha的影響

11)DST_ATOP

[Sa, Sa * Dc + Sc * (1 - Da)],源圖像和目標圖像相交處繪製目標圖像,不相交的地方繪製源圖像,而且相交處的效果會受到源圖像和目標圖像alpha的影響

12)XOR

[Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc],在不相交的地方按原樣繪製源圖像和目標圖像,相交的地方受到對應alpha和顏色值影響,按公式進行計算,若是都徹底不透明則相交處徹底不繪製

13)DARKEN

[Sa + Da - SaDa, Sc(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)],該模式處理事後,會感受效果變暗,即進行對應像素的比較,取較暗值,若是色值相同則進行混合; 從算法上看,alpha值變大,色值上若是都不透明則取較暗值,非徹底不透明狀況下使用上面算法進行計算,受到源圖和目標圖對應色值和alpha值影響。

14)LIGHTEN

[Sa + Da - SaDa, Sc(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)],能夠和 DARKEN 對比起來看,DARKEN 的目的是變暗,LIGHTEN 的目的則是變亮,若是在均徹底不透明的狀況下,色值取源色值和目標色值中的較大值,不然按上面算法進行計算。

15)MULTIPLY

[Sa * Da, Sc * Dc],正片疊底,即查看每一個通道中的顏色信息,並將基色與混合色複合。結果色老是較暗的顏色,任何顏色與黑色複合產生黑色,任何顏色與白色複合保持不變,當用黑色或白色之外的顏色繪畫時,繪畫工具繪製的連續描邊產生逐漸變暗的顏色。

16)SCREEN

[Sa + Da - Sa * Da, Sc + Dc - Sc * Dc],濾色,濾色模式與咱們所用的顯示屏原理相同,因此也有版本把它翻譯成屏幕;簡單的說就是保留兩個圖層中較白的部分,較暗的部分被遮蓋;當一層使用了濾色(屏幕)模式時,圖層中純黑的部分變成徹底透明,純白部分徹底不透明,其餘的顏色根據顏色級別產生半透明的效果。

17)ADD

Saturate(S + D),飽和度疊加

18)OVERLAY(覆蓋)

像素是進行 Multiply (正片疊底)混合仍是 Screen (屏幕)混合,取決於底層顏色,但底層顏色的高光與陰影部分的亮度細節會被保留

輸入圖片說明

public class CustomView extends View {
    private int width = 800;
    private int height = 800;
    private Bitmap dstBmp;
    private Bitmap srcBmp;
    private Paint mPaint;

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
       setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        srcBmp = makeSrc(width, height);
        dstBmp = makeDst(width, height);
        mPaint = new Paint();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
//        canvas.drawColor(Color.WHITE);
        int layerID = canvas.saveLayer(0,0,width*2,height*2,mPaint,Canvas.ALL_SAVE_FLAG);

        //兩個bitmap的位置相同
        canvas.drawBitmap(dstBmp, 0, 0, mPaint);
//        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
        canvas.drawBitmap(srcBmp,0,0, mPaint);

//        mPaint.setColor(0xFFFFCC44);
//        canvas.drawCircle(width/2,height/2,width/4,mPaint);
//        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
//        mPaint.setColor(0xFF66AAFF);
//        canvas.drawRect(width/2,height/2, width, height, mPaint);



        mPaint.setXfermode(null);
        canvas.restoreToCount(layerID);

    }

    // create a bitmap with a circle, used for the "dst" image
    static Bitmap makeDst(int w, int h) {
        Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bm);
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);

        p.setColor(0xFFFFCC44);
       //此處座標不要隨意更改
        c.drawOval(new RectF(0, 0, w*3/4, h*3/4), p);
        return bm;
    }

    // create a bitmap with a rect, used for the "src" image
    static Bitmap makeSrc(int w, int h) {
        Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bm);
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);

        p.setColor(0xFF66AAFF);
      //此處座標不要隨意更改
        c.drawRect(w/3, h/3, w*19/20, h*19/20, p);
        return bm;
    }

若將座標位置更改以下:

public class CustomView extends View {
    private int width = 800;
    private int height = 800;
    private Bitmap dstBmp;
    private Bitmap srcBmp;
    private Paint mPaint;

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
       setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        srcBmp = makeSrc(width, height);
        dstBmp = makeDst(width, height);
        mPaint = new Paint();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
//        canvas.drawColor(Color.WHITE);
        int layerID = canvas.saveLayer(0,0,width*2,height*2,mPaint,Canvas.ALL_SAVE_FLAG);

        //兩個bitmap的位置不相同
        canvas.drawBitmap(dstBmp, 0, 0, mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
        canvas.drawBitmap(srcBmp,width/2,height/2, mPaint);



        mPaint.setXfermode(null);
        canvas.restoreToCount(layerID);

    }

// create a bitmap with a circle, used for the "dst" image
    static Bitmap makeDst(int w, int h) {
        Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bm);
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);

        p.setColor(0xFFFFCC44);
        c.drawOval(new RectF(0, 0, w, h), p);
        return bm;
    }

    // create a bitmap with a rect, used for the "src" image
    static Bitmap makeSrc(int w, int h) {
        Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bm);
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);

        p.setColor(0xFF66AAFF);
        c.drawRect(0, 0, w, h, p);
        return bm;
    }

則運行結果以下:

輸入圖片說明

另外,若是目標(圓形)圖片和源(矩形)圖片相互交互,又會出現不同的狀況

參考連接:http://www.jianshu.com/p/d11892bbe055

http://blog.csdn.net/iispring/article/details/50472485

相關文章
相關標籤/搜索