Jetpack Compose - Canvas之BlendMode

Posted 乐翁龙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jetpack Compose - Canvas之BlendMode相关的知识,希望对你有一定的参考价值。

Jetpack Compose - Canvas之BlendMode

Compose系列文章,请点原文阅读。原文,是时候学习Compose了!

0、介绍

关于混合模式,具体请看 官网链接

在画布上绘制时使用的算法。将形状或图像绘制到画布上时,可以使用不同的算法来混合像素。 BlendMode的不同值指定了不同的此类算法。

1、属性一览

关于混合模式,有多达十几种,在展示下文代码前,我们需要先达成一个共识,这个混合模式,是两个图片相交区域的混合模式,如果不相交,那么混合模式根本不会生效。

所以这就决定了你绘制图片绘制形状时,图形位置不同就可能会出现奇怪的混合结果。

下文的代码我们会分别绘制大小相同的png图片(如下图所示)以及绘制形状来展示其区别。

目标图像源图像

注意上图两张图片的区别,大小都是300像素。但是目标图像圆形是直径200像素,位于图片左上角的位置。源图像正方形是宽高200像素,位于图片右下角的位置。

2、使用示例

我们会先绘制红色圆形,然后再绘制蓝色矩形,据此,再给大家灌输一个概念:

  • 先绘制的红色圆形,我们将其命名为目标图像,目标就是destination,简写Dst
  • 后绘制的蓝色矩形,我们将其命名为源图像,来源就是source,简写为Src

得到这样一个概念后我们再来看代码:
首先是使用绘制图片的函数drawImage(),然后混合模式为:blendMode = BlendMode.Src,代码如下所示:

@Composable
fun SrcOverImage() 

    val destination = ImageBitmap.imageResource(id = R.drawable.canvas_dst)
    val source = ImageBitmap.imageResource(id = R.drawable.canvas_src)

    Canvas(
        modifier = androidx.compose.ui.Modifier
            .size(defaultSizeImage)
    ) 
        drawImage(
            image = destination,
        )

        drawImage(
            image = source,
            blendMode = BlendMode.Src
        )
    

绘制结果如下所示:

此时我们再来看下这个混合模式的含义:Src,Src也就是源图像。所以就是说只显示源图像,效果就是只显示出了蓝色矩形,虽然红色矩形是先绘制的。(至于黑色区域,只是因为截图产生的,其实真实显示效果依旧是透明色,请大家理解)

好了,以上是根据图片混合得到的结果,下面我们使用draw()函数来进行展示(大致意思是一样的,分别在左上角绘制了红色圆形,在右下角绘制了蓝色矩形):

@Composable
fun SrcOver() 
    Canvas(
        modifier = Modifier
            .size(defaultSizeDraw)
    ) 
        val radius = size.width / 3
        drawCircle(
            color = myRed,
            radius = radius,
            center = Offset(radius, radius),
        )

        drawRect(
            color = myBlue,
            topLeft = Offset(radius, radius),
            size = Size(radius * 2, radius * 2),
            blendMode = BlendMode.Src
        )
    

然而渲染出来的结果却是这样的:

大家思考下为什么会是这样呢???还记得文章开头说的一点么两个图像相交的区域,这里绘制出来的两个图像,相交区域只有最中间的那100像素的大小,其余的地方都不会混合,从而原样展示出来。而上面使用图片进行混合的话,整个区域都是相交的地方。


所以千万别再说官方的示例不正确了,只是各位审题不仔细啊!!!


下面的讲解会将绘制图片的效果示例放置在左侧,draw函数绘制形状效果放置在右侧,注意我们的draw函数绘制图像区域的问题,仅仅是对比而已。

2.1、Clear

删除源和目标图像,不留下任何东西。

绘制图片绘制形状

2.2、Src

删除目标图像,只绘制源图像。
从概念上讲,首先清除目标,然后绘制源图像。

绘制图片绘制形状

2.3、Dst

删除源图像,只绘制目标图像。
从概念上讲,源图像被丢弃,目标图像不受影响。

绘制图片绘制形状

2.4、SrcOver

将源图像与目标图像合成。
这是默认值。它代表了最直观的情况,形状层层往上绘制,用透明区域显示目标层。

绘制图片绘制形状

2.5、DstOver

