android PorterDuffXferMode真正的效果測試集合(對比官方demo)

前言

當(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));
PorterDuff.Mode.SRC

可以看出圓的canvas背景(透明鸯匹,activity本身就是白色,所以這里為白色)也顯示出來并且覆蓋在了正方形上面

 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST));
PorterDuff.Mode.DST

可以看出后畫的圓已經(jīng)不顯示了

paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));

PorterDuff.Mode.SRC_IN

可以看出圓的canvas背景顯示出來泄伪, 只取了與正方形相交的部分(是canvas背景區(qū)域相交的部分)殴蓬,并且相交部分顯示的是后畫的顏色

 paint.setXfermode(newPorterDuffXfermode(PorterDuff.Mode.DST_IN));

PorterDuff.Mode.DST_IN

可以看出圓的canvas背景顯示出來, 只取了與正方形相交的部分(是canvas背景區(qū)域相交的部分)蟋滴,并且相交部分顯示的是先畫正方形的顏色

 paint.setXfermode(newPorterDuffXfermode(PorterDuff.Mode.XOR));

PorterDuff.Mode.XOR

可以看出取的是相交部分之外染厅,并且與官方demo效果一致

 paint.setXfermode(newPorterDuffXfermode(PorterDuff.Mode.CLEAR));

PorterDuff.Mode.CLEAR

可以看出后畫不見了 痘绎,并且相交的部分也不見了

這前面幾個(gè)是相對比較常用,也是比較重要的肖粮。

后面的我直接列出來

PorterDuff.Mode.SRC_OUT
PorterDuff.Mode.SRC_ATOP
PorterDuff.Mode.SRC_OVER
PorterDuff.Mode.DST_OUT
PorterDuff.Mode.DST_ATOP
PorterDuff.Mode.DST_OVER
PorterDuff.Mode.ADD
PorterDuff.Mode.MUTIPLY
PorterDuff.Mode.DARKEN
PorterDuff.Mode.OVERPLAY
PorterDuff.Mode.SCREEN

總覽全局

官方demo
我的
第二篇文獻(xiàn)里面的

注:我的是跟官方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的位置大小都是一樣的

我的

circle的bitmap
rectangle的bitmap
canvas.drawBitmap(rectangle,**100, 100**, paint);
canvas.drawBitmap(circle, 0, 0, paint);

官方的


circle的bitmap
rectangle的bitmap
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è)思路卤妒。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市字币,隨后出現(xiàn)的幾起案子则披,更是在濱河造成了極大的恐慌,老刑警劉巖洗出,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件士复,死亡現(xiàn)場離奇詭異,居然都是意外死亡翩活,警方通過查閱死者的電腦和手機(jī)阱洪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來菠镇,“玉大人冗荸,你說我怎么就攤上這事±#” “怎么了俏竞?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長堂竟。 經(jīng)常有香客問我魂毁,道長,這世上最難降的妖魔是什么出嘹? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任席楚,我火速辦了婚禮,結(jié)果婚禮上税稼,老公的妹妹穿的比我還像新娘烦秩。我一直安慰自己,他們只是感情好郎仆,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布只祠。 她就那樣靜靜地躺著,像睡著了一般扰肌。 火紅的嫁衣襯著肌膚如雪抛寝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天,我揣著相機(jī)與錄音盗舰,去河邊找鬼晶府。 笑死,一個(gè)胖子當(dāng)著我的面吹牛钻趋,可吹牛的內(nèi)容都是我干的川陆。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼蛮位,長吁一口氣:“原來是場噩夢啊……” “哼较沪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起失仁,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤购对,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后陶因,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡垂蜗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年楷扬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贴见。...
    茶點(diǎn)故事閱讀 38,617評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡烘苹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出片部,到底是詐尸還是另有隱情镣衡,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布档悠,位于F島的核電站廊鸥,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏辖所。R本人自食惡果不足惜惰说,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望缘回。 院中可真熱鬧吆视,春花似錦、人聲如沸酥宴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拙寡。三九已至授滓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背褒墨。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工炫刷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人郁妈。 一個(gè)月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓浑玛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親噩咪。 傳聞我的和親對象是個(gè)殘疾皇子顾彰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容