Skip to content

[web] Fix decoding of still AVIF images on Chrome - #195958

Merged
auto-submit[bot] merged 8 commits into
flutter:masterfrom
TeddyYeung:web-avif-prefer-animation
Nov 12, 2026
Merged

auto-submit[bot] merged 8 commits into
flutter:masterfrom
TeddyYeung:web-avif-prefer-animation

Conversation

@TeddyYeung

@TeddyYeung TeddyYeung commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

On Chrome, ImageDecoder rejects still (non-animated) AVIF images with InvalidStateError: Failed to retrieve track metadata. when it is constructed with preferAnimation: true. BrowserImageDecoder always passed true, so decoding a still AVIF image failed on Chrome. The issue reports this failure with both CanvasKit and Skwasm, and both renderers use BrowserImageDecoder on Chrome. Safari and Firefox are not affected because the ImageDecoder path is only used on Blink.

Format detection is not the cause. detectImageType already returns ImageType.avif, and image/avif is passed to ImageDecoder.

This PR stops passing preferAnimation. When it is omitted, Chrome still selects the animated track of animated images. preferAnimation: false does not work, because then animated AVIF decodes as a single frame.

How I checked it:

  • In Chrome 156, I decoded all 141 GIF, WebP, AVIF, PNG and JPEG files in Skia's resources/images (the files served as /test_images/ in the web engine tests) with preferAnimation: true and with preferAnimation omitted. 134 results were identical, including every animated GIF, WebP and AVIF file (same frame count and repetition count). The other 7 were the still AVIF files, which failed with true and decoded as 1 frame when omitted.
  • In an app built with Flutter 3.44.9 (dart2js and dart2wasm), dog.avif fails to decode today. It decodes at 180x180 when preferAnimation is removed from the ImageDecoder init. Animated AVIF, GIF and WebP keep their frame counts (13, 13 and 7).
  • In the same app, I declared an extension type with the same constructor shape as the patched ImageDecoderOptions. With dart2js and dart2wasm, the object it creates has no preferAnimation key when the argument is not passed.

I added four tests to test/ui/image_decoder_test.dart: a still AVIF decodes as 1 frame at its full size, and an animated AVIF decodes with more than 1 frame, each both from bytes and from a URL (the test server serves .avif as image/avif, so the URL tests cover the path that streams the response to ImageDecoder). I could not run felt test locally, because I do not have a full engine checkout.

Fixes #160600

Pre-launch Checklist

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

Chrome's ImageDecoder rejects still AVIF images with "Failed to
retrieve track metadata" when preferAnimation is true. Stop passing
preferAnimation. When it is omitted, Chrome still selects the animated
track for animated GIF, WebP, and AVIF images.

Fixes flutter#160600
@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 4, 2026

@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 modifies the BrowserImageDecoder to omit the preferAnimation property, preventing Chrome from failing to decode still AVIF images. The ImageDecoderOptions interface has been updated to make preferAnimation nullable, and regression tests have been added to verify that both still and animated AVIF images are decoded correctly. I have no feedback to provide as there were no review comments.

@navaronbracke navaronbracke 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.

Just some drive by comments on the test

