您的位置:首页 > Web前端

PorterDuffXferMode不正确的真正原因PorterDuffXferMode深入试验)

2016-10-13 18:28 411 查看
版权声明:本文为博主原创文章,欢迎注明出处后转载。

菜鸡wing遇敌PorterDuffXferMode,不料过于轻敌,应战吃力。随后与其大战三天三夜,三百余回合不分胜负。幸得
@咪咪控 相助,侥幸获胜。

关键字:PorterDuffXferMode  错误 不正确  不达到预期  bug

上一篇带来一个使用PorterDuffXferMode  做的 水波纹loadingview,中间遇到了点小困难。

(说人话)  PorterDuffXferMode总是不能按照效果图预期的效果执行。关于PorterDuffXferMode的错误显示是一个对初学者十分深的坑,到底是bug呢,还是有需要注意的地方呢。这里就跟随我 带上手电筒,去一探究竟。

转载请注明出处:http://blog.csdn.NET/wingichoy/article/details/50534175

首先,大家都知道有一个图片:



然后,大部分时候 是看到了觉得很神奇,就跃跃欲试,尤其是src_in  和dstIn可以实现遮罩效果,例如圆角图片 圆形图片都用了这种模式。

于是就挨个测试各种模式是否生效,结果往往不能达到预期效果。我们来做个测试。

从最简单的开始:

1.直接在canvas上面绘制图形

[java]
view plain
copy





@Override  
  protected void onDraw(Canvas canvas) {  
      //dst  
      canvas.drawRect(20,20,80,80,mDstPaint);  
  
      //src  
      canvas.drawCircle(30,30,30,mSrcPaint);  
        
  }  

原图效果是这样的:



现在加一个mode上来,XOR

[java]
view plain
copy





@Override  
protected void onDraw(Canvas canvas) {  
    //dst  
    canvas.drawRect(20,20,80,80,mDstPaint);  
    mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));  
    //src  
    canvas.drawCircle(30,30,30,mSrcPaint);  
  
}  

跑起来的结果是这样的:



WTF!!?? 这是什么鬼。不应该是相交部分消失吗。 网上说“硬件加速”对这个有影响,那么在构造器里关闭硬件加速试一下:

[java]
view plain
copy





public TestView(Context context, AttributeSet attrs, int defStyleAttr) {  
       super(context, attrs, defStyleAttr);  
       mDstPaint = new Paint();  
       mSrcPaint = new Paint();  
       mDstPaint.setColor(Color.YELLOW);  
       mSrcPaint.setColor(Color.BLUE);  
       setLayerType(View.LAYER_TYPE_SOFTWARE, null);  
   }  

运行的结果:



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

结论1:硬件加速对PorterDuffXferMode有影响,使用前请关闭硬件加速。

那么这下真的天下太平了吗?nonono~不要太天真,不然怎么能叫万丈深渊呢。

继续试验其他模式:  将模式改为SRC_IN



WTF?????跟效果图根本不一致好吗!!!! 在试试DST_IN



你确定你没有在逗我????  怎么是这个鬼东西。  (当时鼓捣了我三天四夜,一直在日狗,不过先别急,慢慢来。)

为什么一定要按照那个效果图来呢。。。 因为特么的那个图是官方的一个demo。。 那么我们就来看看这个demo长什么样!

[java]
view plain
copy





package io.appium.android.apis.graphics;  
  
import android.content.Context;  
import android.graphics.Bitmap;  
import android.graphics.BitmapShader;  
import android.graphics.Canvas;  
import android.graphics.Color;  
import android.graphics.Matrix;  
import android.graphics.Paint;  
import android.graphics.PorterDuff;  
import android.graphics.PorterDuffXfermode;  
import android.graphics.RectF;  
import android.graphics.Shader;  
import android.graphics.Xfermode;  
import android.os.Bundle;  
import android.view.View;  
  
public class Xfermodes extends GraphicsActivity {  
  
