Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Implement macOS wide gamut (Display P3) support#181769

Merged
auto-submit[bot] merged 22 commits intoflutter:masterfrom
westito:macos-wide-gamut
Feb 5, 2026
Merged

Implement macOS wide gamut (Display P3) support#181769
auto-submit[bot] merged 22 commits intoflutter:masterfrom
westito:macos-wide-gamut

Conversation

@westito
Copy link
Contributor

@westito westito commented Feb 1, 2026

Adds wide gamut color support to macOS (matching iOS), upgrades the surface pixel format from 10-bit BGRA10_XR to 16-bit float RGBA16Float on both iOS and macOS when enabled, and fixes Impeller's blur filter P3 clamping on macOS.

macOS Wide Gamut Support

  • Added DoesHardwareSupportWideGamut() hardware capability check (MTLGPUFamilyApple2 or MTLGPUFamilyMac2)
  • Wide gamut enabled when both hardware supports it and FLTEnableWideGamut plist flag is YES
  • Dynamic wide gamut switching when windows move between P3 and sRGB displays
  • Added flutter/screenshot method channel on macOS for integration testing

RGBA16Float Surface Format (iOS + macOS)

  • macOS IOSurface: kCVPixelFormatType_64RGBAHalf + MTLPixelFormatRGBA16Float
  • iOS CAMetalLayer: MTLPixelFormatRGBA16Float
  • Image decoder: always uses kRGBA_F16_SkColorType for all wide gamut images (previously only transparent images used 16-bit)

Fix Blur P3 Clamping on macOS
macOS uses the compositor/embedder path, not GPUSurfaceMetalImpeller, so UpdateOffscreenLayerPixelFormat was never called. Added the call in embedder.cc after wrapping the Metal resolve texture.

Why RGBA16Float over BGRA10_XR?
BGRA10_XR has only 10 bits per channel — values outside sRGB gamut get clamped in intermediate render targets (e.g. blur filters). RGBA16Float has 16 bits per channel with full floating-point range, preventing P3 color clamping in multi-pass rendering.

Tests

  • 9 new iOS FlutterView unit tests verifying RGBA16Float pixel format and extended sRGB color space
  • Updated macOS FlutterSurfaceManagerTest for RGBA16Float, dynamic switching, color space, and pixel format verification
  • Updated image decoder and Impeller display list tests for kR16G16B16A16Float
  • 11 macOS/iOS integration tests: image, saveLayer, codecImage, none, blur, drawnImage, container, linearGradient, radialGradient, conicalGradient, sweepGradient

Issues

#164557

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
  • I signed the [CLA].
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is [test-exempt].
  • I followed the [breaking change policy] and added [Data Driven Fixes] where supported.
  • All existing and new tests are passing.

@westito westito requested review from a team and loic-sharma as code owners February 1, 2026 09:41
@github-actions github-actions bot added platform-ios iOS applications specifically engine flutter/engine related. See also e: labels. a: desktop Running on desktop e: impeller Impeller rendering backend issues and features requests team-ios Owned by iOS platform team platform-macos labels Feb 1, 2026
Copy link
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 wide gamut (Display P3) support for macOS, upgrades the surface pixel format on both iOS and macOS to 16-bit float RGBA, and fixes a blur filter clamping issue on macOS. The changes are comprehensive, touching integration tests, the engine, and the embedder API. The switch to RGBA16Float is well-justified and correctly implemented to prevent color clamping in multi-pass rendering. The addition of dynamic wide gamut switching on macOS based on display capabilities is a great feature. The new and updated tests, especially for the surface manager and dynamic switching, are thorough and provide good coverage for the new functionality. The code quality is high, and the changes are well-structured. I have one minor suggestion to improve a comment for future clarity.

Copy link
Member

@gaaclarke gaaclarke left a comment

Choose a reason for hiding this comment

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

Can you please split this pr between MacOS support and switching iOS's pixel format to 16bit floats?

macOS will be easier to approve and accept. Switching iOS's pixel format is going to be much more involved since it will require doing some research about support of legacy devices, including performance. Honestly I'm not sure we have a the resources to do that sort of evaluation soon.

@westito
Copy link
Contributor Author

westito commented Feb 2, 2026

