如何使用 Jetpack Compose 创建 HSL 饱和度和亮度变化渐变或画笔编辑器?

How to create HSL saturation and lightness change gradient or brush editor with Jetpack Compose?

我正在使用 Jetpack Compose 构建颜色选择器,并尝试实现饱和度和亮度选择器菱形(矩形旋转 45 度),如图像所示,但无法找到显示颜色的好方法他们应该看起来像

我可以在菱形中找到位置并用左边的图像画圆圈,因为那些是圆圈,所以看起来不太好。还尝试绘制小路径,但它会显着降低应用程序速度。

/**
 * Get each point and saturation and lightness of the point. This function is for
 * creating points to draw like gradient effect for HSL color
 */
fun getPointsInRhombus(length: Float): MutableList<ColorPoint> {

    val step = length.toInt() / 50
    val colorPoints = mutableListOf<ColorPoint>()

    for (yPos in 0..length.toInt() step step) {
        val range = getIntRangeInLength(length = length, yPos.toFloat())
        for (xPos in range step step) {

            val path = rhombusPath(Size(10f, 10f))
            path.translate(Offset(xPos.toFloat() - 5, yPos.toFloat()))
            val saturation = xPos / length
            val lightness = 1 - (yPos / length)
            val colorPoint =
                ColorPoint(Offset(xPos.toFloat(), yPos.toFloat()), saturation, lightness, path)
            colorPoints.add(colorPoint)
        }
    }
    return colorPoints
}



 colorPoints.forEach { colorPoint: ColorPoint ->
        drawCircle(
            Color.hsl(hue, colorPoint.saturation, colorPoint.lightness),
            center = colorPoint.point,
            radius = 10f
        )
    }

还尝试创建一个用于亮度的形状和另一个用于饱和度的形状,并尝试将它们混合在一起,但它不起作用,如右图所示。

 with(drawContext.canvas.nativeCanvas) {
        val checkPoint = saveLayer(null, null)

        // Destination lightness top to bottom
        drawPath(
            rhombusPath, Brush.verticalGradient(
                colors = listOf(
                    Color.hsl(
                        hue,
                        saturation = .5f,
                        lightness = 1f,
                        alpha = 1f
                    ),
                    Color.hsl(
                        hue,
                        saturation = .5f,
                        lightness = 0f,
                        alpha = 1f
                    )
                )
            )
        )

        // Source saturation left to right
        drawPath(
            rhombusPath,
            Brush.horizontalGradient(
                colors = listOf(
                    Color.hsl(
                        hue,
                        saturation = 0f,
                        lightness = .5f,
                        alpha = 1f
                    ),
                    Color.hsl(
                        hue,
                        saturation = 1f,
                        lightness = .5f,
                        alpha = 1f
                    )
                )
            ),
            blendMode = BlendMode.SrcIn
        )
        
        restoreToCount(checkPoint)
    }

我需要的是将第一个图像中的颜色应用于右侧的菱形图像,而不绘制圆圈或路径。我认为这可以通过一个渐变或多个渐变或混合它们来解决,但不知道如何解决。

查看此 以供参考,但无法弄清楚如何将其应用于 Compose Brush

对于HSL梯度 BlendMode.Multiply不起作用,它适用于获得HSV梯度。解决方案是使用 Blendmode.Overlay 渐变。另外 Brush.linergradient 的默认角度是 45 度 顺时针,需要将其设置为 0 度 才能使饱和度从头到尾发生变化可组合的。

val lightnessGradient = remember {
    Brush.verticalGradient(
        colors = listOf(
            Color.hsl(hue = hue, saturation = .5f, lightness = 1f),
            Color.hsl(hue = hue, saturation = .5f, lightness = 0f)
        )
    )

}

val saturationHSLGradient = remember {
    val gradientOffset = GradientOffset(GradientAngle.CW0)

    Brush.linearGradient(
        colors = listOf(
            Color.hsl(hue, 0f, .5f),
            Color.hsl(hue, 1f, .5f)
        ),
        start = gradientOffset.start,
        end = gradientOffset.end
    )
}

然后在图层中使用叠加的 Blend(PorterDuff) 模式绘制这两个渐变

Canvas(modifier = canvasModifier) {


    drawIntoLayer {
        drawPath(
            path = rhombusPath,
            lightnessGradient,
        )
        drawPath(
            path = rhombusPath,
            saturationHSLGradient,
            blendMode = BlendMode.Overlay
        )
    }
}

混合绘图功能

fun DrawScope.drawIntoLayer(
    content: DrawScope.() -> Unit
) {
    with(drawContext.canvas.nativeCanvas) {
        val checkPoint = saveLayer(null, null)
        content()
        restoreToCount(checkPoint)
    }
}

HSV 和 HSL 渐变的结果,小矩形不是用渐变绘制的,而是在点处绘制小矩形以验证 HSL 渐变与位置处的真实颜色匹配。

Github 完整实施的回购可用 here