Skip to content

[web] Decode images once in instantiateImageCodecWithSize - #196029

Open
TeddyYeung wants to merge 5 commits into
flutter:masterfrom
TeddyYeung:patch-1
Open

TeddyYeung wants to merge 5 commits into
flutter:masterfrom
TeddyYeung:patch-1

Conversation

@TeddyYeung

@TeddyYeung TeddyYeung commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

On the web, instantiateImageCodecWithSize with a getTargetSize callback (the path ResizeImage, and so Image.network(cacheWidth: ...), goes through) decoded every image twice. It created a codec, decoded a full-size first frame into a ui.Image only to read width and height, disposed it, and then created a second codec at the target size. On Chromium, the full-size frame was also copied into a full-size ImageBitmap before upload, so a resized image still allocated its full-size bitmap for a moment.

This PR adds engineInstantiateImageCodecWithSize. When ImageDecoder is available, it creates one BrowserImageDecoder, decodes the first frame, passes its size to getTargetSize, and returns a codec at the target size that reuses that decoder. The decoded first frame is kept by the new BrowserImageDecoder.decodeFirstFrameSize and returned by the next getNextFrame, so it is not decoded again. Later frames of animated images are decoded in order as before. A kept frame is closed if the decoder is disposed first. Without ImageDecoder, the behavior is unchanged (decode once for the size, then again at the target size).

Measurements in Chrome 154 on an Apple Silicon Mac, with a script that makes the same browser calls as the old and new paths (ImageDecoder setup and decode, createImageBitmap, resize to 400 px wide), median of 7 runs:

Image Before After Full-size bitmap no longer allocated
iphone_15.jpeg (3024x4032) 221 ms 109 ms 46.5 MB
mandrill_1600.png (1600x1600) 197 ms 89 ms 9.8 MB
example_3_12bit.avif (512x512) 75 ms 39 ms 1.0 MB

Tests:

  • image_test.dart: instantiateImageCodecWithSize creates no ui.Image before getNextFrame, and exactly one after it.
  • image_decoder_test.dart: decodeFirstFrameSize keeps the first frame for getNextFrame; dispose closes a kept frame; an animated GIF decoded through instantiateImageCodecWithSize returns the same number of frames, with the same durations in the same order, as the unsized codec.
  • The existing instantiateImageCodecWithSize tests (buffer disposal, temporary image disposal, upscaling) cover the unchanged behavior.

dart analyze and dart format pass on the changed files. felt test was not run locally, because there is no full engine checkout here.

Fixes #118985

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

…is available

Read the intrinsic size from the first frame of a single BrowserImageDecoder and return that frame as the first frame of the codec, instead of creating a codec, decoding a full-size image, and creating a second codec at the target size.
@github-actions github-actions Bot added engine flutter/engine related. See also e: labels. platform-web Web applications specifically team-web Owned by Web platform team labels Nov 5, 2026
@TeddyYeung
TeddyYeung marked this pull request as ready for review November 5, 2026 08:33
@TeddyYeung

Copy link
Copy Markdown
Contributor Author

cc @harryterkelsen, since this touches instantiateImageCodecWithSize right after #195836.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request refactors image codec instantiation with target sizing in the Flutter Web engine. It introduces engineInstantiateImageCodecWithSize and updates BrowserImageDecoder to decode and cache the first frame's size via decodeFirstFrameSize(), preventing the first frame from being decoded twice when the browser supports ImageDecoder. Additionally, corresponding unit tests have been added to verify the caching, disposal, and frame ordering behaviors. I have no feedback to provide.

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

Labels

engine flutter/engine related. See also e: labels. platform-web Web applications specifically team-web Owned by Web platform team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[web] [images] Optimize sized image loading

1 participant