This pixel format change only applies to enabled wide gamut mode, not a general change. So, with turned off wide gamut (the default) nothing changes in pixel format. Or you mean changing to 16bit not acceptable even with WG mode? I think on a secondary plist option ex. FLTWideGamutCompatibleMode default true. Or inverse FLTWideGamut16bit default false. Would be acceptable? My plan is to implement WG on all platform, would be nice if iOS also supports it at full scale. However, distinguish 10bit and 16bit precision is impossible to the naked eye :D

All paths where 16bit mode set, guarded with isWideGamut
https://github.com/flutter/flutter/pull/181769/changes#diff-f4068b47f2d6b18474ad3f2d3dda143b67841c064ae6669e64aceff0563e519bL181

https://github.com/flutter/flutter/pull/181769/changes#diff-986647adf5da9851abe541f528debf7dd8e83bedd3a1490810c716c3f3629a93L138

https://github.com/flutter/flutter/pull/181769/changes#diff-4376c05368373eb61880c8dd9190ef6164ac94d2e85300d36de874168be0c0a8R88

https://github.com/flutter/flutter/pull/181769/changes#diff-4376c05368373eb61880c8dd9190ef6164ac94d2e85300d36de874168be0c0a8R108

https://github.com/flutter/flutter/pull/181769/changes#diff-5c06a16b5179fe48a7c92cdd53edd55382c10de7cf37ba2e593a1db5c76b7afdR140

https://github.com/flutter/flutter/pull/181769/changes#diff-5c06a16b5179fe48a7c92cdd53edd55382c10de7cf37ba2e593a1db5c76b7afdR210

@gaaclarke
Copy link
Member

This pixel format change only applies to enabled wide gamut mode, not a general change. So, with turned off wide gamut (the default) nothing changes in pixel format.

Wide gamut is on by default on iOS. Switching the pixel format for iOS would require performance testing and compatibility research into the old devices we support.

Or you mean changing to 16bit not acceptable even with WG mode?

For iOS, I'd like to keep it bgra10xr to avoid the work of trying to make sure it's an acceptable tradeoff. For macOS I expect float16 will be much easier to accommodate since performance is less of a concern on a desktop. We'd still need to do some digging to know where it is supported.

I think on a secondary plist option ex. FLTWideGamutCompatibleMode default true. Or inverse FLTWideGamut16bit default false.

Every option we have in the plist incurs a cost beyond the initial implementation. As the engine evolves those options can easily atrophy or increase the cost of future changes. So, while I believe in our ability to implement something like that. I wouldn't want to sign up for it unless we had a big reason to incur the cost of maintaining it.

My plan is to implement WG on all platform

FWIW we looked briefly into wg on android and I think we'll have to use 8bit per channel but interpreted as DisplayP3 because the performance hit of using float16 on android devices was too much.

@westito
Copy link
Contributor Author

westito commented Feb 2, 2026

Okay, I understand! I reverted iOS, so it uses 10bit.
For Android: It is reasonable to use 8bit only. Thanks for telling me, I will do it that way.

Copy link
Member

@gaaclarke gaaclarke left a comment

Choose a reason for hiding this comment

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

Very impressive work! The big feedback is we need to keep our tests in kB10G10R10A10XR, ideally we add f16 tests instead of replacing them. The integration test changes will be easier to review if you refrain from refactoring them and instead just add the new test that you want. Refactoring can happen in a follow up PR. The changes to macOS look good to me and seem well tested. We'll have to get a review from someone more familiar with the macOS embedder though (pr always need 2 reviews anyways).

