反驳有关博客关于PorterDuffXfermode的讲解

这个标题口气是真大,没办法,讲解这个的博客太多了,而且好多有误导性,有些自己都不知道为啥,还写博客,抄来抄去的。当然了,很多人写的还是很好的,但是感觉还是没有理解PorterDuffXfermode的用法以及为啥和官方demo的不一样的原因,所以写了该博客
主要参考博客以及反驳博客是
http://blog.csdn.net/iispring/article/details/50472485
http://blog.csdn.net/wingichoy/article/details/50534175
通过该篇博客你可以学到
porterduffXfermode到底是怎么一回事,怎么样写才会保证不出错
当然预备知识是对自定义view的简单认识。
那么,接下来我们开始说问题。

网上官方demo真的有错吗,为什么我们写出来的跟demo不一样?

A:确实有错,但是错误在CLEAR,Daeken,Lighten,而不是错在设置SRC后,官方只显示了正方形而我们却显示正方形和圆形
先看一下网上流传的图:
这里写图片描述
再看一下我运行的最新代码 安卓6.0自带demo,api版本是19;
这里写图片描述
运行平台,夜神模拟器,安卓版本4.4,在荣耀6 安卓6.0版本上运行结果一致。所以截取了模拟器截图。

可以看到后续谷歌已经修改过这个demo了,显示效果确实不一样的。我估计是网上的版本是2.3之前的版本了吧。但是博客写完后又没人后续维护,导致大家人云亦云,说白了就是互相抄来抄去,瞎扯淡。

有人在看到最新的demo的Clear属性后,大舒一口气,这不是和我写的demo
一样了吗,我设置了Clear属性后就是这么显示的,哈哈,洋洋飒飒几行代码,一运行,嗯,一致,很开心。

int sc = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null, Canvas.ALL_SAVE_FLAG);//增加一个缓冲层
        canvas.drawCircle(100, 100, 50, mDSTpaint);
        mSrcBluePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        canvas.drawRect(100, 100, 200, 200, mSrcBluePaint);
        mSrcBluePaint.setXfermode(null);
        canvas.restoreToCount(sc);

可是在设置了SRC属性后,显示为这个效果了,懵逼了。
这里写图片描述
哎哟我去,不是显示上部吗,怎么连下层的圆也显示出来了,和官方不一致啊,可是明明第一个正确的,为啥呢?
于是,第一篇博客诞生了。http://blog.csdn.net/iispring/article/details/50472485,被大家认知为神作~
并贴了所谓自己正确的贴图
这里写图片描述
当然这幅图对吗,确实是对的,(除了Daeken,Lighten,这个并没有产生相交部位的加深和变浅,官方也是这样,难道和api版本有关系?)。

有些人可能懵逼了,明明好多和官方不一样,为什么这篇也是对的,你的道理在哪里?为什么会有两种不同的版本,还都是对的?

这篇博客的博主出发点是好的,就是为了解疑为什么我们自己写的代码和官方不一致。但是他和初学者都犯了同一个错误,就是“你以为的以为就是你以为的以为吗?”哈哈,有点绕口。说白了,就是没有解释到点上。

总结一下初学者可能犯的最大错误,也是造成自己写的和demo不一样的根本原因:
以为黄圆是DST ,蓝方是SRC..。错错错!(以官方demo来看)
虽然上面的贴图和我们自己运行代码一致,但是他还是没有走出这个错误,否则写出来的代码就和官方的一样啦~
安卓源码里是这么写的(可以忽视)

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

注意这段代码:

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

再看看博客里及我们自己写的代码,通常都是这么写的:(引用自第一篇博客)

protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //设置背景色
        canvas.drawARGB(255, 139, 197, 186);

        int canvasWidth = canvas.getWidth();
        int canvasHeight = canvas.getHeight();
        int layerId = canvas.saveLayer(0, 0, canvasWidth, canvasHeight, null, Canvas.ALL_SAVE_FLAG);
            int r = canvasWidth / 3;
            //正常绘制黄色的圆形
         paint.setColor(0xFFFFCC44);
            canvas.drawCircle(r, r, r, paint);
            //使用CLEAR作为PorterDuffXfermode绘制蓝色的矩形
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
            paint.setColor(0xFF66AAFF);
            canvas.drawRect(r, r, r * 2.7f, r * 2.7f, paint);        //最后将画笔去除Xfermode
            paint.setXfermode(null);
        canvas.restoreToCount(layerId);
    }

