PorterDuffXferMode不正確的真正原因PorterDuffXferMode深入試驗)

菜雞wing遇敵PorterDuffXferMode,不料過於輕敵,應戰吃力。隨後與其大戰三天三夜,三百餘回合不分勝負。幸得 @咪咪控 相助,僥倖獲勝。

關鍵字:PorterDuffXferMode  錯誤 不正確  不達到預期  bug


上一篇帶來一個使用PorterDuffXferMode  做的 水波紋loadingview,中間遇到了點小困難。


(說人話)  PorterDuffXferMode總是不能按照效果圖預期的效果執行。關於PorterDuffXferMode的錯誤顯示是一個對初學者十分深的坑,到底是bug呢,還是有需要注意的地方呢。這裏就跟隨我 帶上手電筒,去一探究竟。


轉載請註明出處:http://blog.csdn.NET/wingichoy/article/details/50534175


首先,大家都知道有一個圖片:


然後,大部分時候 是看到了覺得很神奇,就躍躍欲試,尤其是src_in  和dstIn可以實現遮罩效果,例如圓角圖片 圓形圖片都用了這種模式。

於是就挨個測試各種模式是否生效,結果往往不能達到預期效果。我們來做個測試。

從最簡單的開始:

1.直接在canvas上面繪製圖形

  1. @Override  
  2.   protected void onDraw(Canvas canvas) {  
  3.       //dst  
  4.       canvas.drawRect(20,20,80,80,mDstPaint);  
  5.   
  6.       //src  
  7.       canvas.drawCircle(30,30,30,mSrcPaint);  
  8.         
  9.   }  

原圖效果是這樣的:


現在加一個mode上來,XOR

  1. @Override  
  2. protected void onDraw(Canvas canvas) {  
  3.     //dst  
  4.     canvas.drawRect(20,20,80,80,mDstPaint);  
  5.     mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));  
  6.     //src  
  7.     canvas.drawCircle(30,30,30,mSrcPaint);  
  8.   
  9. }  
跑起來的結果是這樣的:

WTF!!?? 這是什麼鬼。不應該是相交部分消失嗎。 網上說“硬件加速”對這個有影響,那麼在構造器裏關閉硬件加速試一下:

  1. public TestView(Context context, AttributeSet attrs, int defStyleAttr) {  
  2.        super(context, attrs, defStyleAttr);  
  3.        mDstPaint = new Paint();  
  4.        mSrcPaint = new Paint();  
  5.        mDstPaint.setColor(Color.YELLOW);  
  6.        mSrcPaint.setColor(Color.BLUE);  
  7.        setLayerType(View.LAYER_TYPE_SOFTWARE, null);  
  8.    }  

運行的結果:

這下正常了。相交的部分消失了。

結論1:硬件加速對PorterDuffXferMode有影響,使用前請關閉硬件加速。

那麼這下真的天下太平了嗎?nonono~不要太天真,不然怎麼能叫萬丈深淵呢。

繼續試驗其他模式:  將模式改爲SRC_IN


WTF?????跟效果圖根本不一致好嗎!!!! 在試試DST_IN


你確定你沒有在逗我????  怎麼是這個鬼東西。  (當時鼓搗了我三天四夜,一直在日狗,不過先別急,慢慢來。)

