Paint API之- Xfermode與PorterDuff詳解(二)


本節引言:

上一節,我們學習了Xfermode兩個已經過世(過時)的兒子:AvoidXfermodePixelXorXfermode, 雖然說有點用,但是終歸是被淘汰的了,本節我們來學習Xfermode還健在的三兒子:PorterDuffXfermode;

先祭上官方API文檔:PorterDuffXfermode !文件內容很少,我們可以看到他的建構方法:

1.png

參數只有一個:PorterDuff.Mode mode,而Android則提供了16種圖片混排模式,簡單點可以 理解為兩個圖層依照不同模式,可以組合成不同的結果顯示出來! 16種混排模式的結果圖如下:

2.png

這裡兩個圖層:先繪製的圖是目標圖(DST),後面繪製的圖是源圖(SRC)

當然,在文件中我們發現可供使用的模式並不是16種,而是18種,新增了ADDOVERLAY兩種模式!

嗯,說多也白說,程式碼最實際,本節我們寫下程式碼來驗證下這18種模式吧! 3.gif

PS:這個PorterDuff的命名其實是兩個人名的組合:Tomas Proter和 Tom Duff組成的,他們是最早在 最早在SIGGRAPH上提出圖形混合概念的大神級人物,有興趣的自行百度~


寫個例子來驗證上面的這個圖:

好的,我們來寫個範例驗證下上面這個圖,透過修改不同的模式,來對結果進行比較分析!

程式碼實作

Step 1:我們先寫個取得螢幕寬高的工具類別吧! ScreenUtil.java

/**
 * Created by Jay on 2015/10/23 0023.
 */
public class ScreenUtil {
    /**
     * 获取屏幕宽高,sdk17后不建议采用
     *
     * @param context
     */
    public static int[] getScreenHW(Context context) {
        WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        Display display = manager.getDefaultDisplay();
        int width = display.getWidth();
        int height = display.getHeight();
        int[] HW = new int[] { width, height };
        return HW;
    }

    /**
     * 获取屏幕宽高,建议采用
     *
     * @param context
     */
    public static int[] getScreenHW2(Context context) {
        WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics dm = new DisplayMetrics();
        manager.getDefaultDisplay().getMetrics(dm);
        int width = dm.widthPixels;
        int height = dm.heightPixels;
        int[] HW = new int[] { width, height };
        return HW;
    }

    /**
     * 获取屏幕的宽度
     *
     * @param context
     * @return
     */
    public static int getScreenW(Context context) {
        return getScreenHW2(context)[0];
    }

    /**
     * 获取屏幕的高度
     *
     * @param context
     * @return
     */
    public static int getScreenH(Context context) {
        return getScreenHW2(context)[1];
    }
}

Step 2:寫我們的自訂View類,在這裡做試驗! XfermodeView.java

/**
 * Created by Jay on 2015/10/23 0023.
 */
public class XfermodeView extends View {

    private PorterDuffXfermode pdXfermode;   //定义PorterDuffXfermode变量
    //定义MODE常量,等下直接改这里即可进行测试
    private static PorterDuff.Mode PD_MODE = PorterDuff.Mode.ADD;
    private int screenW, screenH; //屏幕宽高
    private int width = 200;      //绘制的图片宽高
    private int height = 200;
    private Bitmap srcBitmap, dstBitmap;     //上层SRC的Bitmap和下层Dst的Bitmap

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

    public XfermodeView(Context context, AttributeSet attrs) {
        super(context, attrs);
        screenW = ScreenUtil.getScreenW(context);
        screenH = ScreenUtil.getScreenH(context);
        //创建一个PorterDuffXfermode对象
        pdXfermode = new PorterDuffXfermode(PD_MODE);
        //实例化两个Bitmap
        srcBitmap = makeSrc(width, height);
        dstBitmap = makeDst(width, height);
    }

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


