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

然后实际的效果是:


setXfermode() 不起作用?_第1张图片
怎么dst正方形还在?

然后看apidemo里面的例子,是这样的:


setXfermode() 不起作用?_第2张图片

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

结果正常了。


setXfermode() 不起作用?_第3张图片
得到了想要的扇形

为什么会这样呢?根据上面的代码过程是这样的:
1.创建了300X300的Bitmap然后在里面画了一个200X200的红色的正方形,就是这样的。


setXfermode() 不起作用?_第4张图片

2.同样的方法创建了一个绿色的圆形。

setXfermode() 不起作用?_第5张图片

3.画的时候使用SRC_IN做处理。

看完我相信你应该知道为什么了吧?没错,两次画的时候的Bitmap的大小是一样的。相比于最开始直接画正方形和圆是不一样的。

还有一个问题,当在网上看到setXfermode()设置不同模式的时候的效果图片的时候,大部分看到是这样的。


setXfermode() 不起作用?_第6张图片

最明显的是上面的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);
}

相关文章:
http://blog.csdn.net/aigestudio/article/details/41316141
https://en.wikipedia.org/wiki/Alpha_compositing
http://stackoverflow.com/questions/11337679/porterduffxfermode-clear-a-section-of-a-bitmap

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

戳我下载ApiDemo例子

你可能感兴趣的:(Android问题)