Android Paint XferMode踩坑记

前言

某天,在网上看到一个水波纹ProgressView的例子,N天之后突然想自己实现一个,由于之前做过一个圆形ImageView的头像框,所以就想到用同样的原理(XferMode)实现之。不实践不要紧,一实践发现这是一个巨大的坑,并且网上99%的文章并没有正确的解释,同样的代码下载下来也跑不出预想的效果。
直到看到这篇文章https://blog.csdn.net/iispring/article/details/50472485才恍然大悟。原来网上基本上都是这个图加上一段错误的代码(-_-!):

网上的图.png

官方代码

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

官方的代码中有几点要注意:
1、在画SRC和DST时都create了一个bitmap并在这个bitmap上画出图形。
2、两个bitmap的大小相等并且重合
3、默认的bitmap是透明的
4、只有在draw SRC时才设置Paint的XferMode
只要根据官方Demo的代码来写就能实现网上那张图里的所有效果(图没有问题),但是网上的很多讲解都是关于设置什么layerType与硬件加速有关的标志位

官方代码以外的探究

官方代码中在绘制DST和SRC时都新建了一个Bitmap然后将bitmap返回再画到原本的canvas上。
经过探究,只要SRC能完全覆盖DST的区域,即使不新建bitmap也照样可以达到预期的效果。如果没有完全覆盖,在进行运算时就无法将SRC与DST进行运算,也就达不到网图中的效果,甚至有可能出现各种各样的问题。

工作原理

摘抄自文章片段:
我们知道一个像素的颜色由四个分量组成,即ARGB,第一个分量A表示的是Alpha值,后面三个分量RGB表示了颜色。我们用S代表源像素,源像素的颜色值可表示为[Sa, Sc],Sa中的a是alpha的缩写,Sa表示源像素的Alpha值,Sc中的c是颜色color的缩写,Sc表示源像素的RGB。我们用D代表目标像素,目标像素的颜色值可表示为[Da, Dc],Da表示目标像素的Alpha值,Dc表示目标像素的RGB。

源像素与目标像素在不同混合模式下计算颜色的规则如下所示:

CLEAR:[0, 0]

SRC:[Sa, Sc]

DST:[Da, Dc]

SRC_OVER:[Sa + (1 - Sa)Da, Rc = Sc + (1 - Sa)Dc]

DST_OVER:[Sa + (1 - Sa)Da, Rc = Dc + (1 - Da)Sc]

SRC_IN:[Sa * Da, Sc * Da]

DST_IN:[Sa * Da, Sa * Dc]

SRC_OUT:[Sa * (1 - Da), Sc * (1 - Da)]

DST_OUT:[Da * (1 - Sa), Dc * (1 - Sa)]

SRC_ATOP:[Da, Sc * Da + (1 - Sa) * Dc]

DST_ATOP:[Sa, Sa * Dc + Sc * (1 - Da)]

XOR:[Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]

DARKEN:[Sa + Da - SaDa, Sc(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)]

LIGHTEN:[Sa + Da - SaDa, Sc(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)]

MULTIPLY:[Sa * Da, Sc * Dc]

SCREEN:[Sa + Da - Sa * Da, Sc + Dc - Sc * Dc]

ADD:Saturate(S + D)

OVERLAY:Saturate(S + D)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343