[TOC]android
類android.graphics.PorterDuffXfermode繼承自android.graphics.Xfermode。在用Android中的Canvas進行繪圖時,能夠經過使用PorterDuffXfermode將所繪製的圖形的像素與Canvas中對應位置的像素按照必定規則進行混合,造成新的像素值,從而更新Canvas中最終的像素顏色值,這樣會建立不少有趣的效果。當使用PorterDuffXfermode時,須要將將其做爲參數傳給Paint.setXfermode(Xfermode xfermode)方法,這樣在用該畫筆paint進行繪圖時,Android就會使用傳入的PorterDuffXfermode,若是不想再使用Xfermode,那麼能夠執行Paint.setXfermode(null)。
上面的概述中咱們提煉出兩點:canvas
借用google官方demo中的圖bash
不少同窗在測試過程當中發現和demo的實現有很大的出入,其實咱們仔細看官方demo,就會發現有很大不一樣,我將核心代碼抽取以下:ide
static 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(0xFFFFCC44);
c.drawOval(new RectF(0, 0, w * 3 / 4, h * 3 / 4), p);
return bm;
}
// create a bitmap with a rect, used for the "src" image
static 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(0xFF66AAFF);
c.drawRect(w / 3, h / 3, w * 19 / 20, h * 19 / 20, p);
return bm;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GREEN);
int sc = canvas.saveLayer(0, 0, W, H, null, Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(makeDst(W, H), 0, 0, mPaint);
//mPaint.setXfermode(sModes[mIndex]);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(makeSrc(W, H), 0, 0, mPaint);
mPaint.setXfermode(null);
canvas.restoreToCount(sc);
}
複製代碼
代碼中有幾個核心的地方咱們要注意:測試
這是由於在調用saveLayer時,會生成了一個全新的bitmap,這個bitmap的大小就是咱們指定的保存區域的大小,新生成的bitmap是全透明的,在調用saveLayer後全部的繪圖操做都是在這個bitmap上進行的。ui
沒有saveLayer的繪圖流程google
因爲咱們先把整個畫布給染成了綠色,而後再畫上了一個圓形,因此在應用xfermode來畫源圖像的時候,目標圖像當前Bitmap上的全部圖像了,也就是整個綠色的屏幕和一個圓形了。因此這時候源圖像的相交區域是沒有透明像素的,透明度全是100%,這也就不難解釋結果是這樣的緣由了。spa
調用saveLayer的目的就是讓你的目標圖像獨立的存在於一個layer上,而不受原始畫布圖像的干擾。這樣才能進行目標圖像和源圖像的對應位置的像素混合。3d
注:rest
測試一 使用saveLayer獲得的正確圖像以下
測試二 不使用saveLayer
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GREEN);
//int sc = canvas.saveLayer(0, 0, W, H, null, Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(makeDst(W, H), 0, 0, mPaint);
//mPaint.setXfermode(sModes[mIndex]);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(makeSrc(W, H), 0, 0, mPaint);
mPaint.setXfermode(null);
//canvas.restoreToCount(sc);
}
複製代碼
獲得的圖像以下:
PorterDuff.Mode.SRC_IN的含義是在二者相交的地方繪製源圖像,由於沒有使用saveLayer因此咱們目標圖像是綠色的背景+黃色的圓,源圖像是藍色正方形。
那麼這裏有兩個錯誤點:
爲何獲得的是黑色圖像,而不是透明顏色,其實我沒弄明白明白,可是咱們知道了正確使用PorterDuffXfermode的代碼以下:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
.......
int sc = canvas.saveLayer(0, 0, W, H, null, Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(makeDst(W, H), 0, 0, mPaint);
//繪製你的目標圖像
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//繪製你的源圖像
mPaint.setXfermode(null);
canvas.restoreToCount(sc);
.......
}
複製代碼
測試三 你真的瞭解SRC_IN嗎?
咱們在測試一的基礎上修改一段代碼
static 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(0x7fFFCC44);
c.drawOval(new RectF(0, 0, w * 3 / 4, h * 3 / 4), p);
return bm;
}
複製代碼
將目標圖像中0xFFFFCC44改成0x7fFFCC44,也就透明度改成0.5
右邊是沒有修改透明度的圖像,左邊是修改後的;咱們發現SRC_IN的結果發生了變化。讓咱們來看坎SRC_IN完整的解釋:
細節決定成敗,小小的細節頗有可能成爲你平常工做中的大坑,更多PorterDuff.Mode含義,請參考。
測試四 LAYER_TYPE_SOFTWARE使用
網上有很多說使用LAYER_TYPE_SOFTWARE的,咱們也來實踐一下:
初始化時添加以下代碼,使用軟件加速
setLayerType(LAYER_TYPE_SOFTWARE, null);
複製代碼
測試代碼仍是使用調用了saveLayer的代碼,可是使用CLEAR模式
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GREEN);
int sc = canvas.saveLayer(0, 0, W, H, null, Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(makeDst(W, H), 0, 0, mPaint);
//mPaint.setXfermode(sModes[mIndex]);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawBitmap(makeSrc(W, H), 0, 0, mPaint);
mPaint.setXfermode(null);
canvas.restoreToCount(sc);
}
複製代碼
左邊是沒有設置setLayerType獲得的混合圖像,符合CLEAR模式的預期;右邊是設置LAYER_TYPE_SOFTWARE後獲得的圖像,咱們發現混合後所有變成透明的了,這是爲何呢?我給出這樣一個猜測,在沒有設置LAYER_TYPE_SOFTWARE時,只有有效圖像區域參與像素混合,透明區域不參與像素混合;而設置LAYER_TYPE_SOFTWARE後透明區域也參與像素混合了。爲了驗證這一點,咱們進行測試五。
測試五
在測試四的基礎上,目標bitmap和源bitmap是同樣大的,咱們的測試方法是將源bitmap的大小修改成只有目標bitmap的1/4,這樣一來源bitmap將只佔據整個圖像左上角1/4區域,獲得的測試結果以下:
根據咱們的猜測,源bitmap的整個區域(透明區域和圖像區域)都參與到與目標圖像的像素混合中,致使左上角1/4區域變爲了透明的。
以上分析根據我的經驗得出,並不透徹,若有不對,望指正。