zoukankan      html  css  js  c++  java
  • PorterDuffXferMode不正确的真正原因PorterDuffXferMode深入试验)

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

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


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


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


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


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


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

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

    从最简单的开始:

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

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

    原图效果是这样的:


    现在加一个mode上来,XOR

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

     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长什么样!

    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里面的片段,这里

    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上:

     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);
        }

     @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?

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

    加一个XOR 试试

     @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  这样会有影响

    纠正

     @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开始 完全覆盖了。

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

    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的底色都画成灰色, 矩形不要占满画布 留出空间

    mDstCanvas.drawColor(Color.GRAY);

      @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位置不完全一样,可能也是预期效果,只不过你看到的效果和你自己脑补的预期效果不一致。


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


    如果本文帮助到了你,请点一个顶,或者评论一下,蟹蟹!!!!


  • 相关阅读:
    tuple 元组及字典dict
    day 49 css属性补充浮动 属性定位 抽屉作业
    day48 选择器(基本、层级 、属性) css属性
    day47 列表 表单 css初识
    day 46 http和html
    day 45索引
    day 44 练习题讲解 多表查询
    day 40 多表查询 子查询
    day39 表之间的关联关系、 补充 表操作总结 where 、group by、
    day38 数据类型 约束条件
  • 原文地址:https://www.cnblogs.com/muyuge/p/6333543.html
Copyright © 2011-2022 走看看