Jetpack Compose Canvas BlendMode.SRC_IN makes even background transparent

2 min read 06-10-2024
Jetpack Compose Canvas BlendMode.SRC_IN makes even background transparent


Jetpack Compose Canvas: Why BlendMode.SRC_IN Turns Your Background Transparent

Jetpack Compose's Canvas API offers powerful tools for drawing and manipulating graphics. While BlendMode provides flexibility for blending colors, a common issue arises when using BlendMode.SRC_IN: the background becomes transparent, even if you don't intend it to. This article explains the reason behind this behavior and provides solutions for preserving your background.

The Problem:

Imagine you have a simple Compose layout with a red background and a white circle drawn on top using Canvas. You want to create a semi-transparent effect, so you apply BlendMode.SRC_IN to the circle's paint. However, instead of the expected semi-transparent circle, the entire background disappears, leaving only the transparent circle!

@Composable
fun TransparentCircle() {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.Red)
    ) {
        Canvas(modifier = Modifier.fillMaxSize()) {
            drawCircle(
                color = Color.White,
                radius = 100f,
                center = Offset(100f, 100f),
                alpha = 0.5f, // Semi-transparent
                blendMode = BlendMode.SRC_IN
            )
        }
    }
}

Understanding BlendMode.SRC_IN:

The BlendMode.SRC_IN mode dictates that only the overlapping areas between the source (the circle in this case) and destination (the background) are visible. Since the circle's alpha is set to 0.5f, the overlapping areas will be 50% opaque, while the non-overlapping areas, including the background, become fully transparent.

Solution 1: Use BlendMode.SRC_ATOP:

The key is to choose a BlendMode that preserves the destination (background) while applying the alpha of the source. BlendMode.SRC_ATOP achieves this by rendering the source only in the overlapping areas, maintaining the destination's original color.

@Composable
fun TransparentCircle() {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.Red)
    ) {
        Canvas(modifier = Modifier.fillMaxSize()) {
            drawCircle(
                color = Color.White,
                radius = 100f,
                center = Offset(100f, 100f),
                alpha = 0.5f, // Semi-transparent
                blendMode = BlendMode.SRC_ATOP // Use SRC_ATOP
            )
        }
    }
}

Solution 2: Use a Different Approach:

Instead of directly blending on the canvas, you can achieve a semi-transparent effect by layering composables. For example, you could use alpha modifier on the Box containing the circle to control its transparency, preserving the background.

@Composable
fun TransparentCircle() {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.Red)
    ) {
        Box(
            modifier = Modifier
                .fillMaxSize()
                .alpha(0.5f) // Semi-transparent effect
        ) {
            Canvas(modifier = Modifier.fillMaxSize()) {
                drawCircle(
                    color = Color.White,
                    radius = 100f,
                    center = Offset(100f, 100f)
                )
            }
        }
    }
}

Conclusion:

While BlendMode.SRC_IN offers a powerful blending option, it's crucial to understand its impact on the underlying canvas. By using BlendMode.SRC_ATOP or alternative techniques, you can achieve the desired semi-transparent effect while preserving your background. Remember to choose the approach that best suits your specific needs and context.