@flutter-zl flutter-zl added the CICD Run CI/CD label Nov 4, 2026
The new AVIF tests fetch /test_images/*.avif. The felt test server
returned HTTP 500 for them, because its Content-Type map had no entry
for .avif.
@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Nov 5, 2026
Skip the AVIF tests via the skip flag when ImageDecoder is not supported instead of returning early, and register codec/image disposal with addTearDown.
instantiateImageCodecFromUrl streams responses with a known image Content-Type directly to ImageDecoder. The test server now serves .avif as image/avif, so this covers the streaming path in addition to the byte path.
navaronbracke
navaronbracke previously approved these changes Nov 5, 2026

@navaronbracke navaronbracke 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.

LGTM, except for one minor nit for potentially a new test (maybe it's not needed, but worth looking into)

@navaronbracke navaronbracke added the CICD Run CI/CD label Nov 5, 2026
@TeddyYeung

Copy link
Copy Markdown
Contributor Author

@navaronbracke Thanks for the review! Your approval was dismissed by my follow-up push: Linux linux_unopt failed Check formatting on the new tests, so 0a35de0 applies dart format to them (no behavior change). 3b658b5 adds the animated AVIF from a URL test. Could you take another look and re-add the CICD label when you have a moment?

navaronbracke
navaronbracke previously approved these changes Nov 5, 2026
@navaronbracke navaronbracke added the CICD Run CI/CD label Nov 5, 2026
@navaronbracke

Copy link
Copy Markdown
Contributor

FYI @harryterkelsen I noticed that you had to do some setup for AVIF in #196010

When reviewing this PR, i noticed that there is some setup for serving AVIF, so I think maybe your comment in the "detects image type of a Uint8List view into a larger buffer" test group is a bit outdated? (196010 landed already, so maybe for a follow up?)

@harryterkelsen

Copy link
Copy Markdown
Contributor

@navaronbracke , thanks for the heads up. I'll update that comment in a follow-up PR

@TeddyYeung

Copy link
Copy Markdown
Contributor Author

Hi @harryterkelsen, friendly ping: this has an approval from @navaronbracke and presubmits are green, but Google testing is still waiting for approval from a Googler. Could you take a look when you have a moment? Thanks!

@harryterkelsen
harryterkelsen self-requested a review November 10, 2026 22:45
Uri(path: '/test_images/alphabetAnim.avif'),
);
addTearDown(codec.dispose);
expect(codec.frameCount, greaterThan(1));

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.

Optional suggestion: This test verifies codec.frameCount > 1, but unlike the still AVIF tests on lines 116 and 140, it doesn't call codec.getNextFrame() to confirm that an actual frame can be decoded from the URL stream into a raster image. Would it be worth calling final ui.FrameInfo frame = await codec.getNextFrame(); addTearDown(frame.image.dispose); here (and perhaps in the raw bytes test on line 127) as well to exercise the frame decoding path?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks, good call. Done in d0cd36b: both animated AVIF tests (raw bytes and URL) now call getNextFrame() and check that the first frame is 100x100.

@navaronbracke the new commit dismissed your approval and dropped the CICD label. Could you take another look when you have a moment?

@harryterkelsen harryterkelsen 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.

LGTM

@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Nov 11, 2026
@TeddyYeung

TeddyYeung commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor Author

@harryterkelsen Thanks for the approval! ci.yaml validation was failing because this branch still had Mac web_tool_tests, which #196276 removed, so the presubmits were never scheduled. I merged master to fix that. Your approval is still there, but the push dropped the CICD label. Could you re-add it when you have a moment?

@harryterkelsen harryterkelsen added the CICD Run CI/CD label Nov 11, 2026
@navaronbracke navaronbracke added the autosubmit Merge PR when tree becomes green via auto submit App label Nov 11, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Nov 11, 2026
@auto-submit

auto-submit Bot commented Nov 11, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/195958, because - The status or check suite Google testing has failed. Please fix the issues identified (or deflake) before re-applying this label.

@harryterkelsen harryterkelsen added the autosubmit Merge PR when tree becomes green via auto submit App label Nov 11, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Nov 11, 2026
@auto-submit

auto-submit Bot commented Nov 11, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/195958, because - The status or check suite Google testing has failed. Please fix the issues identified (or deflake) before re-applying this label.

@TeddyYeung

Copy link
Copy Markdown
Contributor Author

@harryterkelsen The Google testing check failed on the latest head (8e1f89b) with "Internal tests failed. Please contact a Google developer to help investigate the failure." The public presubmits pass and the only change since your approval is the master merge. Could you take a look at the internal failure when you have a moment? Thanks!

@TeddyYeung TeddyYeung closed this Nov 12, 2026
@TeddyYeung TeddyYeung reopened this Nov 12, 2026
@fluttergithubbot

Copy link
Copy Markdown
Contributor

An existing Git SHA, 8e1f89b4eb18efdfdf0c624955dab7c4a005c7ca, was detected, and no actions were taken.

To re-trigger presubmits after closing or re-opeing a PR, or pushing a HEAD commit (i.e. with --force) that already was pushed before, push a blank commit (git commit --allow-empty -m "Trigger Build") or rebase to continue.

@harryterkelsen harryterkelsen added the autosubmit Merge PR when tree becomes green via auto submit App label Nov 12, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue Nov 12, 2026
Merged via the queue into flutter:master with commit 03f1d60 Nov 12, 2026
32 of 33 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Nov 12, 2026
auto-submit Bot pushed a commit to flutter/packages that referenced this pull request Nov 13, 2026
…13653)

Manual roll Flutter from 9c427b5d7b1c to 1b9585481095 (32 revisions)

Manual roll requested by [email protected]

flutter/flutter@9c427b5...1b95854

2026-11-13 [email protected] Roll pub packages (flutter/flutter#196541)
2026-11-13 [email protected] Do not route PRs for review without cla, convert to draft (flutter/flutter#196341)
2026-11-13 [email protected] Roll Skia from d87a6c04da74 to 11f46cfe8838 (13 revisions) (flutter/flutter#196533)
2026-11-13 [email protected] [flutter_tools, flutter_test] Deprecate --experimental-faster-testing (flutter/flutter#196123)
2026-11-13 [email protected] [flutter_tools] Handle RPCError in ScreenshotCommand.runSkia (flutter/flutter#196132)
2026-11-13 [email protected] Roll Dart SDK from a8b961fd48bb to c91e8b6674e2 (2 revisions) (flutter/flutter#196502)
2026-11-13 [email protected] Roll Skia from 0c5ce4960cef to d87a6c04da74 (12 revisions) (flutter/flutter#196497)
2026-11-13 [email protected] Don't call `skia_image()`  for raster-owned images in `DlImage::Equals` (flutter/flutter#196473)
2026-11-13 [email protected] [flutter_tools] Require FlutterCommandRunner DI fields and extract findCommandName (flutter/flutter#196331)
2026-11-13 [email protected] [web] Unify ui.Canvas behind a shared EngineCanvas frontend (flutter/flutter#196348)
2026-11-13 [email protected] Link to MediaQuery.displayFeaturesOf from DisplayFeatures (flutter/flutter#193728)
2026-11-13 [email protected] Removes reference to unnecessary ensureSemantics call in doc (flutter/flutter#195831)
2026-11-13 [email protected] [flutter_tools] Register trackWidgetCreation in AppleBuildOptionsBundle (flutter/flutter#196078)
2026-11-12 [email protected] [Android] Skip the HardwareBuffer defect fallback for platform views (flutter/flutter#193441)
2026-11-12 [email protected] Fix supports_decal check in framebuffer_blend.frag (flutter/flutter#196227)
2026-11-12 [email protected] [ios] Update the screen in didUpdateEffectiveGeometry (flutter/flutter#195508)
2026-11-12 [email protected] [flutter_tools] Add `Cache-Control` headers for web test assets (flutter/flutter#196414)
2026-11-12 [email protected] Roll Skia from 3b71ec08218f to 0c5ce4960cef (16 revisions) (flutter/flutter#196472)
2026-11-12 [email protected] [web] Render bounded ImageFilter.blur on wimp (flutter/flutter#196081)
2026-11-12 [email protected] Roll Dart SDK from 69567bd3c427 to a8b961fd48bb (2 revisions) (flutter/flutter#196464)
2026-11-12 [email protected] [engine] Release the Skia OpenGL resource context on shutdown (flutter/flutter#193592)
2026-11-12 [email protected] [web] Fix decoding of still AVIF images on Chrome (flutter/flutter#195958)
2026-11-12 [email protected] Gate SurfaceControl context setting on API 34+ (flutter/flutter#196394)
2026-11-12 [email protected] [flutter_tools] Only pass on a valid root tool in DASH__TOOL (flutter/flutter#196228)
2026-11-12 [email protected] Support GL_TEXTURE_EXTERNAL_OES embedder textures in Impeller (flutter/flutter#195857)
2026-11-12 [email protected] Stop building linux/wasm_release in local_engine_builds (flutter/flutter#196345)
2026-11-12 [email protected] Split flame out of Linux customer_testing (flutter/flutter#196347)
2026-11-12 Jiwon Lee [Windows] Fix Space input after Alt+Space with previous Microsoft IME (flutter/flutter#192105)
2026-11-12 [email protected] [flutter_tools] Add ToolExtensionException, exit() guard, graceful shutdown, and base modifiers (flutter/flutter#195908)
2026-11-12 [email protected] Roll Packages from 653628e to 14f9933 (9 revisions) (flutter/flutter#196450)
2026-11-12 [email protected] Roll Dart SDK from e5add7a25b32 to 69567bd3c427 (2 revisions) (flutter/flutter#196437)
2026-11-12 [email protected] Roll Skia from 9aa2513a4ac9 to 3b71ec08218f (1 revision) (flutter/flutter#196436)

If this roll has caused a breakage, revert this CL and set the roller
to dry run mode using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC [email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

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

Labels

CICD Run CI/CD 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.

AVIF Image doesn't work on Chrome browsers

5 participants