    //定义一个绘制圆形Bitmap的方法
    private 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(0xFF26AAD1);
        c.drawOval(new RectF(0, 0, w * 3 / 4, h * 3 / 4), p);
        return bm;
    }

    //定义一个绘制矩形的Bitmap的方法
    private 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(0xFFFFCE43);
        c.drawRect(w / 3, h / 3, w * 19 / 20, h * 19 / 20, p);
        return bm;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Paint paint = new Paint();
        paint.setFilterBitmap(false);
        paint.setStyle(Paint.Style.FILL);
        canvas.drawBitmap(srcBitmap, (screenW / 3 - width) / 2, (screenH / 2 - height) / 2, paint);
        canvas.drawBitmap(dstBitmap, (screenW / 3 - width) / 2 + screenW / 3, (screenH / 2 - height) / 2, paint);

        //创建一个图层,在图层上演示图形混合后的效果
        int sc = canvas.saveLayer(0, 0, screenW, screenH, null, Canvas.MATRIX_SAVE_FLAG |
                Canvas.CLIP_SAVE_FLAG |
                Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
                Canvas.FULL_COLOR_LAYER_SAVE_FLAG |
                Canvas.CLIP_TO_LAYER_SAVE_FLAG);

        canvas.drawBitmap(dstBitmap, (screenW / 3 - width) / 2 + screenW / 3 * 2,
                (screenH / 2 - height) / 2, paint);     //绘制i
        //设置Paint的Xfermode
        paint.setXfermode(pdXfermode);
        canvas.drawBitmap(srcBitmap, (screenW / 3 - width) / 2 + screenW / 3 * 2,
                (screenH / 2 - height) / 2, paint);
        paint.setXfermode(null);
        // 还原画布
        canvas.restoreToCount(sc);
    }
}

程式碼看起來好複雜是吧,其實不然,無非就是獲取了螢幕寬高,然後畫了一個矩形一個圓形, 計算了一下他們的位置,然後設定下圖層(固定寫法),接著設下畫筆setXfermode,接著 繪製到canvas而已,你看不懂的可能是繪製位置的計算吧,其實不然,位置你喜歡怎麼定 都可以!那接下來我們來一個個看下解果咯,你只要修改PD_MODE的值設定為不同模式即可!

運行效果圖

1)PorterDuff.Mode.ADD:

4.png飽和度疊加

2)PorterDuff.Mode.CLEAR:

##所繪製不會提交到畫布上,嗯結果...不知道是為什麼了,正常是沒東西的..5.png

3)PorterDuff.Mode.DARKEN:

#取兩層全部區域,交集部分顏色加深6.png

4)PorterDuff.Mode.DST:

只保留目標圖的alpha和color,所以繪製出來只有目標圖7.png

#5)PorterDuff.Mode.DST_ATOP:

源圖和目標圖相交處繪製目標圖,不相交的地方繪製來源圖8.png

6)PorterDuff.Mode.DST_IN:

兩者相交的地方繪製目標圖,繪製的效果會受到原圖處的透明度影響9.png

7)PorterDuff.Mode.DST_OUT:

在不相交的地方繪製目標圖10.png

8)PorterDuff.Mode.DST_OVER:

目標圖繪製在上面11.png

9)PorterDuff.Mode. LIGHTEN:

取兩圖層全部區域,點亮交集部分顏色12.png

10)PorterDuff.Mode.MULTIPLY:

##取兩個圖層交集部分疊加後顏色

13.png11)PorterDuff.Mode.OVERLAY:

##疊加

12)PorterDuff.Mode.SCREEN:14.png

#取兩圖層全部區域,交集部分變成透明色

13)PorterDuff.Mode.SRC:15.png

只保留來源影像的alpha和color,所以繪製出來只有來源圖

14)PorterDuff.Mode.SRC_ATOP:

000.png源圖和目標圖相交處繪製源圖,不相交的地方繪製目標圖

15)PorterDuff.Mode. SRC_IN:

17.png兩者相交的地方繪製源圖

16)PorterDuff.Mode.SRC_OUT:

18.png不相交的地方繪製來源圖

17)PorterDuff.Mode.SRC_OVER:

19.png把來源圖繪製在上面

18)PorterDuff.Mode.XOR:

20.png不相交的地方以原樣繪製來源圖和目標圖


本節範例程式碼下載:

PorterDuffXfermodeDemo.zip


#本節小結:

嗯,本節就寫了一個簡單的View來驗證這18種不同PorterDuff.Mode下的不同效果, 嘿嘿,蠻耗時間的,不過,讀者看起來肯定清晰多了是吧~當然,這只是一些初步的見解!

PorterDuffXfermodePorterDuff.Mode對於我們自訂控制項是非常重要的! 本節我們初步了解,下節我們挑幾個例子來練練手!

如果你想看關於PorterDuff.Mode更詳細的介紹可見:Android Paint之 setXfermode PorterDuffXfermode 講解,別人寫的不錯的一篇文章! 嗯,就到這裡,明早體檢,今天就寫這麼多~21.jpg