Skip to content

[web] Implement stepped image downscaling for CanvasKit and Skwasm#184741

Merged
auto-submit[bot] merged 10 commits intoflutter:masterfrom
harryterkelsen:fix-downscaling-image-web
Apr 23, 2026
Merged

[web] Implement stepped image downscaling for CanvasKit and Skwasm#184741
auto-submit[bot] merged 10 commits intoflutter:masterfrom
harryterkelsen:fix-downscaling-image-web

Conversation

@harryterkelsen
Copy link
Copy Markdown
Contributor

This change implements a high-quality stepped downscaling strategy (similar to mipmap generation) to resolve rendering artifacts (aliasing) caused by scaling down images by large factors (scale < 0.5) on the web.

Key changes:

  • Added ImageDownscaler with a ref-counted cache for downscaled images linked to the image's source box.
  • Integrated the downscaler into drawImageRect for both CanvasKit and Skwasm renderers.
  • Conditioned the trigger on FilterQuality.medium or higher and scaling factor < 0.5 in both dimensions.
  • Handled cache eviction automatically when the source CkImage or SkwasmImage is fully disposed.
  • Added unit tests for the downscaling algorithm and caching disposal behavior.

This fixes the jagged edges and pixelated artifacts seen when heavily downscaling large images in the web engine.

Before:
Screenshot 2026-04-07 at 3 30 58 PM

After:
Screenshot 2026-04-07 at 3 30 19 PM

Fixes #135655

Pre-launch Checklist

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

Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the gemini-code-assist bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.

This change implements a high-quality stepped downscaling strategy (similar to mipmap generation) to resolve rendering artifacts (aliasing) caused by scaling down images by large factors (scale < 0.5) on the web.

Key changes:
- Added `ImageDownscaler` with a ref-counted cache for downscaled images linked to the image's source box.
- Integrated the downscaler into `drawImageRect` for both CanvasKit and Skwasm renderers.
- Conditioned the trigger on `FilterQuality.medium` or higher and scaling factor < 0.5 in both dimensions.
- Handled cache eviction automatically when the source `CkImage` or `SkwasmImage` is fully disposed.
- Added unit tests for the downscaling algorithm and caching disposal behavior.

This fixes the jagged edges and pixelated artifacts seen when heavily downscaling large images in the web engine.
@harryterkelsen harryterkelsen requested a review from mdebbar April 7, 2026 22:33
@github-actions github-actions Bot added engine flutter/engine related. See also e: labels. platform-web Web applications specifically labels Apr 7, 2026
Copy link
Copy Markdown
Contributor

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

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 implements iterative downscaling for images in the Web UI to improve rendering quality when scaling down significantly, adding a DownscaledImageCache and updating drawImageRect in CanvasKit and Skwasm. Feedback indicates that the source rectangle is currently ignored in the downscaling logic and cache keys, which will cause incorrect rendering for sub-regions and sprite sheets. Additionally, the logic should be restricted to target dimensions of at least 1x1 to prevent potential exceptions, and the source rectangle must be passed through the downscaling functions.

Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/image_downscaler.dart
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/canvas.dart
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/image_downscaler.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/image_downscaler.dart
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/image_downscaler.dart Outdated
@harryterkelsen harryterkelsen removed the request for review from mdebbar April 7, 2026 22:43
@harryterkelsen
Copy link
Copy Markdown
Contributor Author

/gemini review

Copy link
Copy Markdown
Contributor

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

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 implements iterative downscaling for the web engine's CanvasKit and Skwasm renderers to improve image quality during significant downscaling. It introduces a caching mechanism for downscaled variants and updates drawImageRect to use stepped downscaling when FilterQuality is medium or higher. Feedback identifies potential memory growth in the cache during animations, logic errors in the downscaling loop for extreme aspect ratios, and opportunities to optimize GPU memory by disposing of intermediate textures immediately. Additionally, using lower filter quality for intermediate steps in Skwasm is recommended for performance and consistency.

Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/image_downscaler.dart
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/image_downscaler.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/image_downscaler.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/canvas.dart Outdated
@harryterkelsen
Copy link
Copy Markdown
Contributor Author

