Skip to content

Fix crash when drawing shadow on image using RenderNode#2635

Merged
gpeal merged 2 commits intoairbnb:masterfrom
allenchen1154:allen--canvas-restore-underflow-crash
May 27, 2025
Merged

Fix crash when drawing shadow on image using RenderNode#2635
gpeal merged 2 commits intoairbnb:masterfrom
allenchen1154:allen--canvas-restore-underflow-crash

Conversation

@allenchen1154
Copy link
Copy Markdown
Collaborator

@allenchen1154 allenchen1154 commented May 14, 2025

When using the RenderNode rendering strategy, the call to OffscreenLayer.finish() decrements the save count on the Canvas returned from OffscreenLayer.start(), so we need to know whether to skip the call to Canvas.restore() in ImageLayer, otherwise the Canvas will throw an exception.

Fixes #2609

When using the RenderNode rendering strategy, the call to `OffscreenLayer.finish()` decrements the save count on the `Canvas` returned from `OffscreenLayer.start()`, so we need to know whether to skip the call to `Canvas.restore()` in `ImageLayer`, otherwise the `Canvas` will thrown an exception.
@allenchen1154
Copy link
Copy Markdown
Collaborator Author

Draft for now as I would like to add a repro test case.

@github-actions
Copy link
Copy Markdown

Snapshot Tests
API 23: Report Diff
API 31: Report Diff

*/
public boolean finishDecrementsCanvasSaveCount() {
// endRecording() will decrement the save count of the Canvas returned by beginRecording()
return currentStrategy == RenderStrategy.RENDER_NODE;
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@geomaster I'm not sure why this happens, but I narrowed down the beginRecording()/endRecording() interaction as the root cause of the underflow error.

@allenchen1154
Copy link
Copy Markdown
Collaborator Author

allenchen1154 commented May 19, 2025

For reference my repro code is as follows:

val composition by rememberLottieComposition(
    LottieCompositionSpec
        .RawRes(R.raw.feat_campaignlisting_guestsubmission__submit_request),
)
val progress by animateLottieCompositionAsState(composition = composition, isPlaying = true)

val bitmap = Bitmap.createScaledBitmap(
    BitmapFactory.decodeResource(LocalContext.current.resources, R.drawable.feat_campaignlisting_guestsubmission__img_1),
    324,
    238,
    false,
)

val dynamicProperties = rememberLottieDynamicProperties(
    rememberLottieDynamicProperty(LottieProperty.IMAGE, bitmap, "**", "Card.png"),
)

LottieAnimation(
    composition = composition,
    progress = { progress },
    dynamicProperties = dynamicProperties,
)

@allenchen1154 allenchen1154 marked this pull request as ready for review May 20, 2025 00:05
@github-actions
Copy link
Copy Markdown

Snapshot Tests
API 23: Report Diff
API 31: Report Diff


internal fun SnapshotTestCaseContext.getBitmapFromAssets(name: String): Bitmap {
return BitmapFactory.decodeStream(context.assets.open(name), null, BitmapFactory.Options())!!
}
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Moved this utility function so it can be shared

@gpeal gpeal merged commit 1cbe8c1 into airbnb:master May 27, 2025
7 checks passed
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.

CRASH: Fatal Exception: java.lang.IllegalStateException: Underflow in restore - more restores than saves

2 participants