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


    菜鸡wing遇敌PorterDuffXferMode,不料过于轻敌,应战吃力。

    随后与其大战三天三夜。三百余回合不分胜负。

    幸得 @咪咪控 相助,侥幸获胜。

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


    最后想再说几句。

    鼓捣这个模式鼓捣了差点儿一周。每天晚上下班都在搞。查了无数资料。可是好多不完整。甚至有一些误导性。

    所以为了避免后来者入坑。亲自试验,尽量总结。 假设有说的不对的地方请及时向我提出。我会及时改正。


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

    !!


  • 相关阅读:
    修改VNC的分辨率
    How to use WinSCP with public key authentication
    CentOS-7-x86_64-DVD-1511.iso
    6.828
    Tampermonkey版Vimium
    servlet+jsp完成简单登录
    Servlet知识点小结
    HTTP基础知识点小结
    LeetCode 371两数之和
    LeetCode53 最大子序列问题
  • 原文地址:https://www.cnblogs.com/wzjhoutai/p/7232570.html
Copyright © 2020-2023  润新知