    // create a bitmap with a circle, used for the "dst" image  
    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 onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(new SampleView(this));  
    }  
  
    private static class SampleView extends View {  
        private static final int W = 64;  
        private static final int H = 64;  
        private static final int ROW_MAX = 4;   // number of samples per row  
  
        private Bitmap mSrcB;  
        private Bitmap mDstB;  
        private Shader mBG;     // background checker-board pattern  
  
        private static final Xfermode[] sModes = {  
            new PorterDuffXfermode(PorterDuff.Mode.CLEAR),  
            new PorterDuffXfermode(PorterDuff.Mode.SRC),  
            new PorterDuffXfermode(PorterDuff.Mode.DST),  
            new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),  
            new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),  
            new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),  
            new PorterDuffXfermode(PorterDuff.Mode.DST_IN),  
            new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),  
            new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),  
            new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),  
            new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),  
            new PorterDuffXfermode(PorterDuff.Mode.XOR),  
            new PorterDuffXfermode(PorterDuff.Mode.DARKEN),  
            new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),  
            new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),  
            new PorterDuffXfermode(PorterDuff.Mode.SCREEN)  
        };  
  
        private static final String[] sLabels = {  
            "Clear", "Src", "Dst", "SrcOver",  
            "DstOver", "SrcIn", "DstIn", "SrcOut",  
            "DstOut", "SrcATop", "DstATop", "Xor",  
            "Darken", "Lighten", "Multiply", "Screen"  
        };  
  
        public SampleView(Context context) {  
            super(context);  
  
            mSrcB = makeSrc(W, H);  
            mDstB = makeDst(W, H);  
  
            // make a ckeckerboard pattern  
            Bitmap bm = Bitmap.createBitmap(new int[] { 0xFFFFFFFF, 0xFFCCCCCC,  
                                            0xFFCCCCCC, 0xFFFFFFFF }, 2, 2,  
                                            Bitmap.Config.RGB_565);  
            mBG = new BitmapShader(bm,  
                                   Shader.TileMode.REPEAT,  
                                   Shader.TileMode.REPEAT);  
            Matrix m = new Matrix();  
            m.setScale(6, 6);  
            mBG.setLocalMatrix(m);  
        }  
  
        @Override protected void onDraw(Canvas canvas) {  
            canvas.drawColor(Color.WHITE);  
  
            Paint labelP = new Paint(Paint.ANTI_ALIAS_FLAG);  
            labelP.setTextAlign(Paint.Align.CENTER);  
  
            Paint paint = new Paint();  
            paint.setFilterBitmap(false);  
  
            canvas.translate(15, 35);  
  
            int x = 0;  
            int y = 0;  
            for (int i = 0; i < sModes.length; i++) {  
                // draw the border  
                paint.setStyle(Paint.Style.STROKE);  
                paint.setShader(null);  
                canvas.drawRect(x - 0.5f, y - 0.5f,  
                                x + W + 0.5f, y + H + 0.5f, paint);  
  
                // draw the checker-board pattern  
                paint.setStyle(Paint.Style.FILL);  
                paint.setShader(mBG);  
                canvas.drawRect(x, y, x + W, y + H, paint);  
  
                // draw the src/dst example into our offscreen bitmap  
                int sc = canvas.saveLayer(x, y, x + W, y + H, 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);  
                canvas.translate(x, y);  
                canvas.drawBitmap(mDstB, 0, 0, paint);  
                paint.setXfermode(sModes[i]);  
                canvas.drawBitmap(mSrcB, 0, 0, paint);  
                paint.setXfermode(null);  
                canvas.restoreToCount(sc);  
  
                // draw the label  
                canvas.drawText(sLabels[i],  
                                x + W/2, y - labelP.getTextSize()/2, labelP);  
  
                x += W + 10;  
  
                // wrap around when we've drawn enough for one row  
                if ((i % ROW_MAX) == ROW_MAX - 1) {  
                    x = 0;  
                    y += H + 30;  
                }  
            }  
        }  
    }  
}  

一点一点看,截取onDraw里面的片段,这里

[java]
view plain
copy





canvas.drawBitmap(mDstB, 0, 0, paint);  
                paint.setXfermode(sModes[i]);  
                canvas.drawBitmap(mSrcB, 0, 0, paint);  
                paint.setXfermode(null);  

他是画了两个bitmap。网上有人说好像只对bitmap生效,那到底是不是这样呢。我们来试验一下。

我们新定义一个canvas  再定义一个bitmap   现在bitmap上画出来src  然后将bitmap画到canvas上:

[java]
view plain
copy





public TestView(Context context, AttributeSet attrs, int defStyleAttr) {  
       super(context, attrs, defStyleAttr);  
       mDstPaint = new Paint();  
       mSrcPaint = new Paint();  
       mDstPaint.setColor(Color.YELLOW);  
       mSrcPaint.setColor(Color.BLUE);  
       setLayerType(View.LAYER_TYPE_SOFTWARE, null);  
       mSrcBitmap = Bitmap.createBitmap(50,50, Bitmap.Config.ARGB_8888);  
       mCanvas = new Canvas(mSrcBitmap);  
   }  

[java]
view plain
copy





@Override  
   protected void onDraw(Canvas canvas) {  
       //dst  
       canvas.drawRect(20,20,80,80,mDstPaint);  
  
       //src  
/        mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));  
  
  
       mCanvas.drawCircle(25,25,25,mSrcPaint);  
  
       canvas.drawBitmap(mSrcBitmap,0,0,null);  
  
  
   }  