爲什麼一定要按照那個效果圖來呢。。。 因爲特麼的那個圖是官方的一個demo。。 那麼我們就來看看這個demo長什麼樣!

  1. package io.appium.android.apis.graphics;  
  2.   
  3. import android.content.Context;  
  4. import android.graphics.Bitmap;  
  5. import android.graphics.BitmapShader;  
  6. import android.graphics.Canvas;  
  7. import android.graphics.Color;  
  8. import android.graphics.Matrix;  
  9. import android.graphics.Paint;  
  10. import android.graphics.PorterDuff;  
  11. import android.graphics.PorterDuffXfermode;  
  12. import android.graphics.RectF;  
  13. import android.graphics.Shader;  
  14. import android.graphics.Xfermode;  
  15. import android.os.Bundle;  
  16. import android.view.View;  
  17.   
  18. public class Xfermodes extends GraphicsActivity {  
  19.   
  20.     // create a bitmap with a circle, used for the "dst" image  
  21.     static Bitmap makeDst(int w, int h) {  
  22.         Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);  
  23.         Canvas c = new Canvas(bm);  
  24.         Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);  
  25.   
  26.         p.setColor(0xFFFFCC44);  
  27.         c.drawOval(new RectF(00, w*3/4, h*3/4), p);  
  28.         return bm;  
  29.     }  
  30.   
  31.     // create a bitmap with a rect, used for the "src" image  
  32.     static Bitmap makeSrc(int w, int h) {  
  33.         Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);  
  34.         Canvas c = new Canvas(bm);  
  35.         Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);  
  36.   
  37.         p.setColor(0xFF66AAFF);  
  38.         c.drawRect(w/3, h/3, w*19/20, h*19/20, p);  
  39.         return bm;  
  40.     }  
  41.   
  42.     @Override  
  43.     protected void onCreate(Bundle savedInstanceState) {  
  44.         super.onCreate(savedInstanceState);  
  45.         setContentView(new SampleView(this));  
  46.     }  
  47.   
  48.     private static class SampleView extends View {  
  49.         private static final int W = 64;  
  50.         private static final int H = 64;  
  51.         private static final int ROW_MAX = 4;   // number of samples per row  
  52.   
  53.         private Bitmap mSrcB;  
  54.         private Bitmap mDstB;  
  55.         private Shader mBG;     // background checker-board pattern  
  56.   
  57.         private static final Xfermode[] sModes = {  
  58.             new PorterDuffXfermode(PorterDuff.Mode.CLEAR),  
  59.             new PorterDuffXfermode(PorterDuff.Mode.SRC),  
  60.             new PorterDuffXfermode(PorterDuff.Mode.DST),  
  61.             new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),  
  62.             new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),  
  63.             new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),  
  64.             new PorterDuffXfermode(PorterDuff.Mode.DST_IN),  
  65.             new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),  
  66.             new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),  
  67.             new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),  
  68.             new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),  
  69.             new PorterDuffXfermode(PorterDuff.Mode.XOR),  
  70.             new PorterDuffXfermode(PorterDuff.Mode.DARKEN),  
  71.             new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),  
  72.             new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),  
  73.             new PorterDuffXfermode(PorterDuff.Mode.SCREEN)  
  74.         };  
  75.   
  76.         private static final String[] sLabels = {  
  77.             "Clear""Src""Dst""SrcOver",  
  78.             "DstOver""SrcIn""DstIn""SrcOut",  
  79.             "DstOut""SrcATop""DstATop""Xor",  
  80.             "Darken""Lighten""Multiply""Screen"  
  81.         };  
  82.   
  83.         public SampleView(Context context) {  
  84.             super(context);  
  85.   
  86.             mSrcB = makeSrc(W, H);  
  87.             mDstB = makeDst(W, H);  
  88.   
  89.             // make a ckeckerboard pattern  
  90.             Bitmap bm = Bitmap.createBitmap(new int[] { 0xFFFFFFFF0xFFCCCCCC,  
  91.                                             0xFFCCCCCC0xFFFFFFFF }, 22,  
  92.                                             Bitmap.Config.RGB_565);  
  93.             mBG = new BitmapShader(bm,  
  94.                                    Shader.TileMode.REPEAT,  
  95.                                    Shader.TileMode.REPEAT);  
  96.             Matrix m = new Matrix();  
  97.             m.setScale(66);  
  98.             mBG.setLocalMatrix(m);  
  99.         }  
  100.   
  101.         @Override protected void onDraw(Canvas canvas) {  
  102.             canvas.drawColor(Color.WHITE);  
  103.   
  104.             Paint labelP = new Paint(Paint.ANTI_ALIAS_FLAG);  
  105.             labelP.setTextAlign(Paint.Align.CENTER);  
  106.   
  107.             Paint paint = new Paint();  
  108.             paint.setFilterBitmap(false);  
  109.   
  110.             canvas.translate(1535);  
  111.   
  112.             int x = 0;  
  113.             int y = 0;  
  114.             for (int i = 0; i < sModes.length; i++) {  
  115.                 // draw the border  
  116.                 paint.setStyle(Paint.Style.STROKE);  
  117.                 paint.setShader(null);  
  118.                 canvas.drawRect(x - 0.5f, y - 0.5f,  
  119.                                 x + W + 0.5f, y + H + 0.5f, paint);  
  120.   
  121.                 // draw the checker-board pattern  
  122.                 paint.setStyle(Paint.Style.FILL);  
  123.                 paint.setShader(mBG);  
  124.                 canvas.drawRect(x, y, x + W, y + H, paint);  
  125.   
  126.                 // draw the src/dst example into our offscreen bitmap  
  127.                 int sc = canvas.saveLayer(x, y, x + W, y + H, null,  
  128.                                           Canvas.MATRIX_SAVE_FLAG |  
  129.                                           Canvas.CLIP_SAVE_FLAG |  
  130.                                           Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |  
  131.                                           Canvas.FULL_COLOR_LAYER_SAVE_FLAG |  
  132.                                           Canvas.CLIP_TO_LAYER_SAVE_FLAG);  
  133.                 canvas.translate(x, y);  
  134.                 canvas.drawBitmap(mDstB, 00, paint);  
  135.                 paint.setXfermode(sModes[i]);  
  136.                 canvas.drawBitmap(mSrcB, 00, paint);  
  137.                 paint.setXfermode(null);  
  138.                 canvas.restoreToCount(sc);  
  139.   
  140.                 // draw the label  
  141.                 canvas.drawText(sLabels[i],  
  142.                                 x + W/2, y - labelP.getTextSize()/2, labelP);  
  143.   
  144.                 x += W + 10;  
  145.   
  146.                 // wrap around when we've drawn enough for one row  
  147.                 if ((i % ROW_MAX) == ROW_MAX - 1) {  
  148.                     x = 0;  
  149.                     y += H + 30;  
  150.                 }  
  151.             }  
  152.         }  
  153.     }  
  154. }  


