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.