Jetpack Compose Canvas BlendMode.SRC_IN 使背景透明度更高

11

我试图使用BlendMode.SRC_IN在PNG图片(透明背景)上方添加覆盖颜色,但背景变成了黑色,好像也遮挡了背景像素一样。

@Composable
fun Icon(
        fraction: Float,
        image: ImageAsset,
        defaultColor: Color = Color(0xFFEEEEEE),
        progressColor: Color = Color(0xFF888888),
        size: Dp = image.width.dp
) {
    Box(modifier = Modifier.size(size = size)) {
        Canvas(modifier = Modifier.fillMaxSize()) {
            drawImage(
                    image = image,
                    dstSize = IntSize(
                            width = size.toIntPx(),
                            height = size.toIntPx()
                    ),
                    colorFilter = ColorFilter.tint(
                            color = defaultColor
                    ),
                    blendMode = BlendMode.Src
            )
            drawIntoCanvas {
                val paint = Paint().apply {
                    color = progressColor
                    blendMode = BlendMode.SrcIn
                }

                it.restore()
                it.drawRect(
                        rect = Rect(
                                offset = Offset.Zero,
                                size = Size(
                                        width = size.toPx() * fraction,
                                        height = size.toPx()
                                )
                        ),
                        paint = paint
                )
                it.save()
            }
        }
    }
}

以下是我在一个Screen(color = Color.WHITE)上排列多个Icon()的样子,

Surface(color = Color.White) {
        Row {
            listOf(
                    R.drawable.anemo,
                    R.drawable.cryo,
                    R.drawable.dendro,
                    R.drawable.electro,
                    R.drawable.geo,
                    R.drawable.hydro,
                    R.drawable.pyro
            ).forEachIndexed { index, imageRes ->
                val from = 100f/7 * index
                val to = 100f/7 * (index + 1)
                val fraction = when {
                    progress > to -> 1f
                    progress > from -> (progress - from)/(to - from)
                    else -> 0f
                }
                Icon(
                        fraction = fraction,
                        image = imageResource(id = imageRes),
                        size = 50.dp
                )
            }
        }
    }

icons

我希望得到的结果是, result 我做错了什么吗?
这里是我尝试的Github代码库

你的主视图为什么要使用Surface?如果你不需要任何高程处理的话? - Ahmad Sattout
本来可以不用Surface的,但是为了展示我面临的问题,即使Surface对背景颜色没有任何影响,我还是加上了它。 - Suraj Kumar Sau
3个回答

7
在我的情况下,我可以通过添加如下的 graphicsLayer 修饰符来解决应该是透明像素的地方出现黑色像素的问题:
Box(
    modifier = Modifier.fillMaxSize()
) {
    Canvas(modifier = Modifier
        .fillMaxSize()
        .graphicsLayer(alpha = 0.99f)
    ) {
        drawRect(
            color = Color.Black,
            size = size,
            blendMode = BlendMode.Xor
        )

        drawCircle(
            color = Color.Black,
            radius = 300f,
            blendMode = BlendMode.Xor
        )
    }
}

没有修饰符:

输入图像描述

有修饰符:

输入图像描述

我从这里获取了这个想法:https://gist.github.com/nadewad/14f04c788aea43bd6d31d94cd8100ab5(请注意,drawLayer已更名为graphicsLayer)。


5

.graphicsLayer(alpha = 0.99f) 是一种有效的解决方法,但它会改变内容的透明度,这并不是必要的。

为了将绘制重定向到离屏渲染目标,可以利用 android.graphics.Canvas#saveLayer(android.graphics.RectF, android.graphics.Paint),一个简单的修改器可能如下:

fun Modifier.drawOffscreen(): Modifier = this.drawWithContent {
    with(drawContext.canvas.nativeCanvas) {
        val checkPoint = saveLayer(null, null)
        drawContent()
        restoreToCount(checkPoint)
    }
}

在调用 restoreToCount 方法之前,内容将被离屏绘制。


2

@LN-12 给出的原始答案是有效的,并依赖于框架的内部逻辑。通过应用 .graphicsLayer(alpha = 0.99f) 修饰符,Compose 在渲染时更改了使用的 CompositingStrategy

然而,改变 alpha 值可能会产生不良影响,并需要注释来解释修饰符的目的。 幸运的是,从 compose 1.4.0 开始,可以使用更加优雅的方式实现相同的结果: .graphicsLayer(compositingStrategy = CompositingStrategy.Offscreen)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接