Skip to content

Conversation

@irgaly
Copy link

@irgaly irgaly commented Dec 13, 2025

AsyncImage on Compose will exceeds maxBitmapSize limitation, and leads crash on some Android platforms.

Reproducer

  • platform: Android 14 Emulator
  • coil v3.3.0
  • Reproducer condition:
    • The image has larger size than maxBitmapSize
    • The incoming constraints of AsyncImage are Infinity
    • The Scale config is FILL
Box(Modifier.padding(padding)) {
    Column(
        Modifier
            // IntrinsicSize leads the constraints to Infinity
            .width(IntrinsicSize.Max)
            .background(Color.Yellow.copy(alpha = 0.4f))
    ) {
        AsyncImage(
            model = ImageRequest.Builder(LocalPlatformContext.current)
                // 8000px x 4000px image
                .data("https://placehold.jp/8000x4000.png")
                .build(),
            contentDescription = null,
            Modifier.fillMaxWidth().aspectRatio(4f / 1f),
            // ContentScale.Crop is used on calculation as FILL
            contentScale = ContentScale.Crop,
        )
        Text("Title text")
        Text("Body text. Body text. Body text.")
    }
}

This layout is like this:

image

In this example, the AsyncImage receives Infinity constraints in ConstraintsSizeResolver.

image

Then, resolved size is Size(width = Undefined, height = Undefined) (Size.ORIGINAL).

image

Then, DecodeUtils.computeSizeMultiplier() will calculate as Scale.FILL to (dstWidth, dstHeight) = (4096, 4000). dstWidth is limited to maxBitmapSize. So the multiplier = 1.0f will be used.

image

Finally, AsyncImage will draw the bitmap with 8000px x 4000px, and this will cause crash on some Android versions (old versions or hardware limitation?).

java.lang.RuntimeException: Canvas: trying to draw too large(128000000bytes) bitmap.

image

Fix

The problem is DecodeUtils.computeSizeMultiplier(). When it computes with Scale.FILL, the multiplier and resulting bitmap will be larger than the size of maxBitmapSize.

I fixed computeSizeMultiplier() on this PR, by limiting the multiplier after calculating that with FILL or FIT. The multiplier will be limited to the both of the maxBitmapSize.width and the maxBitmapSize.height.

image

fun veryLargeImageWithFill() = runTest {
val request = ImageRequest.Builder(context)
.data(R.drawable.very_large)
.scale(Scale.FILL)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test with FILL scaling.

@irgaly
Copy link
Author

irgaly commented Dec 14, 2025

I fixed lint error and updated API dump.

@irgaly
Copy link
Author

irgaly commented Dec 15, 2025

./gradlew iosSimulatorArm64Test macosArm64Test is passed on my local Mac and my forked repository https://github.com/irgaly/coil/actions/runs/20220087681/job/58040384774

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant