PatilShreyas / Capturable

🚀Jetpack Compose utility library for capturing Composable content and transforming it into Bitmap Image🖼️
https://patilshreyas.github.io/Capturable/
MIT License
1.06k stars 39 forks source link

Use Graphics Layer Api to capture bitmap #194

Closed Murmurl912 closed 1 month ago

Murmurl912 commented 3 months ago

When using capturable with the SharedElementTransition API, the app crashes when trying to capture a thumbnail. However, the good news is that using the graphicsLayer API to capture a bitmap works correctly.

Refer to the official documentation for more details.

Implementation:

val coroutineScope = rememberCoroutineScope()
val graphicsLayer = rememberGraphicsLayer()

Box(
    modifier = Modifier
        .drawWithContent {
            // Capture the content in the graphics layer
            graphicsLayer.record {
                // Draw the contents of the composable into the graphics layer
                this@drawWithContent.drawContent()
            }
            // Draw the graphics layer on the visible canvas
            drawLayer(graphicsLayer)
        }
        .clickable {
            coroutineScope.launch {
                val bitmap = graphicsLayer.toImageBitmap()
                // Handle the captured bitmap
            }
        }
        .background(Color.White)
) {
    Text("Hello Android", fontSize = 26.sp)
}

Stack Trace:

java.lang.IllegalArgumentException: Software rendering doesn't support drawRenderNode
    at android.graphics.Canvas.drawRenderNode(Canvas.java:2329)
    at androidx.compose.ui.graphics.layer.GraphicsLayerV29.draw(GraphicsLayerV29.android.kt:254)
    at androidx.compose.ui.graphics.layer.GraphicsLayer.draw$ui_graphics_release(AndroidGraphicsLayer.android.kt:548)
    at androidx.compose.ui.graphics.layer.GraphicsLayerKt.drawLayer(GraphicsLayer.kt:52)
    at androidx.compose.animation.SharedBoundsNode.draw(SharedContentNode.kt:269)
    at androidx.compose.ui.node.LayoutNodeDrawScope.drawDirect-eZhPAX0$ui_release(LayoutNodeDrawScope.kt:110)
    at androidx.compose.ui.node.LayoutNodeDrawScope.draw-eZhPAX0$ui_release(LayoutNodeDrawScope.kt:89)
    at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:450)
    at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:439)
    at androidx.compose.ui.node.LayoutModifierNodeCoordinator.performDraw(LayoutModifierNodeCoordinator.kt:280)
    at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:447)
    at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:439)
    at androidx.compose.ui.node.LayoutModifierNodeCoordinator.performDraw(LayoutModifierNodeCoordinator.kt:280)
    at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:447)
    at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:439)
    at androidx.compose.ui.node.LayoutNode.draw$ui_release(LayoutNode.kt:999)
    at androidx.compose.ui.node.InnerNodeCoordinator.performDraw(InnerNodeCoordinator.kt:196)
    at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:447)
    at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:439)
    at androidx.compose.ui.node.LayoutModifierNodeCoordinator.performDraw(LayoutModifierNodeCoordinator.kt:280)
    at androidx.compose.ui.node.LayoutNodeDrawScope.drawContent(LayoutNodeDrawScope.kt:68)
    at dev.shreyaspatil.capturable.CapturableModifierNode$drawCanvasIntoPicture$delegatedNode$1$1.invoke(Capturable.kt:240)
    at dev.shreyaspatil.capturable.CapturableModifierNode$drawCanvasIntoPicture$delegatedNode$1$1.invoke(Capturable.kt:236)
    at androidx.compose.ui.draw.CacheDrawModifierNodeImpl.draw(DrawModifier.kt:300)
    at androidx.compose.ui.node.LayoutNodeDrawScope.drawDirect-eZhPAX0$ui_release(LayoutNodeDrawScope.kt:110)
    at androidx.compose.ui.node.LayoutNodeDrawScope.draw-eZhPAX0$ui_release(LayoutNodeDrawScope.kt:89)
    at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:450)
    at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:439)
    at androidx.compose.ui.node.LayoutNode.draw$ui_release(LayoutNode.kt:999)
    at androidx.compose.ui.node.InnerNodeCoordinator.performDraw(InnerNodeCoordinator.kt:196)
    at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:447)
    at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:439)
    at androidx.compose.ui.node.LayoutNode.draw$ui_release(LayoutNode.kt:999)
    at androidx.compose.ui.node.InnerNodeCoordinator.performDraw(InnerNodeCoordinator.kt:196)
    at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:447)
    at androidx.compose.ui.node.NodeCoordinator.access$drawContainedDrawModifiers(NodeCoordinator.kt:58)
    at androidx.compose.ui.node.NodeCoordinator$drawBlock$1$1.invoke(NodeCoordinator.kt:469)
    at androidx.compose.ui.node.NodeCoordinator$drawBlock$1$1.invoke(NodeCoordinator.kt:468)
    at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2441)
    at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:502)
    at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:258)

Summary: The crash occurs because software rendering doesn't support drawRenderNode. Switching to the graphicsLayer API resolves this issue and allows capturing bitmaps correctly.

PatilShreyas commented 1 month ago

Great, thanks for reporting this. Will look into this.

PatilShreyas commented 1 month ago

Implemented in v3.0.0