'Android Compose: draw transparent circle on image
I have an image and I want to draw dark rectangle over it with a transparent circle, so the result will be something like this:
I have ended up with this code:
Box(modifier = Modifier
.clip(RectangleShape)
.fillMaxSize()
.background(Color.Black)
.pointerInput(Unit) {
detectTransformGestures { centroid, pan, zoom, rotation ->
scale *= zoom
}
}) {
Image(
modifier = Modifier
.align(Alignment.Center)
.graphicsLayer(
scaleX = maxOf(.2f, minOf(5f, scale)),
scaleY = maxOf(.2f, minOf(5f, scale))
),
bitmap = bitmap.asImageBitmap(),
contentDescription = null
)
Canvas(modifier = Modifier.fillMaxSize(), onDraw = {
drawRect(Color.Black.copy(alpha = 0.8f))
drawCircle(
Color.Transparent,
style = Fill,
blendMode = BlendMode.Clear
)
})
}
But it seems like it just draws a dark circle on top of the image instead of clearing darken rectangle...
It would be also super handy if you would suggest how to crop image based on this circle coordinates.
Solution 1:[1]
You need to use clipPath
in this case.
Canvas(modifier = Modifier.fillMaxSize(), onDraw = {
val circlePath = Path().apply {
addOval(Rect(center, size.minDimension / 2))
}
clipPath(circlePath, clipOp = ClipOp.Difference) {
drawRect(SolidColor(Color.Black.copy(alpha = 0.8f)))
}
})
Solution 2:[2]
You don't need to necessarily use clipPath
, you can use Porterduff(BlendMode) modes too, which is actually preferred way when you check sample codes or questions for clearing or removing some pixels or manipulating pixels.
It only requires small modifications to your code to work, you can check my answer how to apply Porterduff modes here.
1- adding graphics layer with alpha less than 1f
.graphicsLayer {
alpha = .99f
}
will fix the issue.
Full implementation
Box(
modifier = Modifier
.background(Color.Black)
.fillMaxSize()
) {
Image(
modifier = Modifier.fillMaxSize(),
painter = painterResource(id = R.drawable.landscape),
contentScale = ContentScale.Crop,
contentDescription = null
)
Canvas(
modifier = Modifier
.fillMaxSize()
// ONLY ADD THIS
.graphicsLayer {
alpha = .99f
}
) {
// Destination
drawRect(Color.Black.copy(alpha = 0.8f))
// Source
drawCircle(
color = Color.Transparent,
blendMode = BlendMode.Clear
)
}
}
2- Saving to a layer
with(drawContext.canvas.nativeCanvas) {
val checkPoint = saveLayer(null, null)
// Destination
drawRect(Color.Black.copy(alpha = 0.8f))
// Source
drawCircle(
color = Color.Transparent,
blendMode = BlendMode.Clear
)
restoreToCount(checkPoint)
}
Full Implementation
Canvas(modifier = Modifier
.fillMaxSize()
) {
with(drawContext.canvas.nativeCanvas) {
val checkPoint = saveLayer(null, null)
// Destination
drawRect(Color.Black.copy(alpha = 0.8f))
// Source
drawCircle(
color = Color.Transparent,
blendMode = BlendMode.Clear
)
restoreToCount(checkPoint)
}
}
}
Result
Also you can check here to get familiar with BlendModes, path operations and more about canvas.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Pylyp Dukhov |
Solution 2 |