mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Add basic DisplayP3 color support (#42830)
Summary: This adds initial support for wide gamut (DisplayP3) colors to React Native iOS per the [RFC](https://github.com/react-native-community/discussions-and-proposals/pull/738). It provides the ability to set the default color space to sRGB or DisplayP3 and provides the native code necessary to support `color()` function syntax per the [W3C CSS Color Module Level 4](https://www.w3.org/TR/css-color-4/#color-function) spec. It does _not_ yet support animations and requires additional JS code before fully supporting the `color()` function syntax. bypass-github-export-checks ## Changelog: [IOS] [ADDED] - Add basic DisplayP3 color support Pull Request resolved: https://github.com/facebook/react-native/pull/42830 Test Plan:  Follow test steps from https://github.com/facebook/react-native/issues/42831 to test support for `color()` function syntax. To globally change the default color space to DisplayP3 make the following changes to RNTester AppDelegate.mm: ```diff + #import <React/RCTConvert.h> - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // ... + RCTSetDefaultColorSpace(RCTColorSpaceDisplayP3); return [super application:application didFinishLaunchingWithOptions:launchOptions]; } ``` Reviewed By: javache Differential Revision: D53380407 Pulled By: cipolleschi fbshipit-source-id: 938523958f9021e8d98bdb1d4e254047e3ecdad7
This commit is contained in:
committed by
Facebook GitHub Bot
parent
3b93f0e3c1
commit
a40bd8e34a
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#import <React/RCTBridgeDelegate.h>
|
||||
#import <React/RCTConvert.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class RCTBridge;
|
||||
@@ -131,6 +132,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/// @return: `YES` to use RuntimeScheduler, `NO` to use JavaScript scheduler. The default value is `YES`.
|
||||
- (BOOL)runtimeSchedulerEnabled;
|
||||
|
||||
/**
|
||||
* The default `RCTColorSpace` for the app. It defaults to `RCTColorSpaceSRGB`.
|
||||
*/
|
||||
@property (nonatomic, readonly) RCTColorSpace defaultColorSpace;
|
||||
|
||||
@property (nonatomic, strong) RCTSurfacePresenterBridgeAdapter *bridgeAdapter;
|
||||
|
||||
/// This method returns a map of Component Descriptors and Components classes that needs to be registered in the
|
||||
|
||||
@@ -6,11 +6,13 @@
|
||||
*/
|
||||
|
||||
#import "RCTAppDelegate.h"
|
||||
#import <React/RCTColorSpaceUtils.h>
|
||||
#import <React/RCTCxxBridgeDelegate.h>
|
||||
#import <React/RCTLog.h>
|
||||
#import <React/RCTRootView.h>
|
||||
#import <React/RCTSurfacePresenterBridgeAdapter.h>
|
||||
#import <React/RCTUtils.h>
|
||||
#import <react/renderer/graphics/ColorComponents.h>
|
||||
#import <react/renderer/runtimescheduler/RuntimeScheduler.h>
|
||||
#import "RCTAppDelegate+Protected.h"
|
||||
#import "RCTAppSetupUtils.h"
|
||||
@@ -75,6 +77,7 @@ static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabri
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
RCTSetNewArchEnabled([self newArchEnabled]);
|
||||
[RCTColorSpaceUtils applyDefaultColorSpace:self.defaultColorSpace];
|
||||
BOOL enableTM = self.turboModuleEnabled;
|
||||
BOOL fabricEnabled = self.fabricEnabled;
|
||||
BOOL enableBridgeless = self.bridgelessEnabled;
|
||||
@@ -225,6 +228,11 @@ static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabri
|
||||
}
|
||||
}
|
||||
|
||||
- (RCTColorSpace)defaultColorSpace
|
||||
{
|
||||
return RCTColorSpaceSRGB;
|
||||
}
|
||||
|
||||
#pragma mark - New Arch Enabled settings
|
||||
|
||||
- (BOOL)newArchEnabled
|
||||
|
||||
@@ -17,6 +17,15 @@
|
||||
#import <React/RCTTextDecorationLineType.h>
|
||||
#import <yoga/Yoga.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, RCTColorSpace) {
|
||||
RCTColorSpaceSRGB,
|
||||
RCTColorSpaceDisplayP3,
|
||||
};
|
||||
|
||||
// Change the default color space
|
||||
RCTColorSpace RCTGetDefaultColorSpace(void);
|
||||
RCT_EXTERN void RCTSetDefaultColorSpace(RCTColorSpace colorSpace);
|
||||
|
||||
/**
|
||||
* This class provides a collection of conversion functions for mapping
|
||||
* JSON objects to native types and classes. These are useful when writing
|
||||
@@ -91,6 +100,13 @@ typedef NSURL RCTFileURL;
|
||||
|
||||
+ (CGAffineTransform)CGAffineTransform:(id)json;
|
||||
|
||||
+ (UIColor *)UIColorWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha;
|
||||
+ (UIColor *)UIColorWithRed:(CGFloat)red
|
||||
green:(CGFloat)green
|
||||
blue:(CGFloat)blue
|
||||
alpha:(CGFloat)alpha
|
||||
andColorSpace:(RCTColorSpace)colorSpace;
|
||||
+ (RCTColorSpace)RCTColorSpaceFromString:(NSString *)colorSpace;
|
||||
+ (UIColor *)UIColor:(id)json;
|
||||
+ (CGColorRef)CGColor:(id)json CF_RETURNS_NOT_RETAINED;
|
||||
|
||||
|
||||
@@ -879,6 +879,48 @@ static NSString *RCTSemanticColorNames(void)
|
||||
return names;
|
||||
}
|
||||
|
||||
// The iOS side is kept in synch with the C++ side by using the
|
||||
// RCTAppDelegate which, at startup, sets the default color space.
|
||||
// The usage of dispatch_once and of once_flag ensoure that those are
|
||||
// set only once when the app starts and that they can't change while
|
||||
// the app is running.
|
||||
static RCTColorSpace _defaultColorSpace = RCTColorSpaceSRGB;
|
||||
RCTColorSpace RCTGetDefaultColorSpace(void)
|
||||
{
|
||||
return _defaultColorSpace;
|
||||
}
|
||||
void RCTSetDefaultColorSpace(RCTColorSpace colorSpace)
|
||||
{
|
||||
_defaultColorSpace = colorSpace;
|
||||
}
|
||||
|
||||
+ (UIColor *)UIColorWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha
|
||||
{
|
||||
RCTColorSpace space = RCTGetDefaultColorSpace();
|
||||
return [self UIColorWithRed:red green:green blue:blue alpha:alpha andColorSpace:space];
|
||||
}
|
||||
+ (UIColor *)UIColorWithRed:(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 colorWithRed:red green:green blue:blue alpha:alpha];
|
||||
}
|
||||
|
||||
+ (RCTColorSpace)RCTColorSpaceFromString:(NSString *)colorSpace
|
||||
{
|
||||
if ([colorSpace isEqualToString:@"display-p3"]) {
|
||||
return RCTColorSpaceDisplayP3;
|
||||
} else if ([colorSpace isEqualToString:@"srgb"]) {
|
||||
return RCTColorSpaceSRGB;
|
||||
}
|
||||
return RCTGetDefaultColorSpace();
|
||||
}
|
||||
|
||||
+ (UIColor *)UIColor:(id)json
|
||||
{
|
||||
if (!json) {
|
||||
@@ -887,21 +929,29 @@ static NSString *RCTSemanticColorNames(void)
|
||||
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];
|
||||
return [self UIColorWithRed:[self CGFloat:components[0]]
|
||||
green:[self CGFloat:components[1]]
|
||||
blue:[self CGFloat:components[2]]
|
||||
alpha:alpha];
|
||||
} else if ([json isKindOfClass:[NSNumber class]]) {
|
||||
NSUInteger argb = [self NSUInteger:json];
|
||||
CGFloat a = ((argb >> 24) & 0xFF) / 255.0;
|
||||
CGFloat r = ((argb >> 16) & 0xFF) / 255.0;
|
||||
CGFloat g = ((argb >> 8) & 0xFF) / 255.0;
|
||||
CGFloat b = (argb & 0xFF) / 255.0;
|
||||
return [UIColor colorWithRed:r green:g blue:b alpha:a];
|
||||
return [self UIColorWithRed:r green:g blue:b alpha:a];
|
||||
} else if ([json isKindOfClass:[NSDictionary class]]) {
|
||||
NSDictionary *dictionary = json;
|
||||
id value = nil;
|
||||
if ((value = [dictionary objectForKey:@"semantic"])) {
|
||||
NSString *rawColorSpace = [dictionary objectForKey:@"space"];
|
||||
if ([rawColorSpace isEqualToString:@"display-p3"] || [rawColorSpace isEqualToString:@"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];
|
||||
RCTColorSpace colorSpace = [self RCTColorSpaceFromString:rawColorSpace];
|
||||
return [self UIColorWithRed:r green:g blue:b alpha:a andColorSpace:colorSpace];
|
||||
} else if ((value = [dictionary objectForKey:@"semantic"])) {
|
||||
if ([value isKindOfClass:[NSString class]]) {
|
||||
NSString *semanticName = value;
|
||||
UIColor *color = [UIColor colorNamed:semanticName];
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <React/RCTConvert.h>
|
||||
|
||||
@interface RCTColorSpaceUtils : NSObject
|
||||
|
||||
+ (void)applyDefaultColorSpace:(RCTColorSpace)colorSpace;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "RCTColorSpaceUtils.h"
|
||||
|
||||
#import <react/renderer/graphics/ColorComponents.h>
|
||||
|
||||
@implementation RCTColorSpaceUtils
|
||||
|
||||
+ (void)applyDefaultColorSpace:(RCTColorSpace)colorSpace
|
||||
{
|
||||
facebook::react::ColorSpace cxxColorSpace =
|
||||
colorSpace == RCTColorSpaceSRGB ? facebook::react::ColorSpace::sRGB : facebook::react::ColorSpace::DisplayP3;
|
||||
|
||||
RCTSetDefaultColorSpace(colorSpace);
|
||||
facebook::react::setDefaultColorSpace(cxxColorSpace);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "ColorComponents.h"
|
||||
#include <mutex>
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
static ColorSpace defaultColorSpace = ColorSpace::sRGB;
|
||||
|
||||
ColorSpace getDefaultColorSpace() {
|
||||
return defaultColorSpace;
|
||||
}
|
||||
|
||||
void setDefaultColorSpace(ColorSpace newColorSpace) {
|
||||
defaultColorSpace = newColorSpace;
|
||||
}
|
||||
|
||||
} // namespace facebook::react
|
||||
@@ -9,11 +9,17 @@
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
enum class ColorSpace { sRGB, DisplayP3 };
|
||||
|
||||
ColorSpace getDefaultColorSpace();
|
||||
void setDefaultColorSpace(ColorSpace newColorSpace);
|
||||
|
||||
struct ColorComponents {
|
||||
float red{0};
|
||||
float green{0};
|
||||
float blue{0};
|
||||
float alpha{0};
|
||||
ColorSpace colorSpace{getDefaultColorSpace()};
|
||||
};
|
||||
|
||||
} // namespace facebook::react
|
||||
|
||||
@@ -61,7 +61,8 @@ Pod::Spec.new do |s|
|
||||
|
||||
s.dependency "glog"
|
||||
s.dependency "RCT-Folly/Fabric", folly_version
|
||||
s.dependency "React-Core/Default", version
|
||||
s.dependency "React-jsi"
|
||||
s.dependency "React-jsiexecutor"
|
||||
s.dependency "React-utils"
|
||||
s.dependency "DoubleConversion"
|
||||
s.dependency "fmt", "9.1.0"
|
||||
|
||||
@@ -44,6 +44,24 @@ inline void fromRawValueShared(
|
||||
|
||||
result = colorFromComponents(colorComponents);
|
||||
} else {
|
||||
if (value.hasType<std::unordered_map<std::string, RawValue>>()) {
|
||||
const auto& items = (std::unordered_map<std::string, RawValue>)value;
|
||||
if (items.find("space") != items.end()) {
|
||||
colorComponents.red = (float)items.at("r");
|
||||
colorComponents.green = (float)items.at("g");
|
||||
colorComponents.blue = (float)items.at("b");
|
||||
colorComponents.alpha = (float)items.at("a");
|
||||
colorComponents.colorSpace = getDefaultColorSpace();
|
||||
std::string space = (std::string)items.at("space");
|
||||
if (space == "display-p3") {
|
||||
colorComponents.colorSpace = ColorSpace::DisplayP3;
|
||||
} else if (space == "srgb") {
|
||||
colorComponents.colorSpace = ColorSpace::sRGB;
|
||||
}
|
||||
result = colorFromComponents(colorComponents);
|
||||
return;
|
||||
}
|
||||
}
|
||||
result = parsePlatformColor(contextContainer, surfaceId, value);
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -87,6 +87,12 @@ int32_t ColorFromUIColor(const std::shared_ptr<void> &uiColor)
|
||||
|
||||
UIColor *_Nullable UIColorFromComponentsColor(const facebook::react::ColorComponents &components)
|
||||
{
|
||||
if (components.colorSpace == ColorSpace::DisplayP3) {
|
||||
return [UIColor colorWithDisplayP3Red:components.red
|
||||
green:components.green
|
||||
blue:components.blue
|
||||
alpha:components.alpha];
|
||||
}
|
||||
return [UIColor colorWithRed:components.red green:components.green blue:components.blue alpha:components.alpha];
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
Reference in New Issue
Block a user