1. 程式人生 > >setXfermode() 不起作用?

setXfermode() 不起作用?

在不理解PorterDuff.Mode的幾種模式的時候,經常會導致想象出來的內容和實際畫出來的內容不太一樣,然後各種想砸手機的衝動。

舉一個例子,畫一個1/4圓的扇形,使用setXfermode()。那就是先畫一個正方形,然後使用PorterDuff.Mode.SRC_IN在正方形的右下角畫一個半徑為正方形邊長的圓,正方形是dst,圓形就是src,這太簡單了吧。

程式碼如下:

// 使用PorterDuff.Mode.SRC_IN相交的地方的展示src
private PorterDuffXfermode porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode
.SRC_IN); private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(Color.WHITE); 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); mPaint.setColor(0xffff0000); // 畫一個紅色的正方形 Rect rect = new Rect(0, 0, 200, 200); canvas.drawRect(rect, mPaint); mPaint.setXfermode
(porterDuffXfermode); mPaint.setColor(0xff00ff00); // 畫一個綠色的圓形 canvas.drawCircle(200, 200, 100, mPaint); mPaint.setXfermode(null); canvas.restoreToCount(sc); }

然後實際的效果是:



怎麼dst正方形還在?

然後看apidemo裡面的例子,是這樣的:



SrcIn確實也只是會展示一個扇形,百思不得其解,哪裡錯了呢?使用apidemo裡面的程式碼模仿寫一遍。

程式碼如下(不推薦這樣寫,onDraw裡面new了太多的物件):

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawColor(Color.WHITE);
    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);

    // 建立一個300X300的Bitmap,然後在左上角畫一個200X200的正方形
    Bitmap bitmap1 = Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888);
    Canvas c1 = new Canvas(bitmap1);
    Paint p1 = new Paint(Paint.ANTI_ALIAS_FLAG);
    p1.setColor(0xffff0000);
    c1.drawRect(new RectF(0, 0, 200, 200), p1);

    // 建立一個300X300的Bitmap,然後在右上角畫一個半徑是100的圓形
    Bitmap bitmap2 = Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888);
    Canvas c2 = new Canvas(bitmap2);
    Paint p2 = new Paint(Paint.ANTI_ALIAS_FLAG);
    p2.setColor(0xff00ff00);
    c2.drawCircle(200, 200, 100, p2);

    canvas.drawBitmap(bitmap1, 0, 0, mPaint);
    mPaint.setXfermode(porterDuffXfermode);
    canvas.drawBitmap(bitmap2, 0, 0, mPaint);
    canvas.restoreToCount(sc);
}

結果正常了。



得到了想要的扇形

為什麼會這樣呢?根據上面的程式碼過程是這樣的:
1.建立了300X300的Bitmap然後在裡面畫了一個200X200的紅色的正方形,就是這樣的。




2.同樣的方法建立了一個綠色的圓形。



3.畫的時候使用SRC_IN做處理。

看完我相信你應該知道為什麼了吧?沒錯,兩次畫的時候的Bitmap的大小是一樣的。相比於最開始直接畫正方形和圓是不一樣的。

還有一個問題,當在網上看到setXfermode()設定不同模式的時候的效果圖片的時候,大部分看到是這樣的。



最明顯的是上面的Clear並沒有完全的清掉,到底是怎麼回事,難道哪個是錯的?其實這兩個都是對的,只是一個設定了硬體加速(上面),一個沒設定(下面)。

來看看PorterDuff.Mode的文件吧。其中Sa全稱為Source alpha表示源圖的Alpha通道;Sc全稱為Source color表示源圖的顏色;Da全稱為Destination alpha表示目標圖的Alpha通道;Dc全稱為Destination color表示目標圖的顏色。最後計算的值也會是兩個值代表的是計算以後的alpha和color值。

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);
}

最後附上apidemo中的例子,推薦大家直接看自己的sdk目錄下面的:
sdk/samples/android-xx/legacy/ApiDemos/src/com/example/android/apis/graphics/Xfermodes.java就行。