一點一點看,截取onDraw裏面的片段,這裏

  1. canvas.drawBitmap(mDstB, 00, paint);  
  2.                 paint.setXfermode(sModes[i]);  
  3.                 canvas.drawBitmap(mSrcB, 00, paint);  
  4.                 paint.setXfermode(null);  
他是畫了兩個bitmap。網上有人說好像只對bitmap生效,那到底是不是這樣呢。我們來試驗一下。


我們新定義一個canvas  再定義一個bitmap   現在bitmap上畫出來src  然後將bitmap畫到canvas上:

  1. public TestView(Context context, AttributeSet attrs, int defStyleAttr) {  
  2.        super(context, attrs, defStyleAttr);  
  3.        mDstPaint = new Paint();  
  4.        mSrcPaint = new Paint();  
  5.        mDstPaint.setColor(Color.YELLOW);  
  6.        mSrcPaint.setColor(Color.BLUE);  
  7.        setLayerType(View.LAYER_TYPE_SOFTWARE, null);  
  8.        mSrcBitmap = Bitmap.createBitmap(50,50, Bitmap.Config.ARGB_8888);  
  9.        mCanvas = new Canvas(mSrcBitmap);  
  10.    }  

  1. @Override  
  2.    protected void onDraw(Canvas canvas) {  
  3.        //dst  
  4.        canvas.drawRect(20,20,80,80,mDstPaint);  
  5.   
  6.        //src  
  7. /        mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));  
  8.   
  9.   
  10.        mCanvas.drawCircle(25,25,25,mSrcPaint);  
  11.   
  12.        canvas.drawBitmap(mSrcBitmap,0,0,null);  
  13.   
  14.   
  15.    }  
現在的效果是這樣的:



加一個XOR 試試。。

日了狗了!!!!!沒反應啊,到底是什麼鬼。

是不是兩個都需要bitmap纔可以呢,再創建一個dstBitmap和dstCanvas?

  1. mDstBitmap =  Bitmap.createBitmap(50,50, Bitmap.Config.ARGB_8888);  
  2. mDstCanvas = new Canvas(mDstBitmap);  

加一個XOR 試試

  1. @Override  
  2.    protected void onDraw(Canvas canvas) {  
  3.        //dst  
  4.        mDstCanvas.drawRect(20,20,80,80,mDstPaint);  
  5.   
  6.        canvas.drawBitmap(mDstBitmap,0,0,mDstPaint);  
  7.   
  8.        //src  
  9.        mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));  
  10.   
  11.   
  12.        mSrcCanvas.drawCircle(25,25,25,mSrcPaint);  
  13.   
  14.        canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint);  
  15.   
  16.   
  17.    }  
