Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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: https://www.google.com/url?sa=i&url=https%3A%2F%2Fstackoverflow.com%2Fquestions%2F36763696%2Fhow-to-create-a-transparent-circle-inside-rectangle-shape-in-xml-in-android&psig=AOvVaw18ZjvY-j8QyYzFw1xsmOdJ&ust=1630768957499000&source=images&cd=vfe&ved=0CAsQjRxqFwoTCPD1mveN4_ICFQAAAAAdAAAAABAE

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.

like image 409
Demigod Avatar asked Feb 15 '26 21:02

Demigod


2 Answers

You can to use clipPath with clipOp = ClipOp.Difference option:

Canvas(modifier = Modifier.fillMaxSize()) {
    val circlePath = Path().apply {
        addOval(Rect(center, size.minDimension / 2))
    }
    clipPath(circlePath, clipOp = ClipOp.Difference) {
        drawRect(SolidColor(Color.Black.copy(alpha = 0.8f)))
    }
}

like image 60
Philip Dukhov Avatar answered Feb 18 '26 11:02

Philip Dukhov


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

enter image description here

Also you can check here to get familiar with BlendModes, path operations and more about canvas.

like image 45
Thracian Avatar answered Feb 18 '26 10:02

Thracian



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!