在目标图像下合成源图像。
这是反面的SrcOver。
当源图像应该在目标图像之前绘制,但不能这样做时,这是很有用的。

绘制图片绘制形状

2.6、SrcIn

显示源图像,但只显示两个图像重叠的地方。目标图像没有被渲染,它只是作为一个遮罩来处理。目标的颜色通道被忽略,只有不透明度有效果。

绘制图片绘制形状

2.7、DstIn

显示目标图像,但只显示两个图像重叠的地方。源图像没有被渲染,它仅仅被当作遮罩来处理。源图像的颜色通道被忽略,只有不透明度有效果。

绘制图片绘制形状

2.8、SrcOut

显示源图像,但只在两个图像不重叠的地方。目标图像没有被渲染,它只是作为一个遮罩来处理。目标的颜色通道被忽略,只有不透明度有效果。

绘制图片绘制形状

2.9、DstOut

显示目标图像,但只在两个图像不重叠的地方。源图像没有被渲染,它仅仅被当作遮罩来处理。光源的颜色通道被忽略,只有不透明度有效果。

绘制图片绘制形状

2.10、SrcAtop

将源图像与目标图像合成,但只在源图像与目标图像重叠的地方。
这本质上是SrcOver操作符,但是输出的不透明度通道被设置为目标图像的不透明度通道,而不是两个图像的不透明度通道的组合。

绘制图片绘制形状

2.11、DstAtop

将目标图像与源图像合成,但只在目标图像与源图像重叠的地方。
这本质上是DstOver操作符,但是输出的不透明度通道被设置为源图像的不透明度通道,而不是两个图像的不透明度通道的组合。

绘制图片绘制形状

2.12、Xor

对源图像和目标图像应用逐位异或运算符。这使得它们的重叠部分变得透明。

绘制图片绘制形状

2.13、Plus

将源图像和目标图像的组件相加。
其中一幅图像的像素的透明度降低了该图像对相应输出像素的贡献,就好像该图像中的像素的颜色较暗一样。

绘制图片绘制形状

2.14、Modulate

将源图像和目标图像的颜色组件相乘。
这只能导致相同或更深的颜色(乘以白色,1.0,结果没有变化;乘以黑色,0.0,结果为黑色)。
当合成两个不透明的图像时,这与在投影仪上重叠两个幻灯片的效果相似。
对于一个也会乘以alpha通道的变量,考虑乘法。

绘制图片绘制形状

2.15、Screen

将源图像和目标图像的分量的逆值相乘,然后将结果相逆。
反转分量意味着将完全饱和的通道(不透明的白色)视为值0.0,将通常视为0.0的值(黑色,透明)视为1.0。
这本质上与“调制”混合模式相同,但是颜色的值在乘法之前先反转,结果在渲染前先反转。
这只能导致颜色相同或更浅(乘以黑色1.0不会导致更改;乘以白色0.0则会导致白色)。同样,在Alpha通道中,它只能导致更不透明的颜色。
这与两台投影机在同一屏幕上同时显示其图像的效果相似。

绘制图片绘制形状

2.16、Overlay

调整源图像和目标图像的分量以使其适合目标后,将它们相乘。
具体来说,如果目标值较小,则将其与源值相乘,而当源值较小时,则将源值的逆与目标值的相乘相乘,然后对结果求反。
反转分量意味着将完全饱和的通道(不透明的白色)视为值0.0,将通常视为0.0的值(黑色,透明)视为1.0。

绘制图片绘制形状

2.17、Darken

通过从每个颜色通道中选择最小值来合成源图像和目标图像。
以与SrcOver相同的方式计算输出图像的不透明度。

绘制图片绘制形状

2.18、Lighten

通过从每个颜色通道中选择最大值来合成源图像和目标图像。
以与SrcOver相同的方式计算输出图像的不透明度。

绘制图片绘制形状

2.19、ColorDodge

将目标除以源的倒数。
反转分量意味着将完全饱和的通道(不透明的白色)视为值0.0,将通常视为0.0的值(黑色,透明)视为1.0。
注意此BlendMode只能在Android API级别29及更高版本上使用

绘制图片绘制形状

2.20、ColorBurn

