kotlinandroid-jetpack-composedrawingandroid-jetpack

Jetpack Compose: Drawing outside of Composable bounds While using Transparent Colors


I'm creating a custom component in Jetpack Compose that needs to be drawn beyond the component's bounds, Which might use transparent color in BlendMode. I'm using CompositingStrategy for handling this but facing some issues.

Below is the example recreation of the issue.

@Composable
@Preview(widthDp = 600, heightDp = 600)
private fun Preview() {
    Column(
        Modifier.fillMaxSize().background(Color.Blue),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Canvas(
            modifier = Modifier.size(250.dp).graphicsLayer(
                compositingStrategy = CompositingStrategy.Offscreen,
//                compositingStrategy = CompositingStrategy.ModulateAlpha
            ).border(1.dp, color = Color.Black)
        ) {
            drawCircle(
                Color.Red,
                (size.minDimension/2f)*1.5f
            )
            drawCircle(
                Color.Transparent,
                (size.minDimension/2f)*1f,
                blendMode = BlendMode.Src
            )
        }
    }
}

On using CompositingStrategy.Offscreen: Handles alpha correctly but clips the drawing to the component's size.

Result with CompositingStrategy.Offscreen

On using CompositingStrategy.ModulateAlpha: Solves the clipping issue, but transparent colors Inner Circle are not working.

Result with CompositingStrategy.ModulateAlpha

So is there any way of achieving both of these at the same time?

Below is the Result i am expecting, where the inner circle is transparent, So the blue background is visible:

Expected result


Solution

  • compositingStrategy = CompositingStrategy.Offscreen
    

    clips content in Composable and Modifiers after it.

    By default even if you can draw anything to any Composable even if their size is zero.

    To apply Porter-Duff or blend modes correctly you can create a layer with

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

    And draw inside it with

    @Composable
    @Preview(widthDp = 600, heightDp = 600)
    private fun Preview() {
        Column(
            Modifier.fillMaxSize().background(Color.Blue),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Canvas(
                modifier = Modifier.size(250.dp)
                    .border(1.dp, color = Color.Black)
            ) {
    
                drawWithLayer {
                    drawCircle(
                        Color.Red,
                        (size.minDimension/2f)*1.5f
                    )
                    drawCircle(
                        Color.Transparent,
                        (size.minDimension/2f)*1f,
                        blendMode = BlendMode.Src
                    )
                }
            }
        }
    }
    

    enter image description here

    Jetpack Compose Applying PorterDuffMode to Image

    How to clip or cut a Composable?