注意这段代码:

            canvas.drawCircle(r, r, r, paint);
            //使用CLEAR作为PorterDuffXfermode绘制蓝色的矩形
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
            canvas.drawRect(r, r, r * 2.7f, r * 2.7f, paint);

看到区别了吗?
放到一起对比一下:

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

//很多人的写法:
canvas.drawCircle(r, r, r, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawRect(r, r, r * 2.7f, r * 2.7f, paint);

这两段代码的区别就在与Xfermode的作用域
官方的作用域在整个bitmap,而很多人的写法仅仅在于蓝色方形。。。注意,这是重点,重点!!!!!!!!!!!!!!

反驳观点1:官方在图的大小上做了手脚了吗?—( Xfermode的作用域)

很明显,没有!!!!但是为什么显示效果不一样?
第一篇博客认为是官方在图的大小上做了手脚,其实根本不是
官方的做法是先把图形画在bitmap上,然后再对bitmap做了相交绘制
而大多数人的做法只是把圆和方进行相交绘制。所以产生了所谓的正确的图。这幅图是正确的,因为作用域的原因,但是用这幅图来反驳官方的图,那就是错上加错了~

作用域是我自己创造的名词。

以SRC_IN为例,表示展示上方相交的图,也就一个蓝色扇形。安卓源码里,是将圆和方画在两个bitmap里,两个图一样大小,然后相交绘制,注意,相交区域是bitmap的大小,bitmap默认是透明的。
那么就很容易知道为什么源码只显示蓝色扇形。相交区域通过某种算法,透明色和底部圆DST后变成透明,蓝方与黄圆相交部分,显示上部相交部分,也就是显示蓝色扇形。
为什么我们写的还存在底部黄圆呢?
因为我们设置的Xfermode 作用域只在蓝方。蓝方与黄圆相交显示上部,展示扇形,没问题,但是仅此而已,没相交的地方Xfermode 没有效果,该咋展示就咋展示,于是产生了和官方不一样的效果。
其他同理。都能解释的通。。。。。。。。

反驳观点2:和硬件加速有关吗?应该没关系

新开一个layer层同样可以解决设置Clear属性后黑圆的效果,调用canvas.saveLayer方法即可。
所以和硬件加速有关不成立,建议以后所有的操作新开图层进行处理。

反驳观点3:必须两个bitmap吗?还必须大小一致吗?

不是的,没必要两个bitmap。一个就完全够了,继续以SRC_IN为例,这里写图片描述
代码如下

 //只有一个bitmap形式
    private void test3(Canvas canvas) {
        int sc = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null, Canvas.ALL_SAVE_FLAG);//增加一个缓冲层
        canvas.drawCircle(100,100,50,mDSTpaint);


        Bitmap bitmap = Bitmap.createBitmap(500, 500, Bitmap.Config.ARGB_8888);
        Canvas ca = new Canvas(bitmap);
        ca.drawRect(100, 100, 200, 200, mSrcBluePaint);
        //注意一下作用域~
        mSrcBluePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(bitmap, 0, 0, mSrcBluePaint);
        mSrcBluePaint.setXfermode(null);
        canvas.restoreToCount(sc);

    }

可以看到至创建了一个bitmap,因为我们要的到的是蓝色扇形,我们的DST是圆,选用的模式是SRC_IN,只要保证画的蓝方和黄圆相交部位是个扇形即可,无所谓一个两个bitmap的,既然一个bitmap都能满足效果,那么就更没有必要要求两个必须一致了,毕竟另一个可以认为bitmap的大小是0 嘛~ 况且创建bitmap那么耗费内存~能省则省,看效果行事~

总结

1.源码是两个bitmap相交绘制的,不是黄圆和和蓝方,搞清楚这个你就不会画错了~
2.还是要多思考,源码比博客保险,不要人云亦云~
3.有些博客讲的都是么比~贴张demo图就去实现效果,虽然有的效果和预想的一样,但是我深刻怀疑是歪打正着而已

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值