/gemini review

Copy link
Copy Markdown
Contributor

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

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 introduces iterative downscaling for images in the Web UI to mitigate aliasing artifacts, adding a DownscaledImageCache and a stepped scaling algorithm for CanvasKit and Skwasm. Feedback includes broadening the downscaling trigger to support non-uniform scaling, using triple-slash documentation for private members as per the repository style guide, and simplifying the intermediate image disposal logic within the scaling loop.

Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/image_downscaler.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/image_downscaler.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/image_downscaler.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/image_downscaler.dart Outdated
@harryterkelsen
Copy link
Copy Markdown
Contributor Author

/gemini review

Copy link
Copy Markdown
Contributor

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

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 introduces an iterative downscaling mechanism for images in the Flutter web engine, specifically for CanvasKit and Skwasm renderers. It adds a new image_downscaler.dart utility to handle high-quality downscaling and caching, and updates the drawImageRect methods in both renderers to utilize this logic when appropriate. The review feedback suggests optimizing performance by reusing paint objects across intermediate downscaling steps and notes potential future improvements for cache key management.

Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/canvas.dart
@harryterkelsen
Copy link
Copy Markdown
Contributor Author

/gemini review

Copy link
Copy Markdown
Contributor

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

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 implements iterative downscaling for the web engine to address aliasing artifacts during significant downscaling operations. It introduces a DownscaledImageCache and integrates it into the drawImageRect implementations for both CanvasKit and Skwasm. Feedback identifies an optimization to lazily initialize paint objects, avoiding unnecessary allocations when a downscaled image is already present in the cache.

Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/canvas.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/canvas.dart Outdated
@harryterkelsen
Copy link
Copy Markdown
Contributor Author

/gemini review

Copy link
Copy Markdown
Contributor

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

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 implements iterative downscaling for the web engine's CanvasKit and Skwasm backends to improve image quality during large downscaling operations. It introduces a DownscaledImageCache to manage intermediate downscaled variants and logic to perform stepped downscaling. A review comment suggests refactoring the createSteppedDownscaledImage function to extract a helper for the drawing logic, which would reduce code duplication between the iterative loop and the final output step.

Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/image_downscaler.dart
@harryterkelsen
Copy link
Copy Markdown
Contributor Author

/gemini review

Copy link
Copy Markdown
Contributor

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

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 introduces iterative downscaling for the CanvasKit and Skwasm web engines to improve image quality during significant downscaling operations. It adds a new DownscaledImageCache and logic to perform downscaling in multiple steps. Review feedback indicates that the cache disposal logic in CkImage and SkwasmImage may prematurely evict entries when image clones are involved, as it triggers on the disposal of a single clone rather than the underlying reference-counted object. Additionally, it is suggested to refine the stepping threshold in createSteppedDownscaledImage to strictly maintain a maximum 2x downscale per iteration.

Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/image.dart
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/image_downscaler.dart
@harryterkelsen harryterkelsen added the CICD Run CI/CD label Apr 8, 2026
@flutter-dashboard
Copy link
Copy Markdown

Golden file changes have been found for this pull request. Click here to view and triage (e.g. because this is an intentional change).

If you are still iterating on this change and are not ready to resolve the images on the Flutter Gold dashboard, consider marking this PR as a draft pull request above. You will still be able to view image results on the dashboard, commenting will be silenced, and the check will not try to resolve itself until marked ready for review.

For more guidance, visit Writing a golden file test for package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

Changes reported for pull request #184741 at sha f04b3d7