将目标的倒数除以源,然后将结果倒数。
反转分量意味着将完全饱和的通道(不透明的白色)视为值0.0,将通常视为0.0的值(黑色,透明)视为1.0。
注意此BlendMode只能在Android API级别29及更高版本上使用

绘制图片绘制形状

2.21、Hardlight

调整源图像和目标图像的成分以使其适合源图像之后,将它们相乘。
具体来说,如果源值较小,则将其与目标值相乘,而当目标值较小时,则将目标值的逆与源值的逆相乘,然后求反。
反转分量意味着将完全饱和的通道(不透明的白色)视为值0.0,将通常视为0.0的值(黑色,透明)视为1.0。
注意此BlendMode只能在Android API级别29及更高版本上使用

绘制图片绘制形状

2.22、Softlight

对于低于0.5的源值使用ColorDodge,对于高于0.5的源值使用ColorBurn。
与Overlay效果相比,这会产生相似但较柔和的效果。
注意此BlendMode只能在Android API级别29及更高版本上使用

绘制图片绘制形状

2.23、Difference

从每个通道的较大值中减去较小的值。
合成黑色没有效果。 合成白色会使另一张图像的颜色反转。
以与SrcOver相同的方式计算输出图像的不透明度。
注意此BlendMode只能在Android API级别29及更高版本上使用
效果类似于“Exclusion”,但更苛刻。

绘制图片绘制形状

2.24、Exclusion

从两个图像的总和中减去两个图像的乘积的两倍。
合成黑色没有效果。 合成白色会使另一张图像的颜色反转。
以与SrcOver相同的方式计算输出图像的不透明度。
注意此BlendMode只能在Android API级别29及更高版本上使用
效果类似于“Difference”,但较柔和。

绘制图片绘制形状

2.25、Multiply

将源图像和目标图像的组成部分相乘,包括alpha通道。
这只能产生相同或较深的颜色(乘以白色1.0,结果不变;乘以黑色0.0,结果黑色)。
由于alpha通道也被相乘,因此一幅图像中的全透明像素(不透明度0.0)导致输出中的全透明像素。 这类似于DstIn,但将颜色组合在一起。
对于多种颜色但不与Alpha通道相乘的变体,请考虑“Modulate”。
注意此BlendMode只能在Android API级别29及更高版本上使用

绘制图片绘制形状

2.26、Hue

获取源图像的色相,以及目标图像的饱和度和光度。
效果是用源图像对目标图像进行着色。
以与SrcOver相同的方式计算输出图像的不透明度。 源图像中完全透明的区域会从目标获得其色相。
注意此BlendMode只能在Android API级别29及更高版本上使用

绘制图片绘制形状

2.27、Saturation

获取源图像的饱和度以及目标图像的色相和亮度。
以与SrcOver相同的方式计算输出图像的不透明度。 源图像中完全透明的区域从目标位置开始饱和。
注意此BlendMode只能在Android API级别29及更高版本上使用

绘制图片绘制形状

2.28、Color

获取源图像的色相和饱和度以及目标图像的光度。
效果是用源图像对目标图像进行着色。
以与SrcOver相同的方式计算输出图像的不透明度。 源图像中完全透明的区域从目标位置获得其色相和饱和度。
注意此BlendMode只能在Android API级别29及更高版本上使用

绘制图片绘制形状

2.29、Luminosity

获取源图像的亮度,以及目标图像的色相和饱和度。
以与SrcOver相同的方式计算输出图像的不透明度。 源图像中完全透明的区域从目的地获取其亮度。
注意此BlendMode只能在Android API级别29及更高版本上使用

绘制图片绘制形状

3、版本更新

  • 暂无

4、未解决问题

哇~,这么多混合模式,头疼,不知道能用到几个[笑哭][笑哭][笑哭]。

我们上文示例的两个最简单的图片混合的效果,并不能完全展示出混合的强大,图片过于简单,展示效果有限。所以大家理解后可以自行更换其他图片尝试加深理解。

以上是关于Jetpack Compose - Canvas之BlendMode的主要内容,如果未能解决你的问题,请参考以下文章

Jetpack Compose中的Canvas

Jetpack Compose 从入门到入门

Jetpack Compose 从入门到入门

Jetpack Compose 从入门到入门

Jetpack Compose 自定义绘制

使用 Jetpack Compose 完成自定义绘制