在不理解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);
}
然后实际的效果是:
然后看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的红色的正方形,就是这样的。
看完我相信你应该知道为什么了吧?没错,两次画的时候的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);
}
相关文章:
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例子