@flutter-dashboard flutter-dashboard Bot added the will affect goldens Changes to golden files label Apr 9, 2026
@harryterkelsen harryterkelsen requested a review from mdebbar April 9, 2026 17:17
mdebbar
mdebbar previously approved these changes Apr 10, 2026
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/canvas.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/canvaskit/canvas.dart
@github-actions github-actions Bot removed the CICD Run CI/CD label Apr 14, 2026
@harryterkelsen harryterkelsen added the CICD Run CI/CD label Apr 14, 2026
@harryterkelsen harryterkelsen requested a review from mdebbar April 14, 2026 17:38
@github-actions github-actions Bot removed the CICD Run CI/CD label Apr 15, 2026
@harryterkelsen harryterkelsen added the CICD Run CI/CD label Apr 15, 2026
@github-actions github-actions Bot removed the CICD Run CI/CD label Apr 21, 2026
@harryterkelsen harryterkelsen added the CICD Run CI/CD label Apr 21, 2026
@flutter-dashboard
Copy link
Copy Markdown

Golden file changes are available for triage from new commit, Click here to view.

For more guidance, visit Writing a golden file test for package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

Changes reported for pull request #184741 at sha d208dd8

@harryterkelsen harryterkelsen added the autosubmit Merge PR when tree becomes green via auto submit App label Apr 23, 2026
@auto-submit auto-submit Bot added this pull request to the merge queue Apr 23, 2026
Merged via the queue into flutter:master with commit 89e16fd Apr 23, 2026
198 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Apr 23, 2026
auto-submit Bot pushed a commit to flutter/packages that referenced this pull request Apr 24, 2026
Roll Flutter from 5e4f16931847 to aeb96234de86 (42 revisions)

flutter/flutter@5e4f169...aeb9623

