mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Support parsing hwb() functions (#48913)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/48913 This brings us to parity with normalize-color, and is mostly similar to hsl, with the notable exception there is not a separate alpha variant, and only modern function syntax is supported. Again, I took the math from normalize-color, and sanity tested it against reference function provided by Color Spec. I'm going to let that cap off color function support for now, and leave the rest as TODO. Changelog: [Internal] Reviewed By: lenaic Differential Revision: D68594471 fbshipit-source-id: 95702d576e068655d34e52a714d38e4fd718bbc9
This commit is contained in:
committed by
Facebook GitHub Bot
parent
676359efd9
commit
1593142648
@@ -104,6 +104,31 @@ hslToRgb(float h, float s, float l) {
|
||||
};
|
||||
}
|
||||
|
||||
inline std::tuple<uint8_t, uint8_t, uint8_t>
|
||||
hwbToRgb(float h, float w, float b) {
|
||||
w = std::clamp(w / 100.0f, 0.0f, 1.0f);
|
||||
b = std::clamp(b / 100.0f, 0.0f, 1.0f);
|
||||
|
||||
if (w + b >= 1.0f) {
|
||||
auto gray = w / (w + b);
|
||||
return {
|
||||
static_cast<uint8_t>(std::round(gray * 255.0f)),
|
||||
static_cast<uint8_t>(std::round(gray * 255.0f)),
|
||||
static_cast<uint8_t>(std::round(gray * 255.0f)),
|
||||
};
|
||||
}
|
||||
|
||||
auto red = hueToRgb(0.0f, 1.0f, h + 1.0f / 3.0f) * (1.0f - w - b) + w;
|
||||
auto green = hueToRgb(0.0f, 1.0f, h) * (1.0f - w - b) + w;
|
||||
auto blue = hueToRgb(0.0f, 1.0f, h - 1.0f / 3.0f) * (1.0f - w - b) + w;
|
||||
|
||||
return {
|
||||
static_cast<uint8_t>(std::round(red * 255.0f)),
|
||||
static_cast<uint8_t>(std::round(green * 255.0f)),
|
||||
static_cast<uint8_t>(std::round(blue * 255.0f)),
|
||||
};
|
||||
}
|
||||
|
||||
template <typename... ComponentT>
|
||||
requires(
|
||||
(std::is_same_v<CSSNumber, ComponentT> ||
|
||||
@@ -269,6 +294,7 @@ inline std::optional<CSSColor> parseLegacyHslFunction(CSSSyntaxParser& parser) {
|
||||
if (!l.has_value()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto a = normalizeComponent(
|
||||
parseNextCSSValue<CSSNumber, CSSPercentage>(parser, CSSDelimiter::Comma),
|
||||
1.0f);
|
||||
@@ -310,6 +336,7 @@ inline std::optional<CSSColor> parseModernHslFunction(CSSSyntaxParser& parser) {
|
||||
if (!l.has_value()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto a = normalizeComponent(
|
||||
parseNextCSSValue<CSSNumber, CSSPercentage>(
|
||||
parser, CSSDelimiter::SolidusOrWhitespace),
|
||||
@@ -337,6 +364,50 @@ inline std::optional<CSSColor> parseHslFunction(CSSSyntaxParser& parser) {
|
||||
return parseModernHslFunction<CSSColor>(parser);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an hwb() function and returns a CSSColor if it is valid.
|
||||
* https://www.w3.org/TR/css-color-4/#funcdef-hwb
|
||||
*/
|
||||
template <typename CSSColor>
|
||||
inline std::optional<CSSColor> parseHwbFunction(CSSSyntaxParser& parser) {
|
||||
auto h =
|
||||
normalizeHueComponent(parseNextCSSValue<CSSNumber, CSSAngle>(parser));
|
||||
if (!h.has_value()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto w = normalizeComponent(
|
||||
parseNextCSSValue<CSSNumber, CSSPercentage>(
|
||||
parser, CSSDelimiter::Whitespace),
|
||||
100.0f);
|
||||
if (!w.has_value()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto b = normalizeComponent(
|
||||
parseNextCSSValue<CSSNumber, CSSPercentage>(
|
||||
parser, CSSDelimiter::Whitespace),
|
||||
100.0f);
|
||||
if (!b.has_value()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto a = normalizeComponent(
|
||||
parseNextCSSValue<CSSNumber, CSSPercentage>(
|
||||
parser, CSSDelimiter::SolidusOrWhitespace),
|
||||
1.0f);
|
||||
|
||||
auto [red, green, blue] = hwbToRgb(*h, *w, *b);
|
||||
|
||||
return CSSColor{
|
||||
.r = red,
|
||||
.g = green,
|
||||
.b = blue,
|
||||
.a = clampAlpha(a),
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
@@ -360,20 +431,11 @@ constexpr std::optional<CSSColor> parseCSSColorFunction(
|
||||
return detail::parseHslFunction<CSSColor>(parser);
|
||||
break;
|
||||
case fnv1a("hwb"):
|
||||
case fnv1a("hwba"):
|
||||
// TODO
|
||||
break;
|
||||
case fnv1a("lab"):
|
||||
break;
|
||||
case fnv1a("lch"):
|
||||
break;
|
||||
case fnv1a("oklab"):
|
||||
break;
|
||||
case fnv1a("oklch"):
|
||||
break;
|
||||
case fnv1a("color"):
|
||||
// TODO T213000437: Support `color()` functions and wide-gamut colors.
|
||||
return detail::parseHwbFunction<CSSColor>(parser);
|
||||
break;
|
||||
|
||||
// TODO T213000437: support lab(), lch(), oklab(), oklch(), color(),
|
||||
// color-mix()
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -394,6 +394,92 @@ TEST(CSSColor, hsl_hsla_values) {
|
||||
EXPECT_TRUE(std::holds_alternative<std::monostate>(valueEndingWithComma));
|
||||
}
|
||||
|
||||
TEST(CSSColor, hwb_values) {
|
||||
auto simpleValue = parseCSSProperty<CSSColor>("hwb(208 14% 42%)");
|
||||
EXPECT_TRUE(std::holds_alternative<CSSColor>(simpleValue));
|
||||
EXPECT_EQ(std::get<CSSColor>(simpleValue).r, 36);
|
||||
EXPECT_EQ(std::get<CSSColor>(simpleValue).g, 96);
|
||||
EXPECT_EQ(std::get<CSSColor>(simpleValue).b, 148);
|
||||
EXPECT_EQ(std::get<CSSColor>(simpleValue).a, 255);
|
||||
|
||||
auto grayValue = parseCSSProperty<CSSColor>("hwb(208 100 100)");
|
||||
EXPECT_TRUE(std::holds_alternative<CSSColor>(grayValue));
|
||||
EXPECT_EQ(std::get<CSSColor>(grayValue).r, 128);
|
||||
EXPECT_EQ(std::get<CSSColor>(grayValue).g, 128);
|
||||
EXPECT_EQ(std::get<CSSColor>(grayValue).b, 128);
|
||||
EXPECT_EQ(std::get<CSSColor>(grayValue).a, 255);
|
||||
|
||||
auto angleValue = parseCSSProperty<CSSColor>("hwb(36.3028E-1rad 14% 42%)");
|
||||
EXPECT_TRUE(std::holds_alternative<CSSColor>(angleValue));
|
||||
EXPECT_EQ(std::get<CSSColor>(angleValue).r, 36);
|
||||
EXPECT_EQ(std::get<CSSColor>(angleValue).g, 96);
|
||||
EXPECT_EQ(std::get<CSSColor>(angleValue).b, 148);
|
||||
EXPECT_EQ(std::get<CSSColor>(angleValue).a, 255);
|
||||
|
||||
auto legacySyntaxValue = parseCSSProperty<CSSColor>("hwb(208, 14%, 42%)");
|
||||
EXPECT_TRUE(std::holds_alternative<std::monostate>(legacySyntaxValue));
|
||||
|
||||
auto alphaValue = parseCSSProperty<CSSColor>("hwb(208 14% 42% 0.5)");
|
||||
EXPECT_TRUE(std::holds_alternative<CSSColor>(alphaValue));
|
||||
EXPECT_EQ(std::get<CSSColor>(alphaValue).r, 36);
|
||||
EXPECT_EQ(std::get<CSSColor>(alphaValue).g, 96);
|
||||
EXPECT_EQ(std::get<CSSColor>(alphaValue).b, 148);
|
||||
EXPECT_EQ(std::get<CSSColor>(alphaValue).a, 128);
|
||||
|
||||
auto alphaPercentageValue =
|
||||
parseCSSProperty<CSSColor>("hwb(208 14% 42% 50%)");
|
||||
EXPECT_TRUE(std::holds_alternative<CSSColor>(alphaPercentageValue));
|
||||
EXPECT_EQ(std::get<CSSColor>(alphaPercentageValue).r, 36);
|
||||
EXPECT_EQ(std::get<CSSColor>(alphaPercentageValue).g, 96);
|
||||
EXPECT_EQ(std::get<CSSColor>(alphaPercentageValue).b, 148);
|
||||
EXPECT_EQ(std::get<CSSColor>(alphaPercentageValue).a, 128);
|
||||
|
||||
auto alphaSolidusValue = parseCSSProperty<CSSColor>("hwb(208 14% 42% / 0.5)");
|
||||
EXPECT_TRUE(std::holds_alternative<CSSColor>(alphaSolidusValue));
|
||||
EXPECT_EQ(std::get<CSSColor>(alphaSolidusValue).r, 36);
|
||||
EXPECT_EQ(std::get<CSSColor>(alphaSolidusValue).g, 96);
|
||||
EXPECT_EQ(std::get<CSSColor>(alphaSolidusValue).b, 148);
|
||||
EXPECT_EQ(std::get<CSSColor>(alphaSolidusValue).a, 128);
|
||||
|
||||
auto mixedWhitespaceValue =
|
||||
parseCSSProperty<CSSColor>(" hwb( 208 14% 42% /0.5 ) ");
|
||||
EXPECT_TRUE(std::holds_alternative<CSSColor>(mixedWhitespaceValue));
|
||||
EXPECT_EQ(std::get<CSSColor>(mixedWhitespaceValue).r, 36);
|
||||
EXPECT_EQ(std::get<CSSColor>(mixedWhitespaceValue).g, 96);
|
||||
EXPECT_EQ(std::get<CSSColor>(mixedWhitespaceValue).b, 148);
|
||||
EXPECT_EQ(std::get<CSSColor>(mixedWhitespaceValue).a, 128);
|
||||
|
||||
auto extraDegreesValue = parseCSSProperty<CSSColor>("hwb(568 14% 42% / 0.5)");
|
||||
EXPECT_TRUE(std::holds_alternative<CSSColor>(extraDegreesValue));
|
||||
EXPECT_EQ(std::get<CSSColor>(extraDegreesValue).r, 36);
|
||||
EXPECT_EQ(std::get<CSSColor>(extraDegreesValue).g, 96);
|
||||
EXPECT_EQ(std::get<CSSColor>(extraDegreesValue).b, 148);
|
||||
EXPECT_EQ(std::get<CSSColor>(extraDegreesValue).a, 128);
|
||||
|
||||
auto negativeDegreesValue =
|
||||
parseCSSProperty<CSSColor>("hwb(-152 14% 42% / 0.5)");
|
||||
EXPECT_TRUE(std::holds_alternative<CSSColor>(negativeDegreesValue));
|
||||
EXPECT_EQ(std::get<CSSColor>(negativeDegreesValue).r, 36);
|
||||
EXPECT_EQ(std::get<CSSColor>(negativeDegreesValue).g, 96);
|
||||
EXPECT_EQ(std::get<CSSColor>(negativeDegreesValue).b, 148);
|
||||
EXPECT_EQ(std::get<CSSColor>(negativeDegreesValue).a, 128);
|
||||
|
||||
auto missingComponentsValue = parseCSSProperty<CSSColor>("hwb(208 14%)");
|
||||
EXPECT_TRUE(std::holds_alternative<std::monostate>(missingComponentsValue));
|
||||
|
||||
auto tooManyComponentsValue =
|
||||
parseCSSProperty<CSSColor>("hwb(208 14% 42% 0.5 0.5)");
|
||||
EXPECT_TRUE(std::holds_alternative<std::monostate>(tooManyComponentsValue));
|
||||
|
||||
auto valueStartingWithComma =
|
||||
parseCSSProperty<CSSColor>("hwb(,208 14% 42% / 0.5)");
|
||||
EXPECT_TRUE(std::holds_alternative<std::monostate>(valueStartingWithComma));
|
||||
|
||||
auto valueEndingWithComma =
|
||||
parseCSSProperty<CSSColor>("hwb(208 14% 42% / 0.5,)");
|
||||
EXPECT_TRUE(std::holds_alternative<std::monostate>(valueEndingWithComma));
|
||||
}
|
||||
|
||||
TEST(CSSColor, constexpr_values) {
|
||||
[[maybe_unused]] constexpr auto emptyValue = parseCSSProperty<CSSColor>("");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user