group('end-to-end test', () {
testWidgets('look for display p3 deepest red', (WidgetTester tester) async {
app.run(app.Setup.image);
await tester.pumpAndSettle(const Duration(seconds: 2));
Copy link
Member

Choose a reason for hiding this comment

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

Why are we removing the time on pump and settle?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because the test using image preloading now. Before, there was no preload and UI had to async wait for the test image to appear. Now load is awaited and the test is much faster this way.

*
* Must be called on the platform thread.
*/
- (void)setEnableWideGamut:(BOOL)enableWideGamut;
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we want this as part of the public interface. Our engine is the one that should be calling it. Let's move this to a category on the class instead. See FlutterEngine_Internal.h

Copy link
Contributor Author

Choose a reason for hiding this comment

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

On iOS this is a static option - checked at app start - but on macOS wide gamut state is dynamically changing based on monitor capabilities, thats why this is in FlutterView header. There was no internal header so I created one.

/dev/devicelab/bin/tasks/very_long_picture_scrolling_perf__e2e_summary.dart @flar @flutter/engine
/dev/devicelab/bin/tasks/web_size__compile_test.dart @yjbanov @flutter/web
/dev/devicelab/bin/tasks/wide_gamut_ios.dart @gaaclarke @flutter/engine
/dev/devicelab/bin/tasks/wide_gamut_macos.dart @gaaclarke @flutter/engine
Copy link
Member

Choose a reason for hiding this comment

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

🫡

@flutter-dashboard
Copy link

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 #181769 at sha 53faef4

@flutter-dashboard flutter-dashboard bot added the will affect goldens Changes to golden files label Feb 3, 2026
@gaaclarke
Copy link
Member

Head's up there are some legitimate failures in the golden tests above: https://flutter-gold.skia.org/cl/github/181769

@loic-sharma loic-sharma requested a review from knopp February 3, 2026 00:33
@knopp
Copy link
Member

knopp commented Feb 3, 2026

I'm wondering if setting layer contentFormat is even necessary? I think it is a format hint for layer backed views, but it doesn't do anything when we provide the content through IOSurface (which already has the content format specified).

@westito
Copy link
Contributor Author

westito commented Feb 3, 2026

I fully replaced 16bit to 10bit at macOS. The difference isn't noticeable to the naked eye. Color picker shows #FE0000 on screen for deep red (#FF0000), but thats all. Also removed contentFormat sets as you mentioned. It isn't necessary for IOSurface as it turned out.

@gaaclarke gaaclarke self-requested a review February 3, 2026 21:11
Copy link
Member

@gaaclarke gaaclarke left a comment

Choose a reason for hiding this comment

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

This LGTM. Thanks again for another great PR. I'll give it an approval as soon as we can get it passing all the tests on CI. Looks like dev/integration_tests/wide_gamut_test/integration_test/app_test.dart isn't formatted correctly right now. Even though you might be thinking we didn't change anything for Linux, Linux is where we run the formatting tests.

NSColor* borderColor,
const std::vector<FlutterRect>& paintRegion) {
const std::vector<FlutterRect>& paintRegion,
BOOL enableWideGamut) {
Copy link
Member

Choose a reason for hiding this comment

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

This parameter was added to this method, but it isn't used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

@knopp
Copy link
Member

knopp commented Feb 3, 2026

There's one thing that is bugging me - I think we should check the surfaces inside [FlutterSurfaceManager commit:] and discard the commit if surfaces don't match current wide gamut mode.

Is possible that these surfaces have been held by raster thread during wide gamut switch and we need them out of circulation.

@westito
Copy link
Contributor Author

westito commented Feb 3, 2026

Thats reasonable. I added a guard to the commit method


// Release all unused back buffer surfaces and replace them with front surfaces.
[_backBufferCache returnSurfaces:_frontSurfaces];
// Only return surfaces to the cache if they match the current wide gamut mode.
Copy link
Member

Choose a reason for hiding this comment

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

This is not what I had in mind. You don't need to touch _frontSurfaces, those are already cleaned setEnableWideGamut. It's the incoming surfaces (method argument) that I'm concerned with. You should check if they match current wide gamut, and if not, simply return early from the method, don't do anything with the surfaces. They will be released.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I fixed

Copy link
Member

@gaaclarke gaaclarke left a comment

Choose a reason for hiding this comment

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

LGTM! Thanks. I'll file a followup issue for making wide gamut the default. We'll have to audit our testing to make sure we are comfortable with that change. Having it be optional is good for now.

Copy link
Member

@knopp knopp left a comment

Choose a reason for hiding this comment

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

LGTM! Thanks!

@gaaclarke gaaclarke added the autosubmit Merge PR when tree becomes green via auto submit App label Feb 4, 2026
@auto-submit auto-submit bot added this pull request to the merge queue Feb 5, 2026
Merged via the queue into flutter:master with commit f916dd6 Feb 5, 2026
182 of 183 checks passed
@flutter-dashboard flutter-dashboard bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Feb 5, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 5, 2026
auto-submit bot pushed a commit to flutter/packages that referenced this pull request Feb 5, 2026
Roll Flutter from bf701fefec86 to f916dd6887bf (44 revisions)

flutter/flutter@bf701fe...f916dd6

2026-02-05 [email protected] Implement macOS wide gamut (Display P3) support (flutter/flutter#181769)
2026-02-04 [email protected] Roll Skia from d23ecfbfdff9 to 8543ce512d5c (3 revisions) (flutter/flutter#181923)
2026-02-04 [email protected] Roll Dart SDK from 8001c99d952b to 8f778ffd318b (3 revisions) (flutter/flutter#181927)
2026-02-04 [email protected] Re-enable AddressSanitizer on the linux_unopt builder (flutter/flutter#181741)
2026-02-04 [email protected] Add exception to log message in ContentSizingFlag.java (flutter/flutter#181813)
2026-02-04 [email protected] Roll pub packages (flutter/flutter#181925)
2026-02-04 [email protected] [flutter_tools] Deprecate web hot reload flag (flutter/flutter#181884)
2026-02-04 [email protected] Marks platform_views_scroll_perf_impeller__timeline_summary unflaky (flutter/flutter#181649)
2026-02-04 [email protected] Roll Dart SDK from 204db085d970 to 8001c99d952b (1 revision) (flutter/flutter#181902)
2026-02-04 [email protected] Roll Skia from f37a22506eb4 to d23ecfbfdff9 (23 revisions) (flutter/flutter#181915)
2026-02-04 [email protected] In the Web codec tests, skip an undecodable image that is used to test a Skia error handling code path. (flutter/flutter#181870)
2026-02-04 [email protected] Roll Packages from 5b1bea8 to 3bddf2c (5 revisions) (flutter/flutter#181918)
2026-02-04 [email protected] Roll Fuchsia Linux SDK from UmQaaNuhkiuE8Dzug... to J2QdLcY2gyt4NP_xV... (flutter/flutter#181893)
2026-02-04 [email protected] Roll Dart SDK from 54322a0b1109 to 204db085d970 (3 revisions) (flutter/flutter#181890)
2026-02-04 [email protected] Cleanup cross imports (flutter/flutter#181807)
2026-02-04 [email protected] [Material] Remove Material import from backdrop_filter_test.dart widget tests (flutter/flutter#181386)
2026-02-04 [email protected] Move CheckedModeBanner tests to material and remove Material import from widgets banner_test (flutter/flutter#181261)
2026-02-04 [email protected] feat: Pass parameters from DropdownMenuFormField to DropDownMenu (flutter/flutter#181373)
2026-02-04 [email protected] Remove `Config complete` log when using `flutter build apk --config-only` (flutter/flutter#181864)
2026-02-04 [email protected] [Impeller] Fix flattening of very large zoomed curves with tiny stroke widths (flutter/flutter#181505)
2026-02-03 [email protected] Propagates Overlay's MediaQueryData to OverlayPortal child (flutter/flutter#181579)
2026-02-03 [email protected] Make sure that an AnimatedScale doesn't crash in 0x0 environment (flutter/flutter#181481)
2026-02-03 [email protected] Roll Dart SDK from 56294a92d5cc to 54322a0b1109 (1 revision) (flutter/flutter#181872)
2026-02-03 [email protected] Fix decorated box (flutter/flutter#179802)
2026-02-03 [email protected] Roll pub packages (flutter/flutter#181871)
2026-02-03 [email protected] Remove Material library dependency from expansible_test.dart (flutter/flutter#181657)
2026-02-03 [email protected] Organize and update fragment shader uniform tests. (flutter/flutter#181822)
2026-02-03 [email protected] fix(web_ui): handle non-invertible matrices in ImageFilter.matrix (flutter/flutter#181742)
2026-02-03 [email protected] Remove unnecessary Material import from cupertino/slider_test.dart (flutter/flutter#180957)
2026-02-03 [email protected] Remove the Flutter.xcframework as a swift dependency (flutter/flutter#181739)
2026-02-03 [email protected] feature: implementation of tooltips in the `_TestWindowingOwner` and minor bugfixes to the multiple windows example app (flutter/flutter#181510)
2026-02-03 [email protected] [Web] Fix flt-platform-view comment (flutter/flutter#181576)
2026-02-03 [email protected] Marks Linux_pixel_7pro android_verified_input_test to be unflaky (flutter/flutter#179120)
2026-02-03 [email protected] Unmark `hybrid_android_views_integration_test` as bringup (flutter/flutter#181628)
2026-02-03 [email protected] Remove material from sliver_tree_test.dart (flutter/flutter#181415)
2026-02-03 [email protected] Make `android_plugin_new_output_dir_test` only build release (flutter/flutter#181677)
2026-02-03 [email protected] Roll customer tests (flutter/flutter#181825)
2026-02-03 [email protected] Add Linux Foundation Health Score badge to README (flutter/flutter#175587)
2026-02-03 [email protected] Remove unused getters on AndroidProject class (flutter/flutter#181860)
2026-02-03 [email protected] Adds batch release doc for flutter/package (flutter/flutter#181676)
2026-02-03 [email protected] [ Tool ] Don't use `globals.platform` in `getFlutterRoot()` (flutter/flutter#181859)
2026-02-03 [email protected] Roll Packages from 837dbbd to 5b1bea8 (10 revisions) (flutter/flutter#181857)
2026-02-03 [email protected] Remove material from basic_test.dart (flutter/flutter#181444)
2026-02-03 [email protected] [ Tool ] Fix regression introduced in flutter/flutter#181421 (flutter/flutter#181826)

If this roll has caused a breakage, revert this CL and stop the roller
...
LongCatIsLooong pushed a commit to LongCatIsLooong/flutter that referenced this pull request Feb 6, 2026
Adds wide gamut color support to macOS (matching iOS), upgrades the
surface pixel format from 10-bit BGRA10_XR to 16-bit float RGBA16Float
on both iOS and macOS when enabled, and fixes Impeller's blur filter P3
clamping on macOS.

**macOS Wide Gamut Support**
- Added DoesHardwareSupportWideGamut() hardware capability check
(MTLGPUFamilyApple2 or MTLGPUFamilyMac2)
- Wide gamut enabled when both hardware supports it and
FLTEnableWideGamut plist flag is YES
- Dynamic wide gamut switching when windows move between P3 and sRGB
displays
- Added flutter/screenshot method channel on macOS for integration
testing

**RGBA16Float Surface Format (iOS + macOS)**
- macOS IOSurface: kCVPixelFormatType_64RGBAHalf +
MTLPixelFormatRGBA16Float
- iOS CAMetalLayer: MTLPixelFormatRGBA16Float
- Image decoder: always uses kRGBA_F16_SkColorType for all wide gamut
images (previously only transparent images used 16-bit)

**Fix Blur P3 Clamping on macOS**
macOS uses the compositor/embedder path, not GPUSurfaceMetalImpeller, so
UpdateOffscreenLayerPixelFormat was never called. Added the call in
embedder.cc after wrapping the Metal resolve texture.

**Why RGBA16Float over BGRA10_XR?**
BGRA10_XR has only 10 bits per channel — values outside sRGB gamut get
clamped in intermediate render targets (e.g. blur filters). RGBA16Float
has 16 bits per channel with full floating-point range, preventing P3
color clamping in multi-pass rendering.

**Tests**
- 9 new iOS FlutterView unit tests verifying RGBA16Float pixel format
and extended sRGB color space
- Updated macOS FlutterSurfaceManagerTest for RGBA16Float, dynamic
switching, color space, and pixel format verification
- Updated image decoder and Impeller display list tests for
kR16G16B16A16Float
- 11 macOS/iOS integration tests: image, saveLayer, codecImage, none,
blur, drawnImage, container, linearGradient, radialGradient,
conicalGradient, sweepGradient

### Issues
flutter#164557

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.
flutter-zl pushed a commit to flutter-zl/flutter that referenced this pull request Feb 10, 2026
Adds wide gamut color support to macOS (matching iOS), upgrades the
surface pixel format from 10-bit BGRA10_XR to 16-bit float RGBA16Float
on both iOS and macOS when enabled, and fixes Impeller's blur filter P3
clamping on macOS.

**macOS Wide Gamut Support**
- Added DoesHardwareSupportWideGamut() hardware capability check
(MTLGPUFamilyApple2 or MTLGPUFamilyMac2)
- Wide gamut enabled when both hardware supports it and
FLTEnableWideGamut plist flag is YES
- Dynamic wide gamut switching when windows move between P3 and sRGB
displays
- Added flutter/screenshot method channel on macOS for integration
testing

**RGBA16Float Surface Format (iOS + macOS)**
- macOS IOSurface: kCVPixelFormatType_64RGBAHalf +
MTLPixelFormatRGBA16Float
- iOS CAMetalLayer: MTLPixelFormatRGBA16Float
- Image decoder: always uses kRGBA_F16_SkColorType for all wide gamut
images (previously only transparent images used 16-bit)

**Fix Blur P3 Clamping on macOS**
macOS uses the compositor/embedder path, not GPUSurfaceMetalImpeller, so
UpdateOffscreenLayerPixelFormat was never called. Added the call in
embedder.cc after wrapping the Metal resolve texture.

**Why RGBA16Float over BGRA10_XR?**
BGRA10_XR has only 10 bits per channel — values outside sRGB gamut get
clamped in intermediate render targets (e.g. blur filters). RGBA16Float
has 16 bits per channel with full floating-point range, preventing P3
color clamping in multi-pass rendering.

**Tests**
- 9 new iOS FlutterView unit tests verifying RGBA16Float pixel format
and extended sRGB color space
- Updated macOS FlutterSurfaceManagerTest for RGBA16Float, dynamic
switching, color space, and pixel format verification
- Updated image decoder and Impeller display list tests for
kR16G16B16A16Float
- 11 macOS/iOS integration tests: image, saveLayer, codecImage, none,
blur, drawnImage, container, linearGradient, radialGradient,
conicalGradient, sweepGradient

### Issues
flutter#164557

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.
rickhohler pushed a commit to rickhohler/flutter that referenced this pull request Feb 19, 2026
Adds wide gamut color support to macOS (matching iOS), upgrades the
surface pixel format from 10-bit BGRA10_XR to 16-bit float RGBA16Float
on both iOS and macOS when enabled, and fixes Impeller's blur filter P3
clamping on macOS.

**macOS Wide Gamut Support**
- Added DoesHardwareSupportWideGamut() hardware capability check
(MTLGPUFamilyApple2 or MTLGPUFamilyMac2)
- Wide gamut enabled when both hardware supports it and
FLTEnableWideGamut plist flag is YES
- Dynamic wide gamut switching when windows move between P3 and sRGB
displays
- Added flutter/screenshot method channel on macOS for integration
testing

**RGBA16Float Surface Format (iOS + macOS)**
- macOS IOSurface: kCVPixelFormatType_64RGBAHalf +
MTLPixelFormatRGBA16Float
- iOS CAMetalLayer: MTLPixelFormatRGBA16Float
- Image decoder: always uses kRGBA_F16_SkColorType for all wide gamut
images (previously only transparent images used 16-bit)

**Fix Blur P3 Clamping on macOS**
macOS uses the compositor/embedder path, not GPUSurfaceMetalImpeller, so
UpdateOffscreenLayerPixelFormat was never called. Added the call in
embedder.cc after wrapping the Metal resolve texture.

**Why RGBA16Float over BGRA10_XR?**
BGRA10_XR has only 10 bits per channel — values outside sRGB gamut get
clamped in intermediate render targets (e.g. blur filters). RGBA16Float
has 16 bits per channel with full floating-point range, preventing P3
color clamping in multi-pass rendering.

**Tests**
- 9 new iOS FlutterView unit tests verifying RGBA16Float pixel format
and extended sRGB color space
- Updated macOS FlutterSurfaceManagerTest for RGBA16Float, dynamic
switching, color space, and pixel format verification
- Updated image decoder and Impeller display list tests for
kR16G16B16A16Float
- 11 macOS/iOS integration tests: image, saveLayer, codecImage, none,
blur, drawnImage, container, linearGradient, radialGradient,
conicalGradient, sweepGradient

### Issues
flutter#164557

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: desktop Running on desktop e: impeller Impeller rendering backend issues and features requests engine flutter/engine related. See also e: labels. platform-ios iOS applications specifically platform-macos team-ios Owned by iOS platform team will affect goldens Changes to golden files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants