前言
當(dāng)時(shí)在做頭像imageView巡语,就是切圓頭像
1暂幼、如果我們先畫一個(gè)circle(非bitmap)糊肤,然后setXfermode 為Src_In病涨,再畫一個(gè)bitmap(圖片的)熬北。成功疙描,完美 。
2讶隐、如果我們先畫一個(gè)bitmap(圖片的)起胰,然后setXfermode 為Dsr_In,再畫circle。粗略學(xué)習(xí)了網(wǎng)上那張圖之后巫延,理論上應(yīng)該也是成功的效五,但是卻出現(xiàn)了問題。
所以烈评,我很疑惑火俄,就開始了各種探索學(xué)習(xí)測試。(當(dāng)然讲冠,至于為什么出現(xiàn)這種情況瓜客,文末會有解釋)
到目前為止已經(jīng)被PorterDuffXferMode坑了有1天時(shí)間了,網(wǎng)上看了無數(shù)的文章竿开,很亂很雜谱仪,沒有寫得能夠讓我很清楚很明白且有點(diǎn)權(quán)威的文獻(xiàn),因?yàn)楹芏鄾]有測試結(jié)果否彩,并且我沒有親身實(shí)踐過疯攒,所以,現(xiàn)在我要重新自己動手實(shí)踐一下列荔。
在網(wǎng)上搜羅了一大圈敬尺,在群里和很多人交流了枚尼,大概有2篇文章,個(gè)人認(rèn)為說得在理砂吞。(建議大家先去看一下署恍,不過可能你會跟我一樣,看了之后就更云里霧里蜻直,但是還是需要親身實(shí)踐為好)
感謝兩位作者盯质。
PorterDuffXferMode不正確的真正原因PorterDuffXferMode深入試驗(yàn)
Android中Canvas繪圖之PorterDuffXfermode使用及工作原理詳解
第一篇主要總結(jié):
如果想讓PorterDuffXferMode按照預(yù)期Demo(或者效果圖)的效果圖像實(shí)現(xiàn),必須滿足以下條件:
1概而、關(guān)閉硬件加速呼巷。(經(jīng)過作者修改為 開啟硬件離屏緩存)
2、兩個(gè)bitmap大小盡量一樣赎瑰。
3王悍、背景色為透明色。
4乡范、如果兩個(gè)bitmap位置不完全一樣配名,可能也是預(yù)期效果,只不過你看到的效果和你自己腦補(bǔ)的預(yù)期效果不一致晋辆。
第二篇主要總結(jié):
PorterDuffXfermode用于實(shí)現(xiàn)新繪制的像素與Canvas上對應(yīng)位置已有的像素按照混合規(guī)則進(jìn)行顏色混合渠脉。
我主要是在第一篇的結(jié)論基礎(chǔ)上去做測試,并給大家展示測試結(jié)果瓶佳。
下面高能篇幅芋膘,如果你不是真正在學(xué)習(xí)研究PorterDuffXfermode踩坑的人,如果你沒有耐心閱讀的霸饲,就可以別往下看了为朋。
亦或你也想親手嘗試,那還是先去嘗試一下吧厚脉。
首先代碼圖:
public class TestXfermodeView extends View {
public TestXfermodeView(Context context) {
super(context);
}
public TestXfermodeView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TestXfermodeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Bitmap circle = getCircleBitmap();
Bitmap rectangle = getRetangleBitmap();
// int sc = canvas.saveLayer(0, 0, 400, 400, 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);
/**
* 開啟硬件離屏緩存
*/
setLayerType(LAYER_TYPE_HARDWARE, null);
Paint paint = new Paint();
/**
* 畫bitmap的也透明
*/
canvas.drawARGB(0, 0, 0, 0);
// canvas.drawCircle(100, 100, 100, paint);
canvas.drawBitmap(rectangle, 100, 100, paint);
// Bitmap b= BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher);
// Rect rect = new Rect(0, 0, 100, 100);
// canvas.drawBitmap(b,rect, rect, paint);
// paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
// paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
// paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
// paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
// paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
// paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST));
// paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
// paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
// paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
// paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OVER));
// paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
// paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
// paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
// paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
// paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN));
// paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN));
// paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.OVERLAY));
// paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
canvas.drawBitmap(circle, 0, 0, paint);
// canvas.restoreToCount(sc);
}
@NonNull
private Bitmap getRetangleBitmap() {
/**
* bm1 在bitmap上面畫正方形
*/
Bitmap rectangle = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
Canvas c1 = new Canvas(rectangle);
Paint p1 = new Paint(Paint.ANTI_ALIAS_FLAG);
p1.setColor(getResources().getColor(R.color.colorAccent));
/**
* 設(shè)置透明
*/
c1.drawARGB(0, 0, 0, 0);
c1.drawRect(0, 0, 200, 200, p1);
return rectangle;
}
@NonNull
private Bitmap getCircleBitmap() {
/**
* bm 在bitmap上面畫圓
*/
Bitmap circle = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(circle);
/**
* 設(shè)置透明
*/
c.drawARGB(0, 0, 0, 0);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(getResources().getColor(R.color.colorPrimary));
c.drawCircle(100, 100, 100, p);
return circle;
}
}
這倆方法 就是在2個(gè)新的bitmap上畫圓和正方形
注意是跟官方demo一致 ** 先畫 正方形 后畫 圓**
滿足條件
2习寸、兩個(gè)bitmap大小盡量一樣。
3傻工、背景色為透明色霞溪。
onDraw中也設(shè)置了透明和開啟硬件離屏緩存
setLayerType(LAYER_TYPE_HARDWARE, null);
Paint paint = new Paint();
/**
* 畫bitmap的也透明
*/
canvas.drawARGB(0, 0, 0, 0);
滿足條件
1、開啟硬件離屏緩存(順便說一下它的好處)
- 1.解決xfermode黑色問題中捆。
- 2.效率比關(guān)閉硬件加速高3倍以上
好正式開始 Xfermode的條件測試:
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
可以看出圓的canvas背景(透明鸯匹,activity本身就是白色,所以這里為白色)也顯示出來并且覆蓋在了正方形上面
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST));
可以看出后畫的圓已經(jīng)不顯示了
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
可以看出圓的canvas背景顯示出來泄伪, 只取了與正方形相交的部分(是canvas背景區(qū)域相交的部分)殴蓬,并且相交部分顯示的是后畫的圓的顏色
paint.setXfermode(newPorterDuffXfermode(PorterDuff.Mode.DST_IN));
可以看出圓的canvas背景顯示出來, 只取了與正方形相交的部分(是canvas背景區(qū)域相交的部分)蟋滴,并且相交部分顯示的是先畫的正方形的顏色
paint.setXfermode(newPorterDuffXfermode(PorterDuff.Mode.XOR));
可以看出取的是相交部分之外染厅,并且與官方demo效果一致
paint.setXfermode(newPorterDuffXfermode(PorterDuff.Mode.CLEAR));
可以看出后畫的圓不見了 痘绎,并且相交的部分也不見了
這前面幾個(gè)是相對比較常用,也是比較重要的肖粮。
后面的我直接列出來
總覽全局
注:我的是跟官方demo一樣先畫正方形后畫圓简逮,第二篇文獻(xiàn)里面的圖是先畫圓后畫正方形,可以對比一下尿赚,體會一下。
我說說我們的demo和官方api demo的區(qū)別:
- 我的demo里面蕉堰,兩個(gè)bitmap大小一樣凌净,是在bitmap里面填充畫滿 circle和rectangle,并且在畫兩個(gè)Bitmap的時(shí)候,是調(diào)整的它的位置來畫的屋讶;
- 官方的demo里面冰寻,是兩個(gè)bitmap大小一樣,circle的bitmap里面只填充了 左上角2/3皿渗,而rectangle的bitmap里面只填充了 右下角的2/3斩芭,并且在畫的時(shí)候,兩個(gè)bitmap的位置大小都是一樣的
我的
canvas.drawBitmap(rectangle,**100, 100**, paint);
canvas.drawBitmap(circle, 0, 0, paint);
官方的
canvas.drawBitmap(mSrcB, **0, 0**, paint);
canvas.drawBitmap(mDstB, 0, 0, paint);
大家可以再去對比對比兩張總結(jié)圖乐疆,思考思考划乖。
如果想要探尋為什么不同的方法會導(dǎo)致不同的效果,也可以去閱讀頁首的第二篇文章
總結(jié)
如果你想要做出實(shí)際效果挤土,那么你要按照官方的那種方式琴庵,你就能夠做出跟網(wǎng)上普遍流傳的那張圖一樣的效果。
如果你要根據(jù)自己的實(shí)際情況來仰美,那么你可能就要考慮我的這種方式和官方的那種方式來決定怎么做了迷殿。
至于文首的問題,原因如下:
因?yàn)槲覀兊腦fermode 疊合裁剪咖杂,都是建立在不同的層級上庆寺,重新畫一個(gè)bitmap會新開一層。
第一種:先畫circle 在canvas那層诉字,再畫Bitmap懦尝,新開了一層,中間鑲嵌Xfermode奏窑,成功导披。
第二種: 先畫bitmap,新開了一層埃唯,再畫circle撩匕,還是在bitmap那層,中間鑲嵌 Xfermode,不成功墨叛。
解決方案:
//第一種
canvas.drawCircle(scaleBitmap.getWidth() / 2, scaleBitmap.getHeight() / 2, scaleBitmap.getWidth() / 2, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
canvas.drawBitmap(scaleBitmap, rect, rect1, paint);
//第二種
canvas.drawBitmap(scaleBitmap, rect, rect1, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
Bitmap bitmap = Bitmap.createBitmap(scaleBitmap.getWidth(), scaleBitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas1 = new Canvas(bitmap);
Paint p = new Paint();
canvas1.drawARGB(0, 0, 0, 0);
canvas1.drawCircle(scaleBitmap.getWidth() / 2, scaleBitmap.getHeight() / 2, scaleBitmap.getWidth() / 2, p);
canvas.drawBitmap(bitmap, rect, rect1, paint);
ps:寫這篇文章呢止毕,主要是我個(gè)人想要實(shí)踐一下模蜡,順便記錄一下實(shí)驗(yàn)結(jié)果。最后也順便分享出來扁凛,個(gè)人感覺這里確實(shí)有很多坑忍疾,如果你是真正在研究PorterDuffXformode的人,那你肯定會跟我一樣很疑惑谨朝,希望這篇文章能夠帶給你一個(gè)思路卤妒。