usuiat / Zoomable

Jetpack Compose library that enables contents zooming with pinch gesture.
https://usuiat.github.io/Zoomable/
Apache License 2.0
375 stars 20 forks source link

Loading high resolutions images leads to crash #116

Closed marek-scholtz closed 11 months ago

marek-scholtz commented 11 months ago

Loading high resolution images (e.g. panoramas) to Image or AsyncImage composable with zoomable modifier leads to crash. Removing zoomable modifier fixes the crash. It seems, that "maximum supported resolution" depends on device.

FATAL EXCEPTION: main
Process: menion.android.locus.test, PID: 19507
java.lang.RuntimeException: Canvas: trying to draw too large(124467200bytes) bitmap.
    at android.graphics.RecordingCanvas.throwIfCannotDraw(RecordingCanvas.java:280)
    at android.graphics.BaseRecordingCanvas.drawBitmap(BaseRecordingCanvas.java:88)
    at androidx.compose.ui.graphics.AndroidCanvas.drawImageRect-HPBpro0(AndroidCanvas.android.kt:271)
    at androidx.compose.ui.graphics.drawscope.CanvasDrawScope.drawImage-AZ2fEMs(CanvasDrawScope.kt:263)
    at androidx.compose.ui.node.LayoutNodeDrawScope.drawImage-AZ2fEMs(Unknown Source:40)
    at androidx.compose.ui.graphics.drawscope.DrawScope.drawImage-AZ2fEMs$default(DrawScope.kt:510)
    at androidx.compose.ui.graphics.painter.BitmapPainter.onDraw(BitmapPainter.kt:93)
    at androidx.compose.ui.graphics.painter.Painter.draw-x_KDEd0(Painter.kt:212)
    at coil.compose.AsyncImagePainter.onDraw(AsyncImagePainter.kt:207)
    at androidx.compose.ui.graphics.painter.Painter.draw-x_KDEd0(Painter.kt:212)
    at androidx.compose.ui.draw.PainterNode.draw(PainterModifier.kt:342)
    at androidx.compose.ui.node.LayoutNodeDrawScope.drawDirect-x_KDEd0$ui_release(LayoutNodeDrawScope.kt:105)
    at androidx.compose.ui.node.LayoutNodeDrawScope.draw-x_KDEd0$ui_release(LayoutNodeDrawScope.kt:86)
    at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:365)
    at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:354)
    at androidx.compose.ui.node.LayoutModifierNodeCoordinator.performDraw(LayoutModifierNodeCoordinator.kt:182)
    at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:362)
    at androidx.compose.ui.node.NodeCoordinator.access$drawContainedDrawModifiers(NodeCoordinator.kt:54)
    at androidx.compose.ui.node.NodeCoordinator$invoke$1.invoke(NodeCoordinator.kt:384)
    at androidx.compose.ui.node.NodeCoordinator$invoke$1.invoke(NodeCoordinator.kt:383)
    at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2299)
    at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:467)
    at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:230)
    at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:133)
    at androidx.compose.ui.node.NodeCoordinator.invoke(NodeCoordinator.kt:383)
    at androidx.compose.ui.node.NodeCoordinator.invoke(NodeCoordinator.kt:54)
    at androidx.compose.ui.platform.RenderNodeApi29.record(RenderNodeApi29.android.kt:209)
    at androidx.compose.ui.platform.RenderNodeLayer.updateDisplayList(RenderNodeLayer.android.kt:305)
    at androidx.compose.ui.platform.AndroidComposeView.dispatchDraw(AndroidComposeView.android.kt:1138)
    at android.view.View.draw(View.java:21428)
    at android.view.View.updateDisplayListIfDirty(View.java:20302)
    at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4372)
    at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4345)
    at android.view.View.updateDisplayListIfDirty(View.java:20262)
    at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4372)
    at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4345)
    at android.view.View.updateDisplayListIfDirty(View.java:20262)
    at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4372)
    at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4345)
    at android.view.View.updateDisplayListIfDirty(View.java:20262)
    at android.view.View.draw(View.java:21157)
    at android.view.ViewGroup.drawChild(ViewGroup.java:4388)
    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4149)
    at android.view.View.draw(View.java:21428)
    at com.android.internal.policy.DecorView.draw(DecorView.java:806)
    at android.view.View.updateDisplayListIfDirty(View.java:20302)
    at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:575)
    at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:581)
    at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:654)
    at android.view.ViewRootImpl.draw(ViewRootImpl.java:3608)
    at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:3416)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2753)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1719)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7602)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1041)
    at android.view.Choreographer.doCallbacks(Choreographer.java:864)
    at android.view.Choreographer.doFrame(Choreographer.java:799)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1026)
    at android.os.Handler.handleCallback(Handler.java:883)
    at android.os.Handler.dispatchMessage(Handler.java:100)
    at android.os.Looper.loop(Looper.java:214)
    at android.app.ActivityThread.main(ActivityThread.java:7397)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)
usuiat commented 11 months ago

@marek-scholtz Thank you for letting me know. I have tried with several high resolution images. However, I could not reproduce the symptom of crashing only when zoomable is used. Images that stopped app that use zoomable also stopped app that do not use zoomable. Is there any good way to reproduce this?

marek-scholtz commented 11 months ago

Thank you for your response @usuiat! After deeper investigation, I found that it has something in common with Coil's ImageRequest and its size parameter. Setting this parameter to Size.ORIGINAL should improve zoomable experience, but when the image has high resolution, it crashes regardless of the use of your zoomable modifier. Without settings size parameter to Size.ORIGINAL, Coil is shrinking images, so zooming loses a bit of its meaning but it doesn't crash at least 🙂. Thank you and closing the issue.

panpf commented 1 month ago

I recommend a similar image zooming library ZoomImage (https://github.com/panpf/zoomimage).

It perfectly supports Compose Multiplatform and Android View, supports zooming, panning, positioning, rotation, and subsampling of large images. It also integrates image loaders such as sketch, glide, picasso, coil, etc.

The most important thing is that it can zoom large images without memory overflow