2026-04-24 robert.ancell@canonical.com Fix leak on error case (flutter/flutter#185516)
2026-04-24 engine-flutter-autoroll@skia.org Roll Skia from 04c6c369cfd0 to 1c9b0b9141e9 (2 revisions) (flutter/flutter#185529)
2026-04-24 engine-flutter-autoroll@skia.org Roll Dart SDK from f386b11262f6 to c26627715892 (1 revision) (flutter/flutter#185526)
2026-04-24 engine-flutter-autoroll@skia.org Roll Skia from 290a056fcd0e to 04c6c369cfd0 (2 revisions) (flutter/flutter#185525)
2026-04-24 chris@bracken.jp [ios] Extract SplashScreenManager from FlutterViewController (flutter/flutter#185405)
2026-04-24 engine-flutter-autoroll@skia.org Roll Fuchsia Linux SDK from j3UCWZhWx7zSl9Asy... to 9fPnyEo9PaNdXtasl... (flutter/flutter#185523)
2026-04-24 engine-flutter-autoroll@skia.org Roll Skia from 4c8bedd3c932 to 290a056fcd0e (1 revision) (flutter/flutter#185518)
2026-04-24 engine-flutter-autoroll@skia.org Roll Dart SDK from 70665fc3fd2e to f386b11262f6 (2 revisions) (flutter/flutter#185512)
2026-04-24 97480502+b-luk@users.noreply.github.com Handle hairline strokes in UberSDF (flutter/flutter#184895)
2026-04-24 engine-flutter-autoroll@skia.org Roll Skia from ea20c73ac72c to 4c8bedd3c932 (3 revisions) (flutter/flutter#185509)
2026-04-24 58529443+srujzs@users.noreply.github.com Use relative path for reloadedSourcesUri and reloaded modules (flutter/flutter#184598)
2026-04-24 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Run all flutter/flutter macOS tests using Xcode 26 and iOS 26 simulator (#185431)" (flutter/flutter#185513)
2026-04-24 engine-flutter-autoroll@skia.org Roll Skia from e8d00d634c22 to ea20c73ac72c (8 revisions) (flutter/flutter#185500)
2026-04-23 okorohelijah@google.com Run all flutter/flutter macOS tests using Xcode 26 and iOS 26 simulator (flutter/flutter#185431)
2026-04-23 rmolivares@renzo-olivares.dev update team-text-input pr triage link to filter out "waiting for response" label (flutter/flutter#185499)
2026-04-23 jason-simmons@users.noreply.github.com Check for overflow when computing the pixel buffer size for an animated PNG frame (flutter/flutter#185442)
2026-04-23 reinar@crypt.ws Impeller: Recreate Vulkan transients on surface size change (flutter/flutter#185122)
2026-04-23 matt.kosarek@canonical.com Updating the windowing API for sized to content regular and dialog windows + removing the decorated flag (flutter/flutter#184977)
2026-04-23 engine-flutter-autoroll@skia.org Roll Dart SDK from 634991935e9a to 70665fc3fd2e (2 revisions) (flutter/flutter#185488)
2026-04-23 louisehsu@google.com Updating ios triage link (flutter/flutter#185437)
2026-04-23 34871572+gmackall@users.noreply.github.com Revert "Preprovision Android NDK for flavored builds and reuse matchi… (flutter/flutter#185439)
2026-04-23 1961493+harryterkelsen@users.noreply.github.com fix(web): Fix LateInitializationError in CkSurface and SkwasmSurface (flutter/flutter#185116)
2026-04-23 1961493+harryterkelsen@users.noreply.github.com [web] Implement stepped image downscaling for CanvasKit and Skwasm (flutter/flutter#184741)
2026-04-23 30870216+gaaclarke@users.noreply.github.com Made wide_gamut_macos only run on arm64 machines (flutter/flutter#185486)
2026-04-23 chris@bracken.jp [ios] Update documentation for FlutterAppDelegate.pluginRegistrant (flutter/flutter#185201)
2026-04-23 engine-flutter-autoroll@skia.org Roll Dart SDK from bdf48933f3cf to 634991935e9a (1 revision) (flutter/flutter#185462)
2026-04-23 engine-flutter-autoroll@skia.org Roll Skia from 5fe6162546b1 to e8d00d634c22 (3 revisions) (flutter/flutter#185459)
2026-04-23 engine-flutter-autoroll@skia.org Roll Skia from 0049c5d91b08 to 5fe6162546b1 (1 revision) (flutter/flutter#185455)
2026-04-23 engine-flutter-autoroll@skia.org Roll Dart SDK from 9648f446f131 to bdf48933f3cf (19 revisions) (flutter/flutter#185451)
2026-04-23 engine-flutter-autoroll@skia.org Roll Skia from 11640d1cbc5c to 0049c5d91b08 (11 revisions) (flutter/flutter#185453)
2026-04-23 ishaquehassan@gmail.com Add disposal guidance to CurvedAnimation and CurveTween docs (flutter/flutter#184569)
2026-04-23 ishaquehassan@gmail.com Add `clipBehavior` parameter to `AnimatedCrossFade` (flutter/flutter#184545)
2026-04-23 rmacnak@google.com [fuchsia] Ask for only VMEX, not ambient-replace-as-executable. (flutter/flutter#185099)
2026-04-23 47866232+chunhtai@users.noreply.github.com Unify SemanticUpdateBuilder API for web and non-web (flutter/flutter#185433)
2026-04-22 68919043+Istiak-Ahmed78@users.noreply.github.com Fix ImageInfo.isCloneOf tests by moving async work to setUp (flutter/flutter#185254)
2026-04-22 engine-flutter-autoroll@skia.org Roll Fuchsia Linux SDK from UdpQnaP5eSaDZd3-i... to j3UCWZhWx7zSl9Asy... (flutter/flutter#185438)
2026-04-22 30870216+gaaclarke@users.noreply.github.com Adds script to run malioc locally (flutter/flutter#185371)
2026-04-22 47866232+chunhtai@users.noreply.github.com Add await mechanism to setClipboard in android_semantics_integration test (flutter/flutter#185428)
2026-04-22 43054281+camsim99@users.noreply.github.com Add ability to pass flags to `et run` (flutter/flutter#185109)
2026-04-22 47866232+chunhtai@users.noreply.github.com Add more guidelines for code review bot (flutter/flutter#185367)
2026-04-22 danny@tuppeny.com Roll pub packages (flutter/flutter#185274)
2026-04-22 saurabhmirajkar000@gmail.com Fix incorrect scale parameter reference in Image constructor docs (flutter/flutter#185403)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
...
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 will affect goldens Changes to golden files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CanvasKit Image downscaling is poor quality compared to HTML and Native

2 participants