Xfermode in android

Xfermode有三个实现类:AvoidXfermode, PixelXorXfermode以及PorterDuffXfermode。 前两个类因为不支持硬件加速在API level 16被标记为Deprecated了,用也可以,但是需要关闭硬件加速,简单说下。

AvoidXfermode

AvoidXfermode xfermode will draw the src everywhere except on top of the opColor or, depending on the Mode, draw only on top of the opColor.

这话翻译成中文太别扭了,自己理解吧。举个栗子,如果你想对原来图像进行处理,把红色换成绿色,可以使用这个;或者,你想把不是红色的换成某个颜色,也行。这里有一个容差值的概念,比如红色是0xff0000 但是在一定范围内也都是红色,如果你设定一个容差值,那么“各种符合要求的红色”都会被处理。

PixelXorfermode

文档都说这种模式对于操作混合色没有什么用,还不支持硬件加速,pass,说说重头戏。

PoterDuffXfermode

Porter-Duff的由来

说来说去,这个到处都是PorterDuff的玩意儿到底是什么意思?

Porter-Duff 操作是 1 组 12 项用于描述数字图像合成的基本手法,包括 Clear、Source Only、Destination Only、Source Over、Source In、Source Out、Source Atop、Destination Over、Destination In、Destination Out、Destination Atop、XOR。通过组合使用 Porter-Duff 操作,可完成任意 2D 图像的合成。 Thomas Porter 和 Tom Duff 发表于 1984年原始论文的扫描版本

看到没!可以完成任意2D图像的合成,理论支撑,所以说是核武器~

###对文档的解释 如果去查阅文档这个模式怎么用,相信你一定会fuck:

public enum Mode {
    /** [0, 0] */
    CLEAR       (0),
    /** [Sa, Sc] */
    SRC         (1),
    /** [Da, Dc] */
    DST         (2),
    /** [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] */
    SRC_OVER    (3),
    /** [Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc] */
    DST_OVER    (4),
    /** [Sa * Da, Sc * Da] */
    SRC_IN      (5),
    /** [Sa * Da, Sa * Dc] */
    DST_IN      (6),
    // ...以下省略

这尼玛是什么意思。。好了,别慌。如果懂些图形学,大致就知道: Sa = Source alpha Da = Dest alpha Sc = Source color Dc = Dst color 如果用叠加的形式看,Dst是下面的图,也就是先画的图;Source是上面的图,也就是后面要画的图。

要说明的是,使用porterduffxfermode绘制的时候,由于窗口时透明的,如果出现黑色结果,那么就是这个原因,stackoberflow上有很多这样的问题答案说需要之前画一个bitmap,原因是对的,但是不应该这么处理,使用layer保存图层即可。

int count = canvas.saveLayer
paint.setXfermode()
canvas.drawXXX
canvas.restoreLayer()

实际效果测试以及mode含义

往上很多对于Xfermode的解释,使用API demo望文生义,并没有考虑到alpha通道的情况,实际上是错误的。典型的解释类似这种:

4.PorterDuff.Mode.SRC_OVER 正常绘制显示,上下层绘制叠盖。 5.PorterDuff.Mode.DST_OVER 上下层都显示。下层居上显示。 6.PorterDuff.Mode.SRC_IN 取两层绘制交集。显示上层。 7.PorterDuff.Mode.DST_IN 取两层绘制交集。显示下层。

只能说太肤浅了,我们根据上面的图像学的解释,alpha通道的存在意味着事情没那么简单,我们用实际的例子验证一下。 验证的代码如下:

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // draw background
        canvas.drawColor(Color.WHITE);
        int count = canvas.saveLayer(0, 0, width, height, p, Canvas.ALL_SAVE_FLAG);
        canvas.save();
        canvas.scale(0.5f, 0.5f, width / 2, height / 2);

        canvas.drawBitmap(mDst, 0, 0, p);
        p.setXfermode(xfermode);
        canvas.drawBitmap(mSrc, 0, 0, p);
        p.setXfermode(null);
        canvas.restore();

        canvas.restoreToCount(count);
    }

这里的mSrc以及mDst分别对应我们绘制的SRC bitmap以及DST bitmap;理论已经解释过了,DST代表先画的,下层图像;SRC是后画的上层图像。测试的图像用代码画出来的:

@Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = w;
        height = h;
        float halfWidth = width / 2;

        // DST Rect
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
        mSrc = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        mDst = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);

        Canvas canvas = new Canvas(mDst);
        p.setColor(Color.RED);
        canvas.drawRect(0, 0, halfWidth, height, p);
        p.setAlpha(1 << 7);
        canvas.drawRect(halfWidth, 0, width, height, p);

        canvas = new Canvas(mSrc);
        // SRC circle
        p.setColor(Color.BLUE);
        p.setAlpha((1 << 8) - 1);

        float radius = Math.min(height, width) / 2;
        mSrcRect.set(width / 2 - radius, height / 2 - radius, width / 2 + radius, height / 2 + radius);
        canvas.drawArc(mSrcRect, 0, 180, true, p);
        p.setAlpha(1 << 7);
        canvas.drawArc(mSrcRect, 180, 180, true, p);
    }

注意到,每一个图都有半透明和全透明的两周状态,画出来,肉眼看到效果如下:

SRC和DST

这个就不是解释了,SRC - [Sa, Sc]只有源图像的alpha和颜色,因此只保留源图像;DST也是一样。

####SRC_OVER [ Sa +(1-Sa) Da , Rc = Sc +( 1- Sa ) Dc ]。从名字上看,从DST上面绘制SRC图像(透明度的叠加):

####DST_OVER [Sa + ( 1 - Sa ) Da ,Rc = Dc + ( 1 - Da ) Sc ]。与上面情况差不多,看看效果:

