Android Canvas - PorterDuffXfermode

本文参照 http://blog.csdn.net/iispring/article/details/50472485

概述

类 Android.graphics.PorterDuffXfermode 继承自 android.graphics.Xfermode。

当绘图时和前面的图案有重叠部分,为了设置重叠规则,需要用到 PorterDuffXfermode,需要将将其作为参数传给 Paint.setXfermode(Xfermode xfermode) 方法。

PorterDuffXfermode 这个类中的 Porter 和 Duff 是两个人名,这两个人在 1984 年一起写了一篇名为《Compositing Digital Images》 的论文。

下面开始介绍 PorterDuffXfermode 的使用。

示例一

在演示 Xfermode 之前,先看一下下面的例子

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int r = canvas.getWidth() / 3;

        // 设置背景色
        canvas.drawARGB(255, 139, 197, 186);

        //绘制黄色的圆形
        paint.setColor(0xFFFFCC44);
        canvas.drawCircle(r, r, r, paint);

        //绘制蓝色的矩形
        paint.setColor(0xFF66AAFF);
        canvas.drawRect(r, r, r * 2.7f, r * 2.7f, paint);
    }

重写了 View 的 onDraw 方法,首先将 View 的背景色设置为绿色,然后绘制了一个黄色的圆形,然后再绘制一个蓝色的矩形,效果如下所示:

没有设置 Xfermode 的时候,后来绘制的图形就会覆盖之前绘制的图形!

示例二

下面我们使用 Xfermode 对上面的代码进行一下修改,修改后的代码如下所示:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int r = canvas.getWidth() / 3;

        // 设置背景色
        canvas.drawARGB(255, 139, 197, 186);

        //绘制黄色的圆形
        paint.setColor(0xFFFFCC44);
        canvas.drawCircle(r, r, r, paint);

        //绘制蓝色的矩形
        paint.setColor(0xFF66AAFF);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        canvas.drawRect(r, r, r * 2.7f, r * 2.7f, paint);
    }

效果如下:

先来讲一下 setXfermode 的绘制原理:在 canvas.drawRect() 之前 setXfermode,那么所绘制的矩形中的像素称作源像素(source,简称 src),所绘制的矩形在 Canvas 中对应位置的矩形内的像素称作目标像素(destination,简称 dst)。根据 Xfermode 的规则,ARGB 的值会重新计算。

本例中 Xfermode 是 PorterDuff.Mode.CLEAR,直接将目标像素的 ARGB 四个分量全置为 0,即 (0,0,0,0),即透明色,所以实际上绘制了一个透明的矩形。但是效果图为什么是白色的呢,因为屏幕本身是白色的。

示例三

示例二中显示的白色,如果想要显示透明色,我们需要将代码作如下修改:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int r = canvas.getWidth() / 3;

        // 设置背景色
        canvas.drawARGB(255, 139, 197, 186);

        int layerId = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null, Canvas.ALL_SAVE_FLAG);

        //绘制黄色的圆形
        paint.setColor(0xFFFFCC44);
        canvas.drawCircle(r, r, r, paint);

        //绘制蓝色的矩形
        paint.setColor(0xFF66AAFF);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        canvas.drawRect(r, r, r * 2.7f, r * 2.7f, paint);

        canvas.restoreToCount(layerId);
    }

效果如下:

我们将绘制圆形与矩形的代码放到了 canvas.saveLayer() 和 canvas.restoreToCount() 之间。

关于 canvas 绘图中的 layer 有以下几点需要说明:

  1. Canvas 是支持图层 layer 渲染这种技术的,Canvas 默认就有一个 layer,当我们平时调用 Canvas 的各种
    drawXXX() 方法时,其实是把所有的东西都绘制到 Canvas 这个默认的 layer 上面。

  2. 我们还可以通过 canvas.saveLayer() 新建一个 layer,新建的 layer 放置在 canvas 默认 layer 的上部,当我们执行了 canvas.saveLayer() 之后,我们所有的绘制操作都绘制到了我们新建的 layer 上,而不是 canvas 默认的 layer。

  3. 用 canvas.saveLayer() 方法产生的 layer 所有像素的 ARGB 值都是 (0,0,0,0),即 canvas.saveLayer()
    方法产生的 layer 初始时时完全透明的。

  4. canvas.saveLayer() 方法会返回一个 int 值,用于表示 layer 的 ID,在我们对这个新 layer 绘制完成后可以通过调用 canvas.restoreToCount(layer) 或者 canvas.restore() 把这个 layer 绘制到 canvas 默认的 layer 上去,这样就完成了一个 layer 的绘制工作。

所以 CLEAR 操作时在新建的图层上面,背景色不再是 Activity 的背景色,变成了默认图层的背景色了。

一张被不经大脑疯传的神图

如果大家 Google 或百度 PorterDuffXfermode 相关的博文,大家肯定会看到下面这张神图,如下所示:

其实这张图有点问题,具体可以参照 http://blog.csdn.net/iispring/article/details/50472485

不同 Mode 的效果

PorterDuff.Mode.CLEAR, //清除
PorterDuff.Mode.SRC, //只绘制源图
PorterDuff.Mode.DST, //只绘制目标图像
PorterDuff.Mode.SRC_OVER, //在目标图像的上方绘制源图像
PorterDuff.Mode.DST_OVER, //在源图像的上方绘制目标图像
PorterDuff.Mode.SRC_IN, //只在源图像和目标图像相交的地方绘制源图像
PorterDuff.Mode.DST_IN, //只在源图像和目标图像相交的地方绘制目标图像
PorterDuff.Mode.SRC_OUT, //只在源图像和目标图像不相交的地方绘制源图像
PorterDuff.Mode.DST_OUT, //只在源图像和目标图像不相交的地方绘制目标图像
PorterDuff.Mode.SRC_ATOP, //在源图像和目标图像相交的地方绘制源图像,在不相交的地方绘制目标图像
PorterDuff.Mode.DST_ATOP, //在源图像和目标图像相交的地方绘制目标图像,在不相交的地方绘制源图像
PorterDuff.Mode.XOR, //在源图像和目标图像重叠之外的任何地方绘制他们,而在重叠的地方不绘制任何内容
PorterDuff.Mode.DARKEN, //变暗
PorterDuff.Mode.LIGHTEN, //变亮
PorterDuff.Mode.MULTIPLY, //正片叠底
PorterDuff.Mode.SCREEN, //滤色
PorterDuff.Mode.ADD, //饱和相加
PorterDuff.Mode.OVERLAY //叠加

代码可见 https://github.com/teletian/Android/tree/master/PorterDuffXfermode

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

推荐阅读更多精彩内容