Skip to main content
  1. About
  2. For Teams
AI Assist is now on Stack Overflow. Start a chat to get instant answers from across the network. Sign up to save and share your chats.
Asked
Viewed 3k times
Part of Mobile Development Collective
2

I'm building a Color picker with Jetpack Compose and trying to implement Saturation and Lightness picker rhombus(rectangle rotated 45 degrees) as can be seen in images but couldn't able to find a good method to display colors as they supposed to look like

I can get positions in rhombus and draw circles with, image on the left, since those are circle they don't look good. Also tried drawing small paths but it slows the app significantly.

/**
 * 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
        )
    }

Also tried creating to shapes one for lightness and other for Saturation and tried to blend them in but it doesn't work as can be seen in image on the right.

 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)
    }

What i need is the colors from first image to be applied to a rhombus like image on the right without drawing circles or paths. I think this can be solved with one gradient or multiple gradients or blending them but can't find out how.

Checked this question in c# for reference but couldn't figure out how to apply it to Compose Brush

1 Answer 1

2

For HSL gradient BlendMode.Multiply doesn't work, it works for getting HSV gradient. Solution is to use Blendmode.Overlay with gradients. Also default angle for Brush.linergradient is 45 degrees clockwise, need to set it to 0 degrees to have a saturation change from start to end of Composable.

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
    )
}

Then draw both of these gradients in a layer with Blend(PorterDuff) mode of Overlay

Canvas(modifier = canvasModifier) {


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

Drawing function for blending

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

Result for HSV and HSL gradients, small rectangles are drawn not with gradient but small rectangles at points to verify that HSL gradient matches with real colors at positions.

enter image description here

Github repo for full implementation is available here.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

Post as a guest

Required, but never shown

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.

Morty Proxy This is a proxified and sanitized view of the page, visit original site.