1

I want to create a half-transparent layer above the camera preview, like this:

enter image description here

I have the camera preview done in my app, all I want is a half-transparent layer over my preview with a cut-out card shape, like in the picture (with rounded corners).

So: fullscreen camera preview, on top of that there is a full screen half-transparent overlay, in which there is a card-shaped hole cut out

How can I do this?

CookieMonster
  • 566
  • 2
  • 6
  • 19

1 Answers1

3

You can use BlendModes to exclude a Rectangle from a transparent layer using

@Composable
private fun TransparentCamLayout() {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .drawWithContent {

                val canvasWidth = size.width
                val canvasHeight = size.height
                val width = canvasWidth * .9f
                val height = width * 3 / 4f

                drawContent()

                drawWithLayer {

                    // Destination
                    // This is transparent color
                    drawRect(Color(0x99000000))

                    // Source
                    // This is where we extract this rect from transparent
                    drawRect(
                        topLeft = Offset((canvasWidth - width) / 2, canvasHeight * .3f),
                        size = Size(width, height),
                        color = Color.Transparent,
                        blendMode = BlendMode.SrcIn
                    )
                }

                drawRect(
                    topLeft = Offset((canvasWidth - width) / 2, canvasHeight * .3f),
                    size = Size(width, height),
                    color = Color.White,
                    style = Stroke(2.dp.toPx())
                )
            }
    ) {
        Image(
            modifier = Modifier.fillMaxSize(),
            painter = painterResource(id = R.drawable.landscape5),
            contentScale = ContentScale.Crop,
            contentDescription = null
        )
    }
}

/**
 * Draw with layer to use [BlendMode]s
 */
private fun DrawScope.drawWithLayer(block: DrawScope.() -> Unit) {
    with(drawContext.canvas.nativeCanvas) {
        val checkPoint = saveLayer(null, null)
        block()
        restoreToCount(checkPoint)
    }
}

Result

enter image description here

In this tutorial's BlendMode section you can find other usages. As in this answer for custom clipping, building a rating bar and there are many usages limited with your imagination. Blend or PorterDuff modes are very functional for building custom clipping, alpha blending or pixel manipulation.

Thracian
  • 43,021
  • 16
  • 133
  • 222
  • Great answer, it really helped me as well. One small question , do u know what the `drawWithLayer` part does? I just tried your approach without that function call and it still appears to behave as expected. – Ionut May 31 '23 at 20:57
  • drawWithLayer is a function i wrote to create another layer to apply blend modes. It creates a offscreen buffer. This is useful for leveraging different blending algorithms for masking content. You can also do this with `Modifier.graphicsLayer{compositingStrategy = CompositingStrategy.Offscreen}`. draw with layer is used in source code under the hood too when alpha of Composable is less than 1. – Thracian Jun 01 '23 at 03:39