From 68432e94d92c7e944ca4bca3f34398f7d91c0529 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Mon, 20 Nov 2023 15:10:21 -0700 Subject: [PATCH 01/29] initial add support for CSS color() function proposal --- ...0000-add-support-for-css-color-function.md | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 proposals/0000-add-support-for-css-color-function.md diff --git a/proposals/0000-add-support-for-css-color-function.md b/proposals/0000-add-support-for-css-color-function.md new file mode 100644 index 00000000..cd6c240b --- /dev/null +++ b/proposals/0000-add-support-for-css-color-function.md @@ -0,0 +1,62 @@ +--- +title: Add support for CSS color() function +author: + - Ryan Linton + - Yulian Glukhenko +date: 2023-11-20 +--- + +# RFC0000: Add support for CSS color() function + +## Summary + +React Native does [not currently support](https://github.com/facebook/react-native/issues/41517) wide color gamut color spaces (e.g. display-p3). This proposal discusses adding support for the CSS color() function enabling support for additional wide-gamut color spaces. + +## Basic example + +```js +StyleSheet.create({ + backgroundColor: "color(display-p3 1 0.5 0)", + color: "color(display-p3 1 0.5 0 / .5)", +}); +``` + +## Motivation + +Since most new devices support wide-gamut color spaces React Native should support them as well. The Display P3 color space has had native support since Android 8.0 and iOS 9.3. The color() function was introduced with [CSS Color Module Level 4](https://drafts.csswg.org/css-color/#color-function), much of which is already implemented in React Native. Also Flutter [recently added support](https://github.com/flutter/flutter/issues/55092) for Display P3 on iOS with plans to follow up with support for Android and then the framework itself. + +## Detailed design + +This is the bulk of the RFC. Explain the design in enough detail for somebody familiar with React Native to understand, and for somebody familiar with the implementation to implement. This should get into specifics and corner-cases, and include examples of how the feature is used. Any new terminology should be defined here. + +## Drawbacks + +Why should we _not_ do this? Please consider: + +- implementation cost, both in term of code size and complexity +- whether the proposed feature can be implemented in user space +- the impact on teaching people React Native +- integration of this feature with other existing and planned features +- cost of migrating existing React Native applications (is it a breaking change?) + +There are tradeoffs to choosing any path. Attempt to identify them here. + +## Alternatives + +What other designs have been considered? Why did you select your approach? + +## Adoption strategy + +If we implement this proposal, how will existing React Native developers adopt it? Is this a breaking change? Can we write a codemod? Should we coordinate with other projects or libraries? + +## How we teach this + +What names and terminology work best for these concepts and why? How is this idea best presented? As a continuation of existing React patterns? + +Would the acceptance of this proposal mean the React Native documentation must be re-organized or altered? Does it change how React Native is taught to new developers at any level? + +How should this feature be taught to existing React Native developers? + +## Unresolved questions + +Optional, but suggested for first drafts. What parts of the design are still TBD? From 48beb66b397aa12509135bf2b82d6e3a9a02aa25 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Mon, 20 Nov 2023 21:02:17 -0700 Subject: [PATCH 02/29] fill in more details --- ...0000-add-support-for-css-color-function.md | 31 +++++++------------ 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/proposals/0000-add-support-for-css-color-function.md b/proposals/0000-add-support-for-css-color-function.md index cd6c240b..c77f6257 100644 --- a/proposals/0000-add-support-for-css-color-function.md +++ b/proposals/0000-add-support-for-css-color-function.md @@ -10,7 +10,7 @@ date: 2023-11-20 ## Summary -React Native does [not currently support](https://github.com/facebook/react-native/issues/41517) wide color gamut color spaces (e.g. display-p3). This proposal discusses adding support for the CSS color() function enabling support for additional wide-gamut color spaces. +React Native does [not currently support](https://github.com/facebook/react-native/issues/41517) wide color gamut color spaces (i.e. display-p3). This proposal discusses adding support for the CSS color() function enabling support for additional wide-gamut color spaces. ## Basic example @@ -23,40 +23,31 @@ StyleSheet.create({ ## Motivation -Since most new devices support wide-gamut color spaces React Native should support them as well. The Display P3 color space has had native support since Android 8.0 and iOS 9.3. The color() function was introduced with [CSS Color Module Level 4](https://drafts.csswg.org/css-color/#color-function), much of which is already implemented in React Native. Also Flutter [recently added support](https://github.com/flutter/flutter/issues/55092) for Display P3 on iOS with plans to follow up with support for Android and then the framework itself. +Since most new devices support wider gamut color spaces React Native should support them as well. The Display P3 color space has had native support since Android 8.0 and iOS 9.3. The color() function was introduced with [CSS Color Module Level 4](https://drafts.csswg.org/css-color/#color-function), much of which is already implemented in React Native. Also Flutter [recently added support](https://github.com/flutter/flutter/issues/55092) for Display P3 on iOS with plans to follow up with support for Android and then the framework itself. ## Detailed design -This is the bulk of the RFC. Explain the design in enough detail for somebody familiar with React Native to understand, and for somebody familiar with the implementation to implement. This should get into specifics and corner-cases, and include examples of how the feature is used. Any new terminology should be defined here. +1. Parse color() function in [normalizeColor](https://github.com/facebook/react-native/blob/63213712125795ac082597dad2716258b90cdcd5/packages/normalize-color/index.js#L235) +2. Include the color space info in the return value of [processColor](https://github.com/facebook/react-native/blob/63213712125795ac082597dad2716258b90cdcd5/packages/react-native/Libraries/StyleSheet/processColor.js) (i.e. display-p3 or rgba) +3. Update iOS to handle new color values (UIColor colorWithDisplayP3Red:green:blue:alpha:) +4. Update Android to handle new color values (??) ## Drawbacks -Why should we _not_ do this? Please consider: - -- implementation cost, both in term of code size and complexity -- whether the proposed feature can be implemented in user space -- the impact on teaching people React Native -- integration of this feature with other existing and planned features -- cost of migrating existing React Native applications (is it a breaking change?) - -There are tradeoffs to choosing any path. Attempt to identify them here. +- Color parsing will be slightly more complicated than before. ## Alternatives -What other designs have been considered? Why did you select your approach? +We could do nothing and require users to patch package for Display P3 color support. But this is a disappointing developer experience and not a satisfying solution. ## Adoption strategy -If we implement this proposal, how will existing React Native developers adopt it? Is this a breaking change? Can we write a codemod? Should we coordinate with other projects or libraries? +This is intended to be a non-breaking change with zero impact to existing users. Adoption is entirely opt-in by using the color function in styles to start using display-p3 colors. ## How we teach this -What names and terminology work best for these concepts and why? How is this idea best presented? As a continuation of existing React patterns? - -Would the acceptance of this proposal mean the React Native documentation must be re-organized or altered? Does it change how React Native is taught to new developers at any level? - -How should this feature be taught to existing React Native developers? +We should document the inclusion of the color function in the [official color reference](https://reactnative.dev/docs/colors). Developers can then start using it if they choose. ## Unresolved questions -Optional, but suggested for first drafts. What parts of the design are still TBD? +While Android does support wide color gamut do [Android View]()'s actually support it? These methods take `int`s while the Android color reference states wide gamut colors are `long`s. From 72dbafacc90703a1adc7c5c7ef2ef515e720e7a2 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Mon, 20 Nov 2023 21:32:43 -0700 Subject: [PATCH 03/29] some android details --- proposals/0000-add-support-for-css-color-function.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0000-add-support-for-css-color-function.md b/proposals/0000-add-support-for-css-color-function.md index c77f6257..081e7b79 100644 --- a/proposals/0000-add-support-for-css-color-function.md +++ b/proposals/0000-add-support-for-css-color-function.md @@ -28,9 +28,9 @@ Since most new devices support wider gamut color spaces React Native should supp ## Detailed design 1. Parse color() function in [normalizeColor](https://github.com/facebook/react-native/blob/63213712125795ac082597dad2716258b90cdcd5/packages/normalize-color/index.js#L235) -2. Include the color space info in the return value of [processColor](https://github.com/facebook/react-native/blob/63213712125795ac082597dad2716258b90cdcd5/packages/react-native/Libraries/StyleSheet/processColor.js) (i.e. display-p3 or rgba) +2. Include the color space info in the return value of [processColor](https://github.com/facebook/react-native/blob/63213712125795ac082597dad2716258b90cdcd5/packages/react-native/Libraries/StyleSheet/processColor.js) (i.e. display-p3) 3. Update iOS to handle new color values (UIColor colorWithDisplayP3Red:green:blue:alpha:) -4. Update Android to handle new color values (??) +4. Update Android to handle new color values (@ColorLong long p3 = pack(1.0f, 1.0f, 0.0f, 1.0f, ColorSpace.Named.DISPLAY_P3); Color opaqueYellow = Color.valueOf(p3);) ## Drawbacks From 8844a6757bb153250496896f7a26122f5d11a423 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Mon, 20 Nov 2023 21:38:42 -0700 Subject: [PATCH 04/29] add some more details --- proposals/0000-add-support-for-css-color-function.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0000-add-support-for-css-color-function.md b/proposals/0000-add-support-for-css-color-function.md index 081e7b79..33868c46 100644 --- a/proposals/0000-add-support-for-css-color-function.md +++ b/proposals/0000-add-support-for-css-color-function.md @@ -29,8 +29,8 @@ Since most new devices support wider gamut color spaces React Native should supp 1. Parse color() function in [normalizeColor](https://github.com/facebook/react-native/blob/63213712125795ac082597dad2716258b90cdcd5/packages/normalize-color/index.js#L235) 2. Include the color space info in the return value of [processColor](https://github.com/facebook/react-native/blob/63213712125795ac082597dad2716258b90cdcd5/packages/react-native/Libraries/StyleSheet/processColor.js) (i.e. display-p3) -3. Update iOS to handle new color values (UIColor colorWithDisplayP3Red:green:blue:alpha:) -4. Update Android to handle new color values (@ColorLong long p3 = pack(1.0f, 1.0f, 0.0f, 1.0f, ColorSpace.Named.DISPLAY_P3); Color opaqueYellow = Color.valueOf(p3);) +3. Update iOS [RCTConvert](https://github.com/facebook/react-native/blob/781b637db4268ad7f5f3910d99ebb5203467840b/packages/react-native/React/Base/RCTConvert.m#L881) to handle new color values (UIColor colorWithDisplayP3Red:green:blue:alpha:) +4. Update Android [ColorPropConverter](https://github.com/facebook/react-native/blob/781b637db4268ad7f5f3910d99ebb5203467840b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ColorPropConverter.java#L27) to handle new color values (@ColorLong long p3 = pack(1.0f, 1.0f, 0.0f, 1.0f, ColorSpace.Named.DISPLAY_P3); Color opaqueYellow = Color.valueOf(p3);) ## Drawbacks From b5c49634915bc25cd9cd5d7a45fdf392977e2c8a Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Tue, 21 Nov 2023 13:09:40 -0700 Subject: [PATCH 05/29] add more details --- ...0000-add-support-for-css-color-function.md | 64 +++++++++++++++++-- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/proposals/0000-add-support-for-css-color-function.md b/proposals/0000-add-support-for-css-color-function.md index 33868c46..cd26b54a 100644 --- a/proposals/0000-add-support-for-css-color-function.md +++ b/proposals/0000-add-support-for-css-color-function.md @@ -27,14 +27,65 @@ Since most new devices support wider gamut color spaces React Native should supp ## Detailed design -1. Parse color() function in [normalizeColor](https://github.com/facebook/react-native/blob/63213712125795ac082597dad2716258b90cdcd5/packages/normalize-color/index.js#L235) -2. Include the color space info in the return value of [processColor](https://github.com/facebook/react-native/blob/63213712125795ac082597dad2716258b90cdcd5/packages/react-native/Libraries/StyleSheet/processColor.js) (i.e. display-p3) -3. Update iOS [RCTConvert](https://github.com/facebook/react-native/blob/781b637db4268ad7f5f3910d99ebb5203467840b/packages/react-native/React/Base/RCTConvert.m#L881) to handle new color values (UIColor colorWithDisplayP3Red:green:blue:alpha:) -4. Update Android [ColorPropConverter](https://github.com/facebook/react-native/blob/781b637db4268ad7f5f3910d99ebb5203467840b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ColorPropConverter.java#L27) to handle new color values (@ColorLong long p3 = pack(1.0f, 1.0f, 0.0f, 1.0f, ColorSpace.Named.DISPLAY_P3); Color opaqueYellow = Color.valueOf(p3);) +### JS Changes + +1. Parse the color() function in [normalizeColor](https://github.com/facebook/react-native/blob/63213712125795ac082597dad2716258b90cdcd5/packages/normalize-color/index.js#L235) + +```js +const COLOR_SPACE = /display-p3|srgb/ + +function getMatchers() { + if (cachedMatchers === undefined) { + cachedMatchers = { + color: new RegExp( + 'color(' + + call(COLOR_SPACE, NUMBER, NUMBER, NUMBER) + + '|' + + callWithSlashSeparator(COLOR_SPACE, NUMBER, NUMBER, NUMBER, NUMBER) + + ')' + ), +``` + +2. Include the color space in the return value of [StyleSheet processColor](https://github.com/facebook/react-native/blob/63213712125795ac082597dad2716258b90cdcd5/packages/react-native/Libraries/StyleSheet/processColor.js) and [Animated processColor](https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Animated/nodes/AnimatedColor.js) + +```js +return { ["display-p3"]: true, r, g, b, a }; +``` + +### iOS Changes + +1. Update [RCTConvert](https://github.com/facebook/react-native/blob/781b637db4268ad7f5f3910d99ebb5203467840b/packages/react-native/React/Base/RCTConvert.m#L881) to handle setting new color values for color space. If color space is not included preserve existing behavior. + +2. Update [RCTConversions](https://github.com/facebook/react-native/blob/16ad818d21773cdf25156642fae83592352ae534/packages/react-native/React/Fabric/RCTConversions.h#L37) to handle setting new color values for color space. If color space is not included preserve existing behavior. + +3. Update [RCTConversions](https://github.com/facebook/react-native/blob/ac1cdaa71620d5bb4860237cafb108f6aeae9aef/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextPrimitivesConversions.h#L116) to handle setting new color values for color space. If color space is not included preserve existing behavior. + +```objc +[UIColor colorWithDisplayP3Red:components.red green:components.green blue:components.blue alpha:components.alpha] +``` + +### Android Changes + +1. Enable the wide color gamut in [ReactActivity](https://github.com/facebook/react-native/blob/7625a502960e6b107e77542ff0d6f40fbf957322/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java#L22) if available. + +```java +Window window = getWindow(); +boolean isScreenWideColorGamut = window.getDecorView().isScreenWideColorGamut() +if (isScreenWideColorGamut) { + window.setColorMode(PixelFormat.COLOR_MODE_WIDE_COLOR_GAMUT) +} +``` + +2. Update [ColorUtil](https://github.com/facebook/react-native/blob/a6964b36294c3bfea09c0cdd65c5d0e3949f2dae/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ColorUtil.java#L17) and [ColorPropConverter](https://github.com/facebook/react-native/blob/781b637db4268ad7f5f3910d99ebb5203467840b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ColorPropConverter.java#L27) to return new color values for color space as longs. + +```java +@ColorLong long p3 = pack(1.0f, 1.0f, 0.0f, 1.0f, ColorSpace.Named.DISPLAY_P3); +Color opaqueYellow = Color.valueOf(p3); +``` ## Drawbacks -- Color parsing will be slightly more complicated than before. +Colors will be a bit more complicated than before. ## Alternatives @@ -50,4 +101,5 @@ We should document the inclusion of the color function in the [official color re ## Unresolved questions -While Android does support wide color gamut do [Android View]()'s actually support it? These methods take `int`s while the Android color reference states wide gamut colors are `long`s. +- While Android does support wide color gamut do [Android View]()'s actually support it? These methods take `int`s while the Android color reference states wide gamut colors are `long`s. +- Do we need to do additional work to support interpolating wide-gamut colors for animations? From db607fd4565746c17572dfc2a1e3050a6af622c9 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Tue, 21 Nov 2023 13:17:13 -0700 Subject: [PATCH 06/29] fix some things --- proposals/0000-add-support-for-css-color-function.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0000-add-support-for-css-color-function.md b/proposals/0000-add-support-for-css-color-function.md index cd26b54a..08d5072b 100644 --- a/proposals/0000-add-support-for-css-color-function.md +++ b/proposals/0000-add-support-for-css-color-function.md @@ -23,7 +23,7 @@ StyleSheet.create({ ## Motivation -Since most new devices support wider gamut color spaces React Native should support them as well. The Display P3 color space has had native support since Android 8.0 and iOS 9.3. The color() function was introduced with [CSS Color Module Level 4](https://drafts.csswg.org/css-color/#color-function), much of which is already implemented in React Native. Also Flutter [recently added support](https://github.com/flutter/flutter/issues/55092) for Display P3 on iOS with plans to follow up with support for Android and then the framework itself. +Since most new devices support wider gamut color spaces, React Native should support them as well. The Display P3 color space has had native support since Android 8.0 and iOS 9.3. The color() function was introduced with [CSS Color Module Level 4](https://drafts.csswg.org/css-color/#color-function), much of which is already implemented in React Native. Also Flutter [recently added support](https://github.com/flutter/flutter/issues/55092) for Display P3 on iOS with plans to follow up with support for Android and then the framework itself. ## Detailed design @@ -58,7 +58,7 @@ return { ["display-p3"]: true, r, g, b, a }; 2. Update [RCTConversions](https://github.com/facebook/react-native/blob/16ad818d21773cdf25156642fae83592352ae534/packages/react-native/React/Fabric/RCTConversions.h#L37) to handle setting new color values for color space. If color space is not included preserve existing behavior. -3. Update [RCTConversions](https://github.com/facebook/react-native/blob/ac1cdaa71620d5bb4860237cafb108f6aeae9aef/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextPrimitivesConversions.h#L116) to handle setting new color values for color space. If color space is not included preserve existing behavior. +3. Update [RCTTextPrimitivesConversions](https://github.com/facebook/react-native/blob/ac1cdaa71620d5bb4860237cafb108f6aeae9aef/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextPrimitivesConversions.h#L116) to handle setting new color values for color space. If color space is not included preserve existing behavior. ```objc [UIColor colorWithDisplayP3Red:components.red green:components.green blue:components.blue alpha:components.alpha] @@ -85,7 +85,7 @@ Color opaqueYellow = Color.valueOf(p3); ## Drawbacks -Colors will be a bit more complicated than before. +There should be no breaking changes for users but the color implementation will be a bit more complicated than before. ## Alternatives @@ -101,5 +101,5 @@ We should document the inclusion of the color function in the [official color re ## Unresolved questions -- While Android does support wide color gamut do [Android View]()'s actually support it? These methods take `int`s while the Android color reference states wide gamut colors are `long`s. -- Do we need to do additional work to support interpolating wide-gamut colors for animations? +- While Android does support wide color gamut, do [Android View]()'s actually support it? These methods take `int`s while the Android color reference states wide gamut colors are `long`s. +- Do we need to do additional work to support interpolating colors for animations? From 9e889179a2163556950092fbd61482c301cf49a2 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Tue, 21 Nov 2023 13:28:33 -0700 Subject: [PATCH 07/29] a note about docs update --- proposals/0000-add-support-for-css-color-function.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proposals/0000-add-support-for-css-color-function.md b/proposals/0000-add-support-for-css-color-function.md index 08d5072b..066da974 100644 --- a/proposals/0000-add-support-for-css-color-function.md +++ b/proposals/0000-add-support-for-css-color-function.md @@ -83,6 +83,10 @@ if (isScreenWideColorGamut) { Color opaqueYellow = Color.valueOf(p3); ``` +### Docs + +Update the [Color Reference](https://github.com/facebook/react-native-website/blob/main/docs/colors.md) to document color function usage in React Native. + ## Drawbacks There should be no breaking changes for users but the color implementation will be a bit more complicated than before. From 260b482df1ff997bd6015d4e794b7a40ab73a7c3 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Mon, 27 Nov 2023 10:47:48 -0700 Subject: [PATCH 08/29] rename RFC --- proposals/0000-add-support-for-css-color-function.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0000-add-support-for-css-color-function.md b/proposals/0000-add-support-for-css-color-function.md index 066da974..beb7c2f5 100644 --- a/proposals/0000-add-support-for-css-color-function.md +++ b/proposals/0000-add-support-for-css-color-function.md @@ -1,12 +1,12 @@ --- -title: Add support for CSS color() function +title: Add Support for Wide Gamut (DisplayP3) Colors to React Native author: - Ryan Linton - Yulian Glukhenko date: 2023-11-20 --- -# RFC0000: Add support for CSS color() function +# RFC0000: Add Support for Wide Gamut (DisplayP3) Colors to React Native ## Summary From 51f32e198f369738736e692d6f9e896ce305a570 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Mon, 27 Nov 2023 10:49:10 -0700 Subject: [PATCH 09/29] update summary Co-authored-by: Riccardo Cipolleschi --- proposals/0000-add-support-for-css-color-function.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proposals/0000-add-support-for-css-color-function.md b/proposals/0000-add-support-for-css-color-function.md index beb7c2f5..65172a08 100644 --- a/proposals/0000-add-support-for-css-color-function.md +++ b/proposals/0000-add-support-for-css-color-function.md @@ -10,7 +10,9 @@ date: 2023-11-20 ## Summary -React Native does [not currently support](https://github.com/facebook/react-native/issues/41517) wide color gamut color spaces (i.e. display-p3). This proposal discusses adding support for the CSS color() function enabling support for additional wide-gamut color spaces. +React Native does [not currently support](https://github.com/facebook/react-native/issues/41517) wide gamut color spaces (i.e. display-p3). This proposal discusses adding support for this color space in React Native, covering: +- JS implementation +- platform changes ## Basic example From e9dd026333052754a68e385bdc61c322a43a9da7 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Mon, 27 Nov 2023 10:56:16 -0700 Subject: [PATCH 10/29] add inline style example --- proposals/0000-add-support-for-css-color-function.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proposals/0000-add-support-for-css-color-function.md b/proposals/0000-add-support-for-css-color-function.md index 65172a08..fec54829 100644 --- a/proposals/0000-add-support-for-css-color-function.md +++ b/proposals/0000-add-support-for-css-color-function.md @@ -17,6 +17,8 @@ React Native does [not currently support](https://github.com/facebook/react-nati ## Basic example ```js +() => ; + StyleSheet.create({ backgroundColor: "color(display-p3 1 0.5 0)", color: "color(display-p3 1 0.5 0 / .5)", From 154fab16f86b5a108a671ee56258c8aced84dee9 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Mon, 27 Nov 2023 11:07:24 -0700 Subject: [PATCH 11/29] improve basic example --- ...0000-add-support-for-css-color-function.md | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/proposals/0000-add-support-for-css-color-function.md b/proposals/0000-add-support-for-css-color-function.md index fec54829..72ffb021 100644 --- a/proposals/0000-add-support-for-css-color-function.md +++ b/proposals/0000-add-support-for-css-color-function.md @@ -11,23 +11,36 @@ date: 2023-11-20 ## Summary React Native does [not currently support](https://github.com/facebook/react-native/issues/41517) wide gamut color spaces (i.e. display-p3). This proposal discusses adding support for this color space in React Native, covering: + - JS implementation - platform changes ## Basic example -```js -() => ; +Add a DisplayP3 background color and text color using color() function syntax per the [W3C CSS Color Module Level 4](https://www.w3.org/TR/css-color-4/#color-function) spec. +### Using StyleSheet + +```js StyleSheet.create({ - backgroundColor: "color(display-p3 1 0.5 0)", - color: "color(display-p3 1 0.5 0 / .5)", + view: { backgroundColor: "color(display-p3 1 0.5 0)" }, + text: { color: "color(display-p3 0 0.5 1)" }, }); ``` +### Using inline styles + +```jsx +const MyComp = () => ( + + + +) +``` + ## Motivation -Since most new devices support wider gamut color spaces, React Native should support them as well. The Display P3 color space has had native support since Android 8.0 and iOS 9.3. The color() function was introduced with [CSS Color Module Level 4](https://drafts.csswg.org/css-color/#color-function), much of which is already implemented in React Native. Also Flutter [recently added support](https://github.com/flutter/flutter/issues/55092) for Display P3 on iOS with plans to follow up with support for Android and then the framework itself. +Since most new devices support wider gamut color spaces, React Native should support them as well. The Display P3 color space has had native support since Android 8.0 and iOS 9.3. The color() function was introduced with [CSS Color Module Level 4](https://www.w3.org/TR/css-color-4/#color-function), much of which is already implemented in React Native. Also Flutter [recently added support](https://github.com/flutter/flutter/issues/55092) for Display P3 on iOS with plans to follow up with support for Android and then the framework itself. ## Detailed design From 9b06e73c7da9c330f4c6350777da17be01bb9281 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Mon, 27 Nov 2023 11:56:35 -0700 Subject: [PATCH 12/29] add line to motivation regarding app quality and mission --- proposals/0000-add-support-for-css-color-function.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0000-add-support-for-css-color-function.md b/proposals/0000-add-support-for-css-color-function.md index 72ffb021..e6e3f982 100644 --- a/proposals/0000-add-support-for-css-color-function.md +++ b/proposals/0000-add-support-for-css-color-function.md @@ -40,7 +40,7 @@ const MyComp = () => ( ## Motivation -Since most new devices support wider gamut color spaces, React Native should support them as well. The Display P3 color space has had native support since Android 8.0 and iOS 9.3. The color() function was introduced with [CSS Color Module Level 4](https://www.w3.org/TR/css-color-4/#color-function), much of which is already implemented in React Native. Also Flutter [recently added support](https://github.com/flutter/flutter/issues/55092) for Display P3 on iOS with plans to follow up with support for Android and then the framework itself. +Since most new devices support wider gamut color spaces, React Native should support them as well. The DisplayP3 color space has had native support since Android 8.0 and iOS 9.3. The color() function was introduced with [CSS Color Module Level 4](https://www.w3.org/TR/css-color-4/#color-function), much of which is already implemented in React Native. Flutter also [recently added support](https://github.com/flutter/flutter/issues/55092) for DisplayP3 on iOS with plans to follow up with support for Android and then the framework itself. Support for DisplayP3 colors can improve the quality of React Native apps by providing developers a wider range of available colors to use and is aligned with React Native's mission to provide the same look and feel as the underlying native platforms. ## Detailed design From 156971bc236f1f31dc5b876cc2e20274f92ce1c9 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Mon, 27 Nov 2023 16:47:16 -0700 Subject: [PATCH 13/29] add more details in how we teach this --- proposals/0000-add-support-for-css-color-function.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/proposals/0000-add-support-for-css-color-function.md b/proposals/0000-add-support-for-css-color-function.md index e6e3f982..e1410e05 100644 --- a/proposals/0000-add-support-for-css-color-function.md +++ b/proposals/0000-add-support-for-css-color-function.md @@ -118,7 +118,11 @@ This is intended to be a non-breaking change with zero impact to existing users. ## How we teach this -We should document the inclusion of the color function in the [official color reference](https://reactnative.dev/docs/colors). Developers can then start using it if they choose. +Developers familiar with [color() on web](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color) should find this feature familiar and straightforward to use. To ensure that this feature is easily accessible to all developers we should do the following: + +1. Document the inclusion of the color function in the [official color reference](https://reactnative.dev/docs/colors). +1. Partner with the Release Crew to make sure the new feature is included in a release blog post. +1. Publish a blog post on the [Infinite Red blog](https://shift.infinite.red/) ## Unresolved questions From abd3d740a0885442ff54eb343e90be0cc0f98d8f Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Tue, 28 Nov 2023 11:34:48 -0700 Subject: [PATCH 14/29] update with more details regarding RCTConvert changes --- ...0000-add-support-for-css-color-function.md | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/proposals/0000-add-support-for-css-color-function.md b/proposals/0000-add-support-for-css-color-function.md index e1410e05..2779d014 100644 --- a/proposals/0000-add-support-for-css-color-function.md +++ b/proposals/0000-add-support-for-css-color-function.md @@ -66,21 +66,47 @@ function getMatchers() { 2. Include the color space in the return value of [StyleSheet processColor](https://github.com/facebook/react-native/blob/63213712125795ac082597dad2716258b90cdcd5/packages/react-native/Libraries/StyleSheet/processColor.js) and [Animated processColor](https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Animated/nodes/AnimatedColor.js) ```js -return { ["display-p3"]: true, r, g, b, a }; +return { ["display-p3"]: true, red, green, blue, alpha }; ``` ### iOS Changes 1. Update [RCTConvert](https://github.com/facebook/react-native/blob/781b637db4268ad7f5f3910d99ebb5203467840b/packages/react-native/React/Base/RCTConvert.m#L881) to handle setting new color values for color space. If color space is not included preserve existing behavior. +```objc ++ (UIColor *)UIColor:(id)json +{ + if (!json) { + return nil; + } + if ([json isKindOfClass:[NSArray class]]) { + NSArray *components = [self NSNumberArray:json]; + CGFloat alpha = components.count > 3 ? [self CGFloat:components[3]] : 1.0; + return [UIColor colorWithRed:[self CGFloat:components[0]] + green:[self CGFloat:components[1]] + blue:[self CGFloat:components[2]] + alpha:alpha]; + } else if ([json isKindOfClass:[NSNumber class]]) { + // ... + } else if ([json isKindOfClass:[NSDictionary class]]) { + NSDictionary *dictionary = json; + id value = nil; + + // handle json with srgb color space specified + if ((value = [dictionary objectForKey:@"srgb"])) { + return [RCTConvert @[value.red, value.green, value.blue, value.alpha]]; + + // handle json with display-p3 color space specified + } else if ((value = [dictionary objectForKey:@"display-p3"])) { + return [UIColor colorWithDisplayP3Red:value.red green:value.green blue:value.blue alpha:value.alpha]; + + // ... +``` + 2. Update [RCTConversions](https://github.com/facebook/react-native/blob/16ad818d21773cdf25156642fae83592352ae534/packages/react-native/React/Fabric/RCTConversions.h#L37) to handle setting new color values for color space. If color space is not included preserve existing behavior. 3. Update [RCTTextPrimitivesConversions](https://github.com/facebook/react-native/blob/ac1cdaa71620d5bb4860237cafb108f6aeae9aef/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextPrimitivesConversions.h#L116) to handle setting new color values for color space. If color space is not included preserve existing behavior. -```objc -[UIColor colorWithDisplayP3Red:components.red green:components.green blue:components.blue alpha:components.alpha] -``` - ### Android Changes 1. Enable the wide color gamut in [ReactActivity](https://github.com/facebook/react-native/blob/7625a502960e6b107e77542ff0d6f40fbf957322/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java#L22) if available. From c0fe6890c5d9bc8b68ff28530f71b7565135f3fa Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Tue, 28 Nov 2023 12:02:27 -0700 Subject: [PATCH 15/29] add color space to color components and add more details to conversions --- ...0000-add-support-for-css-color-function.md | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/proposals/0000-add-support-for-css-color-function.md b/proposals/0000-add-support-for-css-color-function.md index 2779d014..53861de2 100644 --- a/proposals/0000-add-support-for-css-color-function.md +++ b/proposals/0000-add-support-for-css-color-function.md @@ -103,9 +103,35 @@ return { ["display-p3"]: true, red, green, blue, alpha }; // ... ``` -2. Update [RCTConversions](https://github.com/facebook/react-native/blob/16ad818d21773cdf25156642fae83592352ae534/packages/react-native/React/Fabric/RCTConversions.h#L37) to handle setting new color values for color space. If color space is not included preserve existing behavior. +2. Update [ColorComponents.h](https://github.com/facebook/react-native/blob/528f97152b7e0a7465c5b5c02e96c2c4306c78fe/packages/react-native/ReactCommon/react/renderer/graphics/ColorComponents.h) to include color space. + +```cpp +enum class ColorSpace { + sRGB, + DisplayP3 +}; + +struct ColorComponents { + float red{0}; + float green{0}; + float blue{0}; + float alpha{0}; + ColorSpace colorSpace{ColorSpace::sRGB}; // Default to sRGB +}; +``` + +3. Update [RCTConversions](https://github.com/facebook/react-native/blob/16ad818d21773cdf25156642fae83592352ae534/packages/react-native/React/Fabric/RCTConversions.h#L37) to handle setting new color values for color space. If color space is not included preserve existing behavior. + +4. Update [RCTTextPrimitivesConversions](https://github.com/facebook/react-native/blob/ac1cdaa71620d5bb4860237cafb108f6aeae9aef/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextPrimitivesConversions.h#L116) to handle setting new color values for color space. If color space is not included preserve existing behavior. -3. Update [RCTTextPrimitivesConversions](https://github.com/facebook/react-native/blob/ac1cdaa71620d5bb4860237cafb108f6aeae9aef/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextPrimitivesConversions.h#L116) to handle setting new color values for color space. If color space is not included preserve existing behavior. +```cpp +auto components = facebook::react::colorComponentsFromColor(sharedColor); +if (components.colorSpace == ColorSpace::DisplayP3) { + return [UIColor colorWithDisplayP3Red:components.red green:components.green blue:components.blue alpha:components.alpha]; +} else { + return [UIColor colorWithRed:components.red green:components.green blue:components.blue alpha:components.alpha]; +} +``` ### Android Changes From 6fbff3677dc2ac3193cc4ce64b9a21758dafa4a7 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Tue, 28 Nov 2023 12:57:16 -0700 Subject: [PATCH 16/29] add more detail to ReactActivity changes --- proposals/0000-add-support-for-css-color-function.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/proposals/0000-add-support-for-css-color-function.md b/proposals/0000-add-support-for-css-color-function.md index 53861de2..c2cd078d 100644 --- a/proposals/0000-add-support-for-css-color-function.md +++ b/proposals/0000-add-support-for-css-color-function.md @@ -145,6 +145,14 @@ if (isScreenWideColorGamut) { } ``` +This is prerequisite per [Android documentation](https://developer.android.com/training/wide-color-gamut) but they note: + +> When wide color gamut mode is enabled, the activity's window uses more memory and GPU processing for screen composition. Before enabling wide color gamut mode, you should carefully consider if the activity truly benefits from it. For example, an activity that displays photos in fullscreen is a good candidate for wide color gamut mode, but an activity that shows small thumbnails is not. + +So we'll likely want to provide a way to disable this as well. + +This won't impact any existing 32-bit integer colors since those will continue to be in sRGB color space. To actually use DisplayP3 colors it is necessary to pack the color space and 4 color components into a 64-bit long value. + 2. Update [ColorUtil](https://github.com/facebook/react-native/blob/a6964b36294c3bfea09c0cdd65c5d0e3949f2dae/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ColorUtil.java#L17) and [ColorPropConverter](https://github.com/facebook/react-native/blob/781b637db4268ad7f5f3910d99ebb5203467840b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ColorPropConverter.java#L27) to return new color values for color space as longs. ```java From 74b1792d2433de4e587b88fb6fc3b4fe13b40d76 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Tue, 28 Nov 2023 14:36:26 -0700 Subject: [PATCH 17/29] add some more detail to ColorUtil and ColorPropConverter updates --- .../0000-add-support-for-css-color-function.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/proposals/0000-add-support-for-css-color-function.md b/proposals/0000-add-support-for-css-color-function.md index c2cd078d..c3507151 100644 --- a/proposals/0000-add-support-for-css-color-function.md +++ b/proposals/0000-add-support-for-css-color-function.md @@ -156,8 +156,21 @@ This won't impact any existing 32-bit integer colors since those will continue t 2. Update [ColorUtil](https://github.com/facebook/react-native/blob/a6964b36294c3bfea09c0cdd65c5d0e3949f2dae/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ColorUtil.java#L17) and [ColorPropConverter](https://github.com/facebook/react-native/blob/781b637db4268ad7f5f3910d99ebb5203467840b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ColorPropConverter.java#L27) to return new color values for color space as longs. ```java -@ColorLong long p3 = pack(1.0f, 1.0f, 0.0f, 1.0f, ColorSpace.Named.DISPLAY_P3); -Color opaqueYellow = Color.valueOf(p3); +if (value instanceof ReadableMap) { + ReadableMap map = (ReadableMap) value; + float red = (float) map.getDouble("red"); + float green = (float) map.getDouble("green"); + float blue = (float) map.getDouble("blue"); + float alpha = (float) map.getDouble("alpha"); + int colorSpace = map.getInt("colorSpace") + + if (colorSpace === 1) { +@ColorLong long p3 = pack(red, green, blue, alpha, ColorSpace.Named.DISPLAY_P3); + return Color.valueOf(p3); + } else { + // ... + } +} ``` ### Docs From efa99cd7a42327f5eb2c9b0ca3009a03450c4c84 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Tue, 28 Nov 2023 15:00:50 -0700 Subject: [PATCH 18/29] add some lines about why we shouldn't break --- proposals/0000-add-support-for-css-color-function.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0000-add-support-for-css-color-function.md b/proposals/0000-add-support-for-css-color-function.md index c3507151..fd644cfa 100644 --- a/proposals/0000-add-support-for-css-color-function.md +++ b/proposals/0000-add-support-for-css-color-function.md @@ -165,7 +165,7 @@ if (value instanceof ReadableMap) { int colorSpace = map.getInt("colorSpace") if (colorSpace === 1) { -@ColorLong long p3 = pack(red, green, blue, alpha, ColorSpace.Named.DISPLAY_P3); + @ColorLong long p3 = pack(red, green, blue, alpha, ColorSpace.Named.DISPLAY_P3); return Color.valueOf(p3); } else { // ... @@ -179,7 +179,7 @@ Update the [Color Reference](https://github.com/facebook/react-native-website/bl ## Drawbacks -There should be no breaking changes for users but the color implementation will be a bit more complicated than before. +There should be no breaking changes for users but the color implementation will be a bit more complicated than before. On iOS colors specified using existing methods will still use the same code paths and UIColor constructor. On Android colors specified using existing methods will still default to sRGB and only if DisplayP3 is specified will they be packed into a ColorLong. ## Alternatives From dd5cd3e9244a4308848b3f94acecc4e0bf2d7402 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Wed, 29 Nov 2023 10:36:33 -0700 Subject: [PATCH 19/29] update alternatives --- proposals/0000-add-support-for-css-color-function.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/proposals/0000-add-support-for-css-color-function.md b/proposals/0000-add-support-for-css-color-function.md index fd644cfa..351b0477 100644 --- a/proposals/0000-add-support-for-css-color-function.md +++ b/proposals/0000-add-support-for-css-color-function.md @@ -183,7 +183,11 @@ There should be no breaking changes for users but the color implementation will ## Alternatives -We could do nothing and require users to patch package for Display P3 color support. But this is a disappointing developer experience and not a satisfying solution. +1. **Alternatives to CSS Color Module Level 4:**
We could use a different standard for displaying wide gamut color. For example we could follow the [Color.js](https://github.com/color-js/color.js) API: `new Color({space: "p3", coords: [0, 1, 0], alpha: .9})`. However this deviates from the existing color implementation which mostly implements [CSS Color Module Level 4](https://www.w3.org/TR/css-color-4/#color-function) and would be surprising to existing React Native developers. + +2. **Utilizing Swizzling on iOS:**
As an alternative to the proposed iOS changes, swizzling could be considered similar to [RN #41517](https://github.com/facebook/react-native/issues/41517). This has a major downside of being an all or nothing breaking change forcing all React Native users into the DisplayP3 color space as well as being a more confusing and less maintainable solution. + +3. **Full switch to DisplayP3:**
In a similar vein we could just update the constructors directly without preserving existing behavior. While this would be a simpler implementation it would be a breaking change. Any existing visual regression tests would likely break and React Native users would now be required to specify all colors in a wider gamut. ## Adoption strategy From 33af21f05e5b8b04fe84626c640baba66a3ca3a1 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Wed, 29 Nov 2023 10:37:28 -0700 Subject: [PATCH 20/29] update filename --- ...olor-function.md => 0000-add-support-for-display-p3-colors.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename proposals/{0000-add-support-for-css-color-function.md => 0000-add-support-for-display-p3-colors.md} (100%) diff --git a/proposals/0000-add-support-for-css-color-function.md b/proposals/0000-add-support-for-display-p3-colors.md similarity index 100% rename from proposals/0000-add-support-for-css-color-function.md rename to proposals/0000-add-support-for-display-p3-colors.md From 2085088a708f3eb08a980e18ff70886b4ca13b9f Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Wed, 29 Nov 2023 10:43:38 -0700 Subject: [PATCH 21/29] move Android implementation question to implementation section --- proposals/0000-add-support-for-display-p3-colors.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/0000-add-support-for-display-p3-colors.md b/proposals/0000-add-support-for-display-p3-colors.md index 351b0477..463ed8b6 100644 --- a/proposals/0000-add-support-for-display-p3-colors.md +++ b/proposals/0000-add-support-for-display-p3-colors.md @@ -13,7 +13,7 @@ date: 2023-11-20 React Native does [not currently support](https://github.com/facebook/react-native/issues/41517) wide gamut color spaces (i.e. display-p3). This proposal discusses adding support for this color space in React Native, covering: - JS implementation -- platform changes +- Platform changes ## Basic example @@ -173,6 +173,8 @@ if (value instanceof ReadableMap) { } ``` +3. Determine how to utilize wide gamut color in Android. It's not clear if [Android View]()'s actually support it directly. + ### Docs Update the [Color Reference](https://github.com/facebook/react-native-website/blob/main/docs/colors.md) to document color function usage in React Native. @@ -203,5 +205,4 @@ Developers familiar with [color() on web](https://developer.mozilla.org/en-US/do ## Unresolved questions -- While Android does support wide color gamut, do [Android View]()'s actually support it? These methods take `int`s while the Android color reference states wide gamut colors are `long`s. - Do we need to do additional work to support interpolating colors for animations? From 06c4c25a55cdc75341e27f1a356e7977bfd49fb2 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Wed, 29 Nov 2023 11:40:25 -0700 Subject: [PATCH 22/29] add question about images and setLayerPaint --- proposals/0000-add-support-for-display-p3-colors.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0000-add-support-for-display-p3-colors.md b/proposals/0000-add-support-for-display-p3-colors.md index 463ed8b6..47e0b908 100644 --- a/proposals/0000-add-support-for-display-p3-colors.md +++ b/proposals/0000-add-support-for-display-p3-colors.md @@ -173,7 +173,7 @@ if (value instanceof ReadableMap) { } ``` -3. Determine how to utilize wide gamut color in Android. It's not clear if [Android View]()'s actually support it directly. +3. Determine how best to utilize wide gamut color in Android. It's not clear if [Android View]()'s actually support it directly. It is supported by [Paint]() so perhaps it's possible via [setLayerPaint]()? ### Docs @@ -206,3 +206,4 @@ Developers familiar with [color() on web](https://developer.mozilla.org/en-US/do ## Unresolved questions - Do we need to do additional work to support interpolating colors for animations? +- Is there additional work we need to do to support wide gamut color in images? From 54e263afd28be3ff78a8972ae09ea9ba7939ca3b Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Thu, 30 Nov 2023 09:39:30 -0700 Subject: [PATCH 23/29] improve formatting Co-authored-by: Riccardo Cipolleschi --- proposals/0000-add-support-for-display-p3-colors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0000-add-support-for-display-p3-colors.md b/proposals/0000-add-support-for-display-p3-colors.md index 47e0b908..422c1de2 100644 --- a/proposals/0000-add-support-for-display-p3-colors.md +++ b/proposals/0000-add-support-for-display-p3-colors.md @@ -40,7 +40,7 @@ const MyComp = () => ( ## Motivation -Since most new devices support wider gamut color spaces, React Native should support them as well. The DisplayP3 color space has had native support since Android 8.0 and iOS 9.3. The color() function was introduced with [CSS Color Module Level 4](https://www.w3.org/TR/css-color-4/#color-function), much of which is already implemented in React Native. Flutter also [recently added support](https://github.com/flutter/flutter/issues/55092) for DisplayP3 on iOS with plans to follow up with support for Android and then the framework itself. Support for DisplayP3 colors can improve the quality of React Native apps by providing developers a wider range of available colors to use and is aligned with React Native's mission to provide the same look and feel as the underlying native platforms. +Since most new devices support wider gamut color spaces, React Native should support them as well. The DisplayP3 color space has had native support since Android 8.0 and iOS 9.3. The `color()` function was introduced with [CSS Color Module Level 4](https://www.w3.org/TR/css-color-4/#color-function), much of which is already implemented in React Native. Flutter also [recently added support](https://github.com/flutter/flutter/issues/55092) for DisplayP3 on iOS with plans to follow up with support for Android and then the framework itself. Support for DisplayP3 colors can improve the quality of React Native apps by providing developers a wider range of available colors to use and is aligned with React Native's mission to provide the same look and feel as the underlying native platforms. ## Detailed design From f3de1d40faaee46d1f40041b8ea5004b2444ba6d Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Thu, 30 Nov 2023 09:44:06 -0700 Subject: [PATCH 24/29] create srgb color directly as well --- proposals/0000-add-support-for-display-p3-colors.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/proposals/0000-add-support-for-display-p3-colors.md b/proposals/0000-add-support-for-display-p3-colors.md index 422c1de2..231c75da 100644 --- a/proposals/0000-add-support-for-display-p3-colors.md +++ b/proposals/0000-add-support-for-display-p3-colors.md @@ -80,13 +80,6 @@ return { ["display-p3"]: true, red, green, blue, alpha }; return nil; } if ([json isKindOfClass:[NSArray class]]) { - NSArray *components = [self NSNumberArray:json]; - CGFloat alpha = components.count > 3 ? [self CGFloat:components[3]] : 1.0; - return [UIColor colorWithRed:[self CGFloat:components[0]] - green:[self CGFloat:components[1]] - blue:[self CGFloat:components[2]] - alpha:alpha]; - } else if ([json isKindOfClass:[NSNumber class]]) { // ... } else if ([json isKindOfClass:[NSDictionary class]]) { NSDictionary *dictionary = json; @@ -94,7 +87,7 @@ return { ["display-p3"]: true, red, green, blue, alpha }; // handle json with srgb color space specified if ((value = [dictionary objectForKey:@"srgb"])) { - return [RCTConvert @[value.red, value.green, value.blue, value.alpha]]; + return [UIColor colorWithRed:value.red green:value.green blue:value.blue alpha:value.alpha]; // handle json with display-p3 color space specified } else if ((value = [dictionary objectForKey:@"display-p3"])) { From 79b88c31eda5be1dd8a15dd39b2fc769f1e2d24d Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Thu, 30 Nov 2023 10:14:19 -0700 Subject: [PATCH 25/29] android wide color gamut should be opt-in/disabled by default --- proposals/0000-add-support-for-display-p3-colors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0000-add-support-for-display-p3-colors.md b/proposals/0000-add-support-for-display-p3-colors.md index 231c75da..75d0e739 100644 --- a/proposals/0000-add-support-for-display-p3-colors.md +++ b/proposals/0000-add-support-for-display-p3-colors.md @@ -142,7 +142,7 @@ This is prerequisite per [Android documentation](https://developer.android.com/t > When wide color gamut mode is enabled, the activity's window uses more memory and GPU processing for screen composition. Before enabling wide color gamut mode, you should carefully consider if the activity truly benefits from it. For example, an activity that displays photos in fullscreen is a good candidate for wide color gamut mode, but an activity that shows small thumbnails is not. -So we'll likely want to provide a way to disable this as well. +So we'll likely want to leave this disabled by default on Android allow React Native developers to opt-in via a feature flag. This won't impact any existing 32-bit integer colors since those will continue to be in sRGB color space. To actually use DisplayP3 colors it is necessary to pack the color space and 4 color components into a 64-bit long value. From 0e6d251d49779a45a492c78e27a168ea3badc349 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Mon, 18 Dec 2023 10:26:42 -0700 Subject: [PATCH 26/29] add feature flag to basic example --- .../0000-add-support-for-display-p3-colors.md | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/proposals/0000-add-support-for-display-p3-colors.md b/proposals/0000-add-support-for-display-p3-colors.md index 75d0e739..358c14ca 100644 --- a/proposals/0000-add-support-for-display-p3-colors.md +++ b/proposals/0000-add-support-for-display-p3-colors.md @@ -38,6 +38,33 @@ const MyComp = () => ( ) ``` +### Using a feature flag + +Opt-in to using DisplayP3 as the default color space by using a feature flag. + +```sh +# in ios/Podfile +ENV['RCT_WIDE_GAMUT_ENABLED'] = '1' + +# in android/gradle.properties +wideGamutEnabled=true +``` + +Then specify colors as before but now they will be in the DisplayP3 color space. + +```jsx +StyleSheet.create({ + view: { backgroundColor: "rgb(255, 128, 0)" }, + text: { color: "#0080FF" }, +}); + +const MyComp = () => ( + + + +) +``` + ## Motivation Since most new devices support wider gamut color spaces, React Native should support them as well. The DisplayP3 color space has had native support since Android 8.0 and iOS 9.3. The `color()` function was introduced with [CSS Color Module Level 4](https://www.w3.org/TR/css-color-4/#color-function), much of which is already implemented in React Native. Flutter also [recently added support](https://github.com/flutter/flutter/issues/55092) for DisplayP3 on iOS with plans to follow up with support for Android and then the framework itself. Support for DisplayP3 colors can improve the quality of React Native apps by providing developers a wider range of available colors to use and is aligned with React Native's mission to provide the same look and feel as the underlying native platforms. From d4472508cf8b58631f35978ae139f95ae602333a Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Mon, 18 Dec 2023 11:20:33 -0700 Subject: [PATCH 27/29] update implementation to use feature flag --- .../0000-add-support-for-display-p3-colors.md | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/proposals/0000-add-support-for-display-p3-colors.md b/proposals/0000-add-support-for-display-p3-colors.md index 358c14ca..7c4fb8e6 100644 --- a/proposals/0000-add-support-for-display-p3-colors.md +++ b/proposals/0000-add-support-for-display-p3-colors.md @@ -101,6 +101,23 @@ return { ["display-p3"]: true, red, green, blue, alpha }; 1. Update [RCTConvert](https://github.com/facebook/react-native/blob/781b637db4268ad7f5f3910d99ebb5203467840b/packages/react-native/React/Base/RCTConvert.m#L881) to handle setting new color values for color space. If color space is not included preserve existing behavior. ```objc +enum ColorSpace: NSInteger { + case sRGB = 0 + case displayP3 = 1 +}; + ++ (UIColor *) createColorFrom:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha andColorSpace:(ColorSpace)colorSpace +{ + if (colorSpace == displayP3) { + return [UIColor colorWithDisplayP3Red:red green:green blue:blue alpha:alpha]; + } +#if RCT_WIDE_GAMUT_ENABLED + return [UIColor colorWithDisplayP3Red:red green:green blue:blue alpha:alpha]; +#else + return [UIColor red:red green:green blue:blue alpha:alpha]; +#endif +} + + (UIColor *)UIColor:(id)json { if (!json) { @@ -114,11 +131,11 @@ return { ["display-p3"]: true, red, green, blue, alpha }; // handle json with srgb color space specified if ((value = [dictionary objectForKey:@"srgb"])) { - return [UIColor colorWithRed:value.red green:value.green blue:value.blue alpha:value.alpha]; + return [self createColorFrom:value.red green:value.green blue:value.blue alpha:value.alpha andColorSpace:ColorSpace.sRGB]; // handle json with display-p3 color space specified } else if ((value = [dictionary objectForKey:@"display-p3"])) { - return [UIColor colorWithDisplayP3Red:value.red green:value.green blue:value.blue alpha:value.alpha]; + return [UIColor colorWithDisplayP3Red:value.red green:value.green blue:value.blue alpha:value.alpha andColorSpace:ColorSpace.displayP3]; // ... ``` @@ -136,7 +153,11 @@ struct ColorComponents { float green{0}; float blue{0}; float alpha{0}; +#if RCT_WIDE_GAMUT_ENABLED + ColorSpace colorSpace{ColorSpace::DisplayP3}; // Default to DisplayP3 +#else ColorSpace colorSpace{ColorSpace::sRGB}; // Default to sRGB +#endif }; ``` @@ -149,7 +170,11 @@ auto components = facebook::react::colorComponentsFromColor(sharedColor); if (components.colorSpace == ColorSpace::DisplayP3) { return [UIColor colorWithDisplayP3Red:components.red green:components.green blue:components.blue alpha:components.alpha]; } else { +#if RCT_WIDE_GAMUT_ENABLED + return [UIColor colorWithDisplayP3Red:components.red green:components.green blue:components.blue alpha:components.alpha]; +#else return [UIColor colorWithRed:components.red green:components.green blue:components.blue alpha:components.alpha]; +#endif } ``` From 77d6e67971c8d0c2e5d1ab4b4bafa9b1886f120c Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Wed, 20 Dec 2023 10:14:56 -0700 Subject: [PATCH 28/29] update flag implementation --- .../0000-add-support-for-display-p3-colors.md | 60 ++++++++++--------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/proposals/0000-add-support-for-display-p3-colors.md b/proposals/0000-add-support-for-display-p3-colors.md index 7c4fb8e6..3b5d92cd 100644 --- a/proposals/0000-add-support-for-display-p3-colors.md +++ b/proposals/0000-add-support-for-display-p3-colors.md @@ -42,12 +42,27 @@ const MyComp = () => ( Opt-in to using DisplayP3 as the default color space by using a feature flag. -```sh -# in ios/Podfile -ENV['RCT_WIDE_GAMUT_ENABLED'] = '1' +```kt +// MyMainApplication.kt -# in android/gradle.properties -wideGamutEnabled=true +import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load + +override fun onCreate() { + ... + if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { + load(wideGamutEnabled = true) + } + ... +} +``` + +```objc +// AppDelegate.mm + +- (BOOL)wideGamutEnabled +{ + return YES; +} ``` Then specify colors as before but now they will be in the DisplayP3 color space. @@ -108,14 +123,10 @@ enum ColorSpace: NSInteger { + (UIColor *) createColorFrom:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha andColorSpace:(ColorSpace)colorSpace { - if (colorSpace == displayP3) { + if (colorSpace == displayP3 || RCTWideGamutEnabled()) { return [UIColor colorWithDisplayP3Red:red green:green blue:blue alpha:alpha]; } -#if RCT_WIDE_GAMUT_ENABLED - return [UIColor colorWithDisplayP3Red:red green:green blue:blue alpha:alpha]; -#else return [UIColor red:red green:green blue:blue alpha:alpha]; -#endif } + (UIColor *)UIColor:(id)json @@ -128,15 +139,14 @@ enum ColorSpace: NSInteger { } else if ([json isKindOfClass:[NSDictionary class]]) { NSDictionary *dictionary = json; id value = nil; - - // handle json with srgb color space specified - if ((value = [dictionary objectForKey:@"srgb"])) { - return [self createColorFrom:value.red green:value.green blue:value.blue alpha:value.alpha andColorSpace:ColorSpace.sRGB]; - - // handle json with display-p3 color space specified - } else if ((value = [dictionary objectForKey:@"display-p3"])) { - return [UIColor colorWithDisplayP3Red:value.red green:value.green blue:value.blue alpha:value.alpha andColorSpace:ColorSpace.displayP3]; - + if ((value = [dictionary objectForKey:@"display-p3"]) || + (value = [dictionary objectForKey:@"srgb"])) { + CGFloat r = [[dictionary objectForKey:@"r"] floatValue]; + CGFloat g = [[dictionary objectForKey:@"g"] floatValue]; + CGFloat b = [[dictionary objectForKey:@"b"] floatValue]; + CGFloat a = [[dictionary objectForKey:@"a"] floatValue]; + ColorSpace colorSpace = [dictionary objectForKey:@"display-p3"] ? displayP3 : sRGB; + return [self createColorFrom:r green:g blue:b alpha:a andColorSpace:colorSpace]; // ... ``` @@ -153,11 +163,7 @@ struct ColorComponents { float green{0}; float blue{0}; float alpha{0}; -#if RCT_WIDE_GAMUT_ENABLED - ColorSpace colorSpace{ColorSpace::DisplayP3}; // Default to DisplayP3 -#else - ColorSpace colorSpace{ColorSpace::sRGB}; // Default to sRGB -#endif + ColorSpace colorSpace{ColorSpace::sRGB}; }; ``` @@ -167,14 +173,10 @@ struct ColorComponents { ```cpp auto components = facebook::react::colorComponentsFromColor(sharedColor); -if (components.colorSpace == ColorSpace::DisplayP3) { +if (RCTWideGamutEnabled() || components.colorSpace == ColorSpace::DisplayP3) { return [UIColor colorWithDisplayP3Red:components.red green:components.green blue:components.blue alpha:components.alpha]; } else { -#if RCT_WIDE_GAMUT_ENABLED - return [UIColor colorWithDisplayP3Red:components.red green:components.green blue:components.blue alpha:components.alpha]; -#else return [UIColor colorWithRed:components.red green:components.green blue:components.blue alpha:components.alpha]; -#endif } ``` From 3abd3ec025f740351c5c49ed53222924829055d6 Mon Sep 17 00:00:00 2001 From: Ryan Linton Date: Wed, 3 Jan 2024 12:52:25 -0700 Subject: [PATCH 29/29] update ios implementation for global flag --- .../0000-add-support-for-display-p3-colors.md | 95 ++++++++++++------- 1 file changed, 62 insertions(+), 33 deletions(-) diff --git a/proposals/0000-add-support-for-display-p3-colors.md b/proposals/0000-add-support-for-display-p3-colors.md index 3b5d92cd..92a360a2 100644 --- a/proposals/0000-add-support-for-display-p3-colors.md +++ b/proposals/0000-add-support-for-display-p3-colors.md @@ -45,23 +45,23 @@ Opt-in to using DisplayP3 as the default color space by using a feature flag. ```kt // MyMainApplication.kt -import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load - -override fun onCreate() { - ... - if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { - load(wideGamutEnabled = true) - } - ... -} +override val reactNativeHost: ReactNativeHost = + object : DefaultReactNativeHost(this) { + // ... + override val defaultColorSpace: RCTColorSpace = RCTColorSpaceDisplayP3 + } ``` ```objc // AppDelegate.mm -- (BOOL)wideGamutEnabled +#import + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - return YES; + // ... + RCTSetDefaultColorSpace(RCTColorSpaceDisplayP3) + // ... } ``` @@ -91,7 +91,7 @@ Since most new devices support wider gamut color spaces, React Native should sup 1. Parse the color() function in [normalizeColor](https://github.com/facebook/react-native/blob/63213712125795ac082597dad2716258b90cdcd5/packages/normalize-color/index.js#L235) ```js -const COLOR_SPACE = /display-p3|srgb/ +const COLOR_SPACE = 'display-p3|srgb'; function getMatchers() { if (cachedMatchers === undefined) { @@ -101,14 +101,14 @@ function getMatchers() { call(COLOR_SPACE, NUMBER, NUMBER, NUMBER) + '|' + callWithSlashSeparator(COLOR_SPACE, NUMBER, NUMBER, NUMBER, NUMBER) + - ')' + ')', ), ``` 2. Include the color space in the return value of [StyleSheet processColor](https://github.com/facebook/react-native/blob/63213712125795ac082597dad2716258b90cdcd5/packages/react-native/Libraries/StyleSheet/processColor.js) and [Animated processColor](https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Animated/nodes/AnimatedColor.js) ```js -return { ["display-p3"]: true, red, green, blue, alpha }; +return { space: "display-p3", red, green, blue, alpha }; ``` ### iOS Changes @@ -116,17 +116,41 @@ return { ["display-p3"]: true, red, green, blue, alpha }; 1. Update [RCTConvert](https://github.com/facebook/react-native/blob/781b637db4268ad7f5f3910d99ebb5203467840b/packages/react-native/React/Base/RCTConvert.m#L881) to handle setting new color values for color space. If color space is not included preserve existing behavior. ```objc -enum ColorSpace: NSInteger { - case sRGB = 0 - case displayP3 = 1 +typedef NS_ENUM(NSInteger, RCTColorSpace) { + RCTColorSpaceSRGB, + RCTColorSpaceDisplayP3, }; -+ (UIColor *) createColorFrom:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha andColorSpace:(ColorSpace)colorSpace +static RCTColorSpace defaultColorSpace = (RCTColorSpace)facebook::react::defaultColorSpace; +RCTColorSpace RCTGetDefaultColorSpace(void) +{ + return (RCTColorSpace)facebook::react::defaultColorSpace; +} +void RCTSetDefaultColorSpace(RCTColorSpace colorSpace) +{ + facebook::react::setDefaultColorSpace((facebook::react::ColorSpace)colorSpace); +} + ++ (UIColor *)createColorFrom:(CGFloat)r green:(CGFloat)g blue:(CGFloat)b alpha:(CGFloat)a { - if (colorSpace == displayP3 || RCTWideGamutEnabled()) { + RCTColorSpace space = RCTGetDefaultColorSpace(); + return [self createColorFrom:r green:g blue:b alpha:a andColorSpace:space]; +} ++ (UIColor *)createColorFrom:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha andColorSpace:(RCTColorSpace)colorSpace +{ + if (colorSpace == RCTColorSpaceDisplayP3) { return [UIColor colorWithDisplayP3Red:red green:green blue:blue alpha:alpha]; } - return [UIColor red:red green:green blue:blue alpha:alpha]; + return [UIColor colorWithRed:red green:green blue:blue alpha:alpha]; +} + ++ (RCTColorSpace)colorSpaceFromString:(NSString *)colorSpace { + if ([colorSpace isEqualToString:@"display-p3"]) { + return RCTColorSpaceDisplayP3; + } else if ([colorSpace isEqualToString:@"srgb"]) { + return RCTColorSpaceSRGB; + } + return RCTGetDefaultColorSpace(); } + (UIColor *)UIColor:(id)json @@ -139,13 +163,13 @@ enum ColorSpace: NSInteger { } else if ([json isKindOfClass:[NSDictionary class]]) { NSDictionary *dictionary = json; id value = nil; - if ((value = [dictionary objectForKey:@"display-p3"]) || - (value = [dictionary objectForKey:@"srgb"])) { + NSString *rawColorSpace = [dictionary objectForKey: @"space"]; + if ([@[@"display-p3", @"srgb"] containsObject:rawColorSpace]) { CGFloat r = [[dictionary objectForKey:@"r"] floatValue]; CGFloat g = [[dictionary objectForKey:@"g"] floatValue]; CGFloat b = [[dictionary objectForKey:@"b"] floatValue]; CGFloat a = [[dictionary objectForKey:@"a"] floatValue]; - ColorSpace colorSpace = [dictionary objectForKey:@"display-p3"] ? displayP3 : sRGB; + RCTColorSpace colorSpace = [self colorSpaceFromString: rawColorSpace]; return [self createColorFrom:r green:g blue:b alpha:a andColorSpace:colorSpace]; // ... ``` @@ -153,17 +177,22 @@ enum ColorSpace: NSInteger { 2. Update [ColorComponents.h](https://github.com/facebook/react-native/blob/528f97152b7e0a7465c5b5c02e96c2c4306c78fe/packages/react-native/ReactCommon/react/renderer/graphics/ColorComponents.h) to include color space. ```cpp -enum class ColorSpace { - sRGB, - DisplayP3 -}; +enum class ColorSpace { sRGB, DisplayP3 }; + +static ColorSpace defaultColorSpace = ColorSpace::sRGB; +ColorSpace getDefaultColorSpace() { + return defaultColorSpace; +} +void setDefaultColorSpace(ColorSpace newColorSpace) { + defaultColorSpace = newColorSpace; +} struct ColorComponents { float red{0}; float green{0}; float blue{0}; float alpha{0}; - ColorSpace colorSpace{ColorSpace::sRGB}; + ColorSpace colorSpace{getDefaultColorSpace()}; }; ``` @@ -173,11 +202,11 @@ struct ColorComponents { ```cpp auto components = facebook::react::colorComponentsFromColor(sharedColor); -if (RCTWideGamutEnabled() || components.colorSpace == ColorSpace::DisplayP3) { - return [UIColor colorWithDisplayP3Red:components.red green:components.green blue:components.blue alpha:components.alpha]; -} else { - return [UIColor colorWithRed:components.red green:components.green blue:components.blue alpha:components.alpha]; -} +return [RCTConvert createColorFrom:components.red + green:components.green + blue:components.blue + alpha:components.alpha + andColorSpace:(RCTColorSpace)components.colorSpace]; ``` ### Android Changes