Issue #2982 - Initial oklch test implementation

This commit is contained in:
Basilisk-Dev 2026-03-06 12:16:56 -05:00 committed by OwnedByWuigi
commit 45e476f332
3 changed files with 5743 additions and 1994 deletions

View file

@ -166,6 +166,44 @@ struct ReducePercentageCalcOps : ReduceNumberCalcOps
}
};
static constexpr float kOKLabPercentScaleAB = 0.4f;
static constexpr double kRadiansPerDegree = 0.01745329251994329576923690768489;
static inline float
LinearSRGBToEncoded(float aValue)
{
if (aValue <= 0.0031308f) {
return 12.92f * aValue;
}
return 1.055f * std::pow(aValue, 1.0f / 2.4f) - 0.055f;
}
static nscolor
OKLabToSRGBColor(float aL, float aA, float aB, float aAlpha)
{
float lRoot = aL + 0.3963377774f * aA + 0.2158037573f * aB;
float mRoot = aL - 0.1055613458f * aA - 0.0638541728f * aB;
float sRoot = aL - 0.0894841775f * aA - 1.2914855480f * aB;
float l = lRoot * lRoot * lRoot;
float m = mRoot * mRoot * mRoot;
float s = sRoot * sRoot * sRoot;
float linearR = 4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s;
float linearG = -1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s;
float linearB = -0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s;
float r = mozilla::clamped(LinearSRGBToEncoded(linearR), 0.0f, 1.0f);
float g = mozilla::clamped(LinearSRGBToEncoded(linearG), 0.0f, 1.0f);
float b = mozilla::clamped(LinearSRGBToEncoded(linearB), 0.0f, 1.0f);
return NS_RGBA(
NSToIntRound(r * 255.0f),
NSToIntRound(g * 255.0f),
NSToIntRound(b * 255.0f),
nsStyleUtil::FloatToColorComponent(mozilla::clamped(aAlpha, 0.0f, 1.0f)));
}
static_assert(css::eAuthorSheetFeatures == 0 &&
css::eUserSheetFeatures == 1 &&
css::eAgentSheetFeatures == 2,
@ -1233,6 +1271,10 @@ protected:
ComponentType& aA);
bool ParseHSLColor(float& aHue, float& aSaturation, float& aLightness,
float& aOpacity);
bool ParseOKLabColor(nscolor& aColor);
bool ParseOKLCHColor(nscolor& aColor);
bool ParseOKLabComponent(float& aComponent, float aPercentScale,
Maybe<char> aSeparator);
// The ParseColorOpacityAndCloseParen methods below attempt to parse an
// optional [ separator <alpha-value> ] expression, followed by a
@ -7608,6 +7650,22 @@ CSSParserImpl::ParseColor(nsCSSValue& aValue)
SkipUntil(')');
return CSSParseResult::Error;
}
else if (mToken.mIdent.LowerCaseEqualsLiteral("oklab")) {
if (ParseOKLabColor(rgba)) {
aValue.SetColorValue(rgba);
return CSSParseResult::Ok;
}
SkipUntil(')');
return CSSParseResult::Error;
}
else if (mToken.mIdent.LowerCaseEqualsLiteral("oklch")) {
if (ParseOKLCHColor(rgba)) {
aValue.SetColorValue(rgba);
return CSSParseResult::Ok;
}
SkipUntil(')');
return CSSParseResult::Error;
}
break;
}
default:
@ -7808,6 +7866,84 @@ CSSParserImpl::ParseHSLColor(float& aHue, float& aSaturation, float& aLightness,
return false;
}
bool
CSSParserImpl::ParseOKLabComponent(float& aComponent, float aPercentScale,
Maybe<char> aSeparator)
{
if (!GetToken(true)) {
REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
return false;
}
float value;
if (mToken.mType == eCSSToken_Number) {
value = mToken.mNumber;
} else if (mToken.mType == eCSSToken_Percentage) {
value = mToken.mNumber * aPercentScale;
} else {
REPORT_UNEXPECTED_TOKEN(PEExpectedNumberOrPercent);
UngetToken();
return false;
}
if (aSeparator && !ExpectSymbol(*aSeparator, true)) {
REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, *aSeparator);
return false;
}
aComponent = value;
return true;
}
bool
CSSParserImpl::ParseOKLabColor(nscolor& aColor)
{
const char commaSeparator = ',';
float l, a, b, alpha;
if (!ParseOKLabComponent(l, 1.0f, Nothing())) {
return false;
}
bool hasComma = ExpectSymbol(commaSeparator, true);
const char separatorBeforeAlpha = hasComma ? commaSeparator : '/';
if (!ParseOKLabComponent(a, kOKLabPercentScaleAB,
hasComma ? Some(commaSeparator) : Nothing()) ||
!ParseOKLabComponent(b, kOKLabPercentScaleAB, Nothing()) ||
!ParseColorOpacityAndCloseParen(alpha, separatorBeforeAlpha)) {
return false;
}
aColor = OKLabToSRGBColor(l, a, b, alpha);
return true;
}
bool
CSSParserImpl::ParseOKLCHColor(nscolor& aColor)
{
const char commaSeparator = ',';
float l, chroma, hue, alpha;
if (!ParseOKLabComponent(l, 1.0f, Nothing())) {
return false;
}
bool hasComma = ExpectSymbol(commaSeparator, true);
const char separatorBeforeAlpha = hasComma ? commaSeparator : '/';
if (!ParseOKLabComponent(chroma, kOKLabPercentScaleAB,
hasComma ? Some(commaSeparator) : Nothing()) ||
!ParseHue(hue) ||
!ParseColorOpacityAndCloseParen(alpha, separatorBeforeAlpha)) {
return false;
}
double hueRadians = hue * kRadiansPerDegree;
float a = chroma * std::cos(hueRadians);
float b = chroma * std::sin(hueRadians);
aColor = OKLabToSRGBColor(l, a, b, alpha);
return true;
}
bool
CSSParserImpl::ParseColorOpacityAndCloseParen(uint8_t& aOpacity,
@ -8756,6 +8892,8 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue,
tk->mIdent.LowerCaseEqualsLiteral("hsl") ||
tk->mIdent.LowerCaseEqualsLiteral("rgba") ||
tk->mIdent.LowerCaseEqualsLiteral("hsla") ||
tk->mIdent.LowerCaseEqualsLiteral("oklab") ||
tk->mIdent.LowerCaseEqualsLiteral("oklch") ||
tk->mIdent.LowerCaseEqualsLiteral("color-mix"))))
{
// Put token back so that parse color can get it