效果如下:


終於他媽的出來了!!!!那其他效果呢?

clear


一致的!!!!好激動有沒有!!!!搞了4天 越來越接近結論了!!!


結論2:只有兩個bitmap的時候,纔可以生效。

不要高興太早。。如果坑到這裏就完了,那還叫坑麼。

繼續試。。嗯 好多模式都是一致的。

直到!!!SRC_IN和DST_IN ,會發現。。。都消失了。 爲毛呢??

檢查代碼  發現 在往bitmap畫圓之前就set了mode  這樣會有影響

糾正

  1. @Override  
  2.    protected void onDraw(Canvas canvas) {  
  3.        //dst  
  4.        mDstCanvas.drawRect(20,20,80,80,mDstPaint);  
  5.   
  6.        canvas.drawBitmap(mDstBitmap,0,0,mDstPaint);  
  7.   
  8.        //src  
  9. mSrcCanvas.drawCircle(25,25,25,mSrcPaint);  
  10.        //再畫圓之後 設置mode  
  11.        mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));  
  12.   
  13.   
  14.         
  15.        canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint);  
  16.   
  17.   
  18.    }  

發現


艹!!!!!!終於好了!!!!!!!!經過不懈努力!!!撒花!*★,°*:.☆\( ̄▽ ̄)/$:*.°★* 。 





其實我們剛纔bitmap的大小是一樣的。 然後都是從0,0開始 完全覆蓋了。

那麼錯開一點點 是什麼效果呢,調整代碼如下

  1. protected void onDraw(Canvas canvas) {  
  2.         //dst  
  3.         mDstCanvas.drawRect(20,20,80,80,mDstPaint);  
  4.   
  5.         canvas.drawBitmap(mDstBitmap,20,20,mDstPaint);  
  6.   
  7.         //src  
  8.   
  9.   
  10.         mSrcCanvas.drawCircle(25,25,25,mSrcPaint);  
  11.   
  12.         mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));  
  13.         canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint);  
  14.   
  15.   
  16.     }  



效果如下:



可以看有效果了!!!! 但是是一個什麼鬼!!!  矩形缺角??加藍色一點??


這樣看是很難看出效果的。。來調整一下bitmap的底色 和矩形的大小:

把兩個bitmap的底色都畫成灰色, 矩形不要佔滿畫布 留出空間

  1. mDstCanvas.drawColor(Color.GRAY);  

  1. @Override  
  2.   protected void onDraw(Canvas canvas) {  
  3.       //dst  黃色  
  4.       mDstCanvas.drawRect(0,0,40,40,mDstPaint);  
  5.   
  6.       canvas.drawBitmap(mDstBitmap,20,20,mDstPaint);  
  7.   
  8.       //src   藍色  
  9.   
  10.   
  11.       mSrcCanvas.drawCircle(25,25,25,mSrcPaint);  
  12.   
  13.       canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint);  
  14.   
  15.   
  16.   }  


效果如下。。  嗯 這樣就是兩個bitmap了。。很明瞭  bitmap的內容 位置,

然後再搞SRC_IN模式

dst_in

 

那麼把bitmap底色去掉。 改成透明的呢?

dst_in:


SRC_IN:




總結3:兩個bitmap位置不完全重疊的效果如上,並不能總結出效果,要按實際效果來。


---------------------------------------------------------------------------------------------------------華麗麗的分割線-----------------------------------------------------------------------------------------

最終大總結,如果想讓PorterDuffXferMode按照預期Demo(或者效果圖)的效果圖像實現,必須滿足以下條件:


1、關閉硬件加速。

2、兩個bitmap大小盡量一樣。

3、背景色爲透明色。

4、如果兩個bitmap位置不完全一樣,可能也是預期效果,只不過你看到的效果和你自己腦補的預期效果不一致。


最後想再說幾句。鼓搗這個模式鼓搗了幾乎一週,每天晚上下班都在搞。查了無數資料。但是好多不完整,甚至有一些誤導性。所以爲了避免後來者入坑。親自試驗,儘量總結。 如果有說的不正確的地方請及時向我提出。我會及時改正。


如果本文幫助到了你,請點一個頂,或者評論一下,蟹蟹!!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章