现在的效果是这样的:



加一个XOR 试试。。

日了狗了!!!!!没反应啊,到底是什么鬼。



是不是两个都需要bitmap才可以呢,再创建一个dstBitmap和dstCanvas?

[java]
view plain
copy





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

加一个XOR 试试

[java]
view plain
copy





@Override  
   protected void onDraw(Canvas canvas) {  
       //dst  
       mDstCanvas.drawRect(20,20,80,80,mDstPaint);  
  
       canvas.drawBitmap(mDstBitmap,0,0,mDstPaint);  
  
       //src  
       mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));  
  
  
       mSrcCanvas.drawCircle(25,25,25,mSrcPaint);  
  
       canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint);  
  
  
   }  

效果如下:



终于他妈的出来了!!!!那其他效果呢?

clear



一致的!!!!好激动有没有!!!!搞了4天 越来越接近结论了!!!

结论2:只有两个bitmap的时候,才可以生效。

不要高兴太早。。如果坑到这里就完了,那还叫坑么。

继续试。。嗯 好多模式都是一致的。

直到!!!SRC_IN和DST_IN ,会发现。。。都消失了。 为毛呢??

检查代码  发现 在往bitmap画圆之前就set了mode  这样会有影响

纠正

[java]
view plain
copy





@Override  
   protected void onDraw(Canvas canvas) {  
       //dst  
       mDstCanvas.drawRect(20,20,80,80,mDstPaint);  
  
       canvas.drawBitmap(mDstBitmap,0,0,mDstPaint);  
  
       //src  
mSrcCanvas.drawCircle(25,25,25,mSrcPaint);  
       //再画圆之后 设置mode  
       mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));  
  
  
        
       canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint);  
  
  
   }  

发现



艹!!!!!!终于好了!!!!!!!!经过不懈努力!!!撒花!*★,°*:.☆\( ̄▽ ̄)/$:*.°★* 。 

其实我们刚才bitmap的大小是一样的。 然后都是从0,0开始 完全覆盖了。

那么错开一点点 是什么效果呢,调整代码如下

[java]
view plain
copy





protected void onDraw(Canvas canvas) {  
        //dst  
        mDstCanvas.drawRect(20,20,80,80,mDstPaint);  
  
        canvas.drawBitmap(mDstBitmap,20,20,mDstPaint);  
  
        //src  
  
  
        mSrcCanvas.drawCircle(25,25,25,mSrcPaint);  
  
        mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));  
        canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint);  
  
  
    }  

效果如下:



可以看有效果了!!!! 但是是一个什么鬼!!!  矩形缺角??加蓝色一点??

这样看是很难看出效果的。。来调整一下bitmap的底色 和矩形的大小:

把两个bitmap的底色都画成灰色, 矩形不要占满画布 留出空间

[java]
view plain
copy





mDstCanvas.drawColor(Color.GRAY);  

[java]
view plain
copy





@Override  
  protected void onDraw(Canvas canvas) {  
      //dst  黄色  
      mDstCanvas.drawRect(0,0,40,40,mDstPaint);  
  
      canvas.drawBitmap(mDstBitmap,20,20,mDstPaint);  
  
      //src   蓝色  
  
  
      mSrcCanvas.drawCircle(25,25,25,mSrcPaint);  
  
      canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint);  
  
  
  }  

效果如下。。  嗯 这样就是两个bitmap了。。很明了  bitmap的内容 位置,



然后再搞SRC_IN模式



dst_in

 




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

dst_in:



SRC_IN:



总结3:两个bitmap位置不完全重叠的效果如上,并不能总结出效果,要按实际效果来。

---------------------------------------------------------------------------------------------------------华丽丽的分割线-----------------------------------------------------------------------------------------

最终大总结,如果想让PorterDuffXferMode按照预期Demo(或者效果图)的效果图像实现,必须满足以下条件:

1、关闭硬件加速。

2、两个bitmap大小尽量一样。

3、背景色为透明色。

4、如果两个bitmap位置不完全一样,可能也是预期效果,只不过你看到的效果和你自己脑补的预期效果不一致。

最后想再说几句。鼓捣这个模式鼓捣了几乎一周,每天晚上下班都在搞。查了无数资料。但是好多不完整,甚至有一些误导性。所以为了避免后来者入坑。亲自试验,尽量总结。 如果有说的不正确的地方请及时向我提出。我会及时改正。

如果本文帮助到了你,请点一个顶,或者评论一下,蟹蟹!!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息