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就行。