SRC_IN

[ Sa Da , Sc Da ]。注意,alpha通道是SRC和DSTalpha的乘法叠加;颜色是SRC颜色与DSTalpha通道的叠加;考虑一下,我们的图像应该是个什么样子;首先确定图像范围。什么时候才会有图像呢,rgb应该有分量,alpha不能为0;所以rgb分量里面只有SRC,说明图像里面区域里面只有源图像;alpha通道只有DST,当DSTalpha为0的地方没有图像(这句话有两个意思,在DST完全透明的地方不存在源图像)简而言之,就是在相交的地方绘制源图像;但是绘制的alpha通道受DST影响:

DST_IN

[ Sa Da , Sa Dc ]。按照上面的理解。在相交的地方绘制DST,但是alpha受SRC影响:

SRC_OUT

很多地方解释说:

取上层绘制非交集部分。 在不相交的地方绘制 源图像。

我们看看是不是这样:

说好的在不相交的地方绘制源图像呢?如果是这个意思,因为DST包含SRC,那么应该整个应该是什么都没有(待商榷,下面说)。为什么相交的地方还是有源图像?

看看这个porterduff公式:[ Sa ( 1 - Da ) , Sc ( 1 - Da ) ] 这里对应的rgb是Sc * (1 - Da),

  1. 在不相交的地方Da肯定是0,那么不相交的地方就是Sc也就是完全是SRC图像;
  2. 在相交的地方是Sc * (1 - Da)也就是虽然是SRC的颜色,但是受到DST的alpha通道的影响。
  3. 在相交地方的特殊情况,如果DST完全不透明,那么Da = 1;这时候这个表达式值就是0;也就是通常的解释“绘制非交集部分(交集部分没有图像)”

总结一下,这种模式应该是:在不相交的地方绘制源图像,在相交处根据DST的alpha进行过滤;特殊情况下相交处DST不透明,那么相交处没有颜色,如果完全透明(相当于没有DST图像)

DST_OUT

[ Da ( 1 - Sa ) , Dc ( 1 - Sa ) ] 与上面解释类似,不赘述。

SRC_ATOP

[ Da , Sc Da + ( 1 - Sa ) Dc ]。与上面两种模式解释差不多,有一点不同。 源图像和目标图像相交处绘制源图像,不相交的地方绘制目标图像,并且相交处的效果会受到源图像和目标图像alpha的影响;

DST_ATOP

[ Sa , Sa Dc + Sc ( 1 - Da ) ],直接上解释。 源图像和目标图像相交处绘制目标图像,不相交的地方绘制源图像,并且相交处的效果会受到源图像和目标图像alpha的影响;

XOR

[ Sa + Da - 2 Sa Da, Sc ( 1 - Da ) + ( 1 - Sa ) Dc ] 在不相交的地方按原样绘制源图像和目标图像,相交的地方受到对应alpha和色值影响。按上面公式进行计算,如果都完全不透明则相交处完全不绘制;

DARKEN

[ Sa + Da - Sa Da , Sc ( 1 - Da ) + Dc * ( 1 - Sa ) + min(Sc , Dc) ] 从算法上看,alpha值变大,色值上如果都不透明则取较暗值,非完全不透明情况下使用上面算法进行计算,受到源图和目标图对应色值和alpha值影响;正如名字所说,会感觉效果变暗,即进行对应像素的比较,取较暗值,如果色值相同则进行混合;

####LIGHTEN [ Sa + Da - Sa Da , Sc ( 1 -Da ) + Dc * ( 1 - Sa ) + max ( Sc , Dc ) ] 与DARKEN相反,LIGHTEN 的目的则是变亮,如果在均完全不透明的情况下 ,色值取源色值和目标色值中的较大值,否则按上面算法进行计算;

接下来四种 MULTIFYSCREENADDOVERLAY就不说了,产生的结果不太确定。自己查阅文档吧。 emphasized text

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏较真的前端

深入研究CSS字体度量及CSS 盒子

1593
来自专栏腾讯IVWEB团队的专栏

SVG 快速入门

SVG 全称是 Scalable Vector Graphics,即,矢量图。在 Web 中使用 SVG 可以解决位图放大失真的问题。

5481
来自专栏图形学与OpenGL

10.4.3 编程实例-太阳系动画

glClearColor(0.0f, 0.0f, 0.0f, 0.8f); //背景为黑色

1163
来自专栏数据小魔方

sparklines迷你图系列5——Evolution(Horizon)

今天跟大家分享区域(面积图)图的一个变体——水平线图。 之所以说是面积图的变体,因为这种水平线图,表达的信息与面积图几乎差不多,差别仅仅在图表呈现形式上。 ? ...

3186
来自专栏wym

Bomb Catcher 游戏 (Pygame)

951
来自专栏walterlv - 吕毅的博客

Grid 布局算法!自己动手实现一个 Grid

2018-05-20 07:11

822
来自专栏葬爱家族

Android高德之旅(8)绘制线废话简单的api总结

绘制线会比绘制点稍微复杂点,抛开一些复杂的属性不谈,主要分为三类:实线、虚线、纹理。绘制线在自定义地图中是非常重要的一个环节。

2965
来自专栏儿童编程

Processing雁群实验(续)

(1)旋转复杂不规则图形; (2)运用二维数组定义图形; (3)鼠标左右移动控制物体沿 Y 轴旋转; (4)点击鼠标线条变色。

1382
来自专栏前端说吧

CSS3的transition动画功能

3186
来自专栏游戏杂谈

关于坐标旋转

在看<Flash actionscript动画教程>(中文版)的第十章的第二节,它提到“高级坐标旋转”,书中只给出了一个基本公式:

2532

扫码关注云+社区

领取腾讯云代金券