mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-25 09:57:32 +09:00
Issue #2982 - Follow-up: allow color-mix to work with oklab and oklch
Tag #2489 and #3003
This commit is contained in:
parent
e602080115
commit
7e0818c9ec
7 changed files with 460 additions and 59 deletions
|
|
@ -6,6 +6,7 @@
|
||||||
#ifndef nsCSSNonSRGBColorSpace_h___
|
#ifndef nsCSSNonSRGBColorSpace_h___
|
||||||
#define nsCSSNonSRGBColorSpace_h___
|
#define nsCSSNonSRGBColorSpace_h___
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include "mozilla/MathAlgorithms.h"
|
#include "mozilla/MathAlgorithms.h"
|
||||||
|
|
@ -18,7 +19,46 @@ namespace css {
|
||||||
static constexpr float kLabLightnessMax = 100.0f;
|
static constexpr float kLabLightnessMax = 100.0f;
|
||||||
static constexpr float kLchPercentScaleC = 150.0f;
|
static constexpr float kLchPercentScaleC = 150.0f;
|
||||||
static constexpr float kOklabPercentScaleAB = 0.4f;
|
static constexpr float kOklabPercentScaleAB = 0.4f;
|
||||||
|
static constexpr float kOklchPowerlessChromaEpsilon = 0.000004f;
|
||||||
static constexpr double kRadiansPerDegree = 0.01745329251994329576923690768489;
|
static constexpr double kRadiansPerDegree = 0.01745329251994329576923690768489;
|
||||||
|
static constexpr double kDegreesPerRadian = 57.295779513082320876798154814105;
|
||||||
|
|
||||||
|
struct OklabColor {
|
||||||
|
float mL;
|
||||||
|
float mA;
|
||||||
|
float mB;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OklchColor {
|
||||||
|
float mL;
|
||||||
|
float mChroma;
|
||||||
|
float mHue;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LinearSRGBColor {
|
||||||
|
float mR;
|
||||||
|
float mG;
|
||||||
|
float mB;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline float
|
||||||
|
NormalizeHue(float aHue)
|
||||||
|
{
|
||||||
|
float hue = std::fmod(aHue, 360.0f);
|
||||||
|
if (hue < 0.0f) {
|
||||||
|
hue += 360.0f;
|
||||||
|
}
|
||||||
|
return hue;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float
|
||||||
|
EncodedSRGBToLinear(float aValue)
|
||||||
|
{
|
||||||
|
if (aValue <= 0.04045f) {
|
||||||
|
return aValue / 12.92f;
|
||||||
|
}
|
||||||
|
return std::pow((aValue + 0.055f) / 1.055f, 2.4f);
|
||||||
|
}
|
||||||
|
|
||||||
inline float
|
inline float
|
||||||
LinearSRGBToEncoded(float aValue)
|
LinearSRGBToEncoded(float aValue)
|
||||||
|
|
@ -44,43 +84,159 @@ LinearSRGBToColor(float aLinearR, float aLinearG, float aLinearB,
|
||||||
aAlpha);
|
aAlpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline nscolor
|
inline LinearSRGBColor
|
||||||
OklabToSRGBColor(float aL, float aA, float aB, float aAlpha)
|
OklabToLinearSRGB(float aL, float aA, float aB)
|
||||||
{
|
{
|
||||||
// Per CSS Color, the lightness component for Oklab/Oklch is clamped.
|
float lRoot = aL + 0.3963377774f * aA + 0.2158037573f * aB;
|
||||||
float lightness = mozilla::clamped(aL, 0.0f, 1.0f);
|
float mRoot = aL - 0.1055613458f * aA - 0.0638541728f * aB;
|
||||||
uint8_t alpha =
|
float sRoot = aL - 0.0894841775f * aA - 1.2914855480f * aB;
|
||||||
nsStyleUtil::FloatToColorComponent(mozilla::clamped(aAlpha, 0.0f, 1.0f));
|
|
||||||
|
|
||||||
// Treat values extremely close to zero as zero to avoid tiny floating-point
|
|
||||||
// representation differences for percentage inputs.
|
|
||||||
static constexpr float kLightnessEndpointEpsilon = 0.000002f;
|
|
||||||
|
|
||||||
if (lightness <= kLightnessEndpointEpsilon) {
|
|
||||||
return NS_RGBA(0, 0, 0, alpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
float lRoot = lightness + 0.3963377774f * aA + 0.2158037573f * aB;
|
|
||||||
float mRoot = lightness - 0.1055613458f * aA - 0.0638541728f * aB;
|
|
||||||
float sRoot = lightness - 0.0894841775f * aA - 1.2914855480f * aB;
|
|
||||||
|
|
||||||
float l = lRoot * lRoot * lRoot;
|
float l = lRoot * lRoot * lRoot;
|
||||||
float m = mRoot * mRoot * mRoot;
|
float m = mRoot * mRoot * mRoot;
|
||||||
float s = sRoot * sRoot * sRoot;
|
float s = sRoot * sRoot * sRoot;
|
||||||
|
|
||||||
float linearR = 4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s;
|
return {
|
||||||
float linearG = -1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s;
|
4.0767416361f * l - 3.3077115393f * m + 0.2309699032f * s,
|
||||||
float linearB = -0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s;
|
-1.2684379733f * l + 2.6097573493f * m - 0.3413193760f * s,
|
||||||
|
-0.0041960761f * l - 0.7034186179f * m + 1.7076146941f * s
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return LinearSRGBToColor(linearR, linearG, linearB, alpha);
|
inline OklabColor
|
||||||
|
LinearSRGBToOklabColor(float aLinearR, float aLinearG, float aLinearB)
|
||||||
|
{
|
||||||
|
float l = std::cbrt(0.4122214695f * aLinearR +
|
||||||
|
0.5363325373f * aLinearG +
|
||||||
|
0.0514459933f * aLinearB);
|
||||||
|
float m = std::cbrt(0.2119034958f * aLinearR +
|
||||||
|
0.6806995506f * aLinearG +
|
||||||
|
0.1073969535f * aLinearB);
|
||||||
|
float s = std::cbrt(0.0883024592f * aLinearR +
|
||||||
|
0.2817188391f * aLinearG +
|
||||||
|
0.6299787017f * aLinearB);
|
||||||
|
|
||||||
|
return {
|
||||||
|
0.2104542683f * l + 0.7936177747f * m - 0.0040720430f * s,
|
||||||
|
1.9779985324f * l - 2.4285922420f * m + 0.4505937096f * s,
|
||||||
|
0.0259040425f * l + 0.7827717125f * m - 0.8086757549f * s
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline OklabColor
|
||||||
|
SRGBToOklabColor(nscolor aColor)
|
||||||
|
{
|
||||||
|
float linearR = EncodedSRGBToLinear(NS_GET_R(aColor) / 255.0f);
|
||||||
|
float linearG = EncodedSRGBToLinear(NS_GET_G(aColor) / 255.0f);
|
||||||
|
float linearB = EncodedSRGBToLinear(NS_GET_B(aColor) / 255.0f);
|
||||||
|
|
||||||
|
return LinearSRGBToOklabColor(linearR, linearG, linearB);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline OklchColor
|
||||||
|
OklabToOklchColor(const OklabColor& aColor)
|
||||||
|
{
|
||||||
|
float chroma = std::sqrt(aColor.mA * aColor.mA + aColor.mB * aColor.mB);
|
||||||
|
float hue = std::atan2(aColor.mB, aColor.mA) * kDegreesPerRadian;
|
||||||
|
if (hue < 0.0f) {
|
||||||
|
hue += 360.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { aColor.mL, chroma, hue };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline nscolor
|
||||||
|
OklabToSRGBColor(float aL, float aA, float aB, float aAlpha)
|
||||||
|
{
|
||||||
|
// Per CSS Color, the lightness component for Oklab/Oklch is clamped at
|
||||||
|
// parsed-value time.
|
||||||
|
float lightness = mozilla::clamped(aL, 0.0f, 1.0f);
|
||||||
|
uint8_t alpha =
|
||||||
|
nsStyleUtil::FloatToColorComponent(mozilla::clamped(aAlpha, 0.0f, 1.0f));
|
||||||
|
|
||||||
|
OklabColor mapped = { lightness, aA, aB };
|
||||||
|
auto isInSRGBGamut = [](const OklabColor& aColor) {
|
||||||
|
LinearSRGBColor linear =
|
||||||
|
OklabToLinearSRGB(aColor.mL, aColor.mA, aColor.mB);
|
||||||
|
return linear.mR >= 0.0f && linear.mR <= 1.0f &&
|
||||||
|
linear.mG >= 0.0f && linear.mG <= 1.0f &&
|
||||||
|
linear.mB >= 0.0f && linear.mB <= 1.0f;
|
||||||
|
};
|
||||||
|
auto clippedOklab = [](const OklabColor& aColor) {
|
||||||
|
LinearSRGBColor linear =
|
||||||
|
OklabToLinearSRGB(aColor.mL, aColor.mA, aColor.mB);
|
||||||
|
return LinearSRGBToOklabColor(
|
||||||
|
mozilla::clamped(linear.mR, 0.0f, 1.0f),
|
||||||
|
mozilla::clamped(linear.mG, 0.0f, 1.0f),
|
||||||
|
mozilla::clamped(linear.mB, 0.0f, 1.0f));
|
||||||
|
};
|
||||||
|
auto deltaEOK = [](const OklabColor& aColor1, const OklabColor& aColor2) {
|
||||||
|
float deltaL = aColor1.mL - aColor2.mL;
|
||||||
|
float deltaA = aColor1.mA - aColor2.mA;
|
||||||
|
float deltaB = aColor1.mB - aColor2.mB;
|
||||||
|
return std::sqrt(deltaL * deltaL + deltaA * deltaA + deltaB * deltaB);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (lightness <= 0.0f) {
|
||||||
|
mapped = { 0.0f, 0.0f, 0.0f };
|
||||||
|
} else if (lightness >= 1.0f) {
|
||||||
|
mapped = { 1.0f, 0.0f, 0.0f };
|
||||||
|
} else if (!isInSRGBGamut(mapped)) {
|
||||||
|
// CSS Color 4 binary search gamut mapping with local MINDE, targeting sRGB.
|
||||||
|
static constexpr float kJND = 0.02f;
|
||||||
|
static constexpr float kGamutMapEpsilon = 0.0001f;
|
||||||
|
|
||||||
|
OklchColor origin = OklabToOklchColor(mapped);
|
||||||
|
OklabColor clipped = clippedOklab(mapped);
|
||||||
|
float delta = deltaEOK(clipped, mapped);
|
||||||
|
|
||||||
|
if (delta < kJND) {
|
||||||
|
mapped = clipped;
|
||||||
|
} else {
|
||||||
|
float min = 0.0f;
|
||||||
|
float max = origin.mChroma;
|
||||||
|
bool minInGamut = true;
|
||||||
|
OklabColor current = mapped;
|
||||||
|
|
||||||
|
while (max - min > kGamutMapEpsilon) {
|
||||||
|
float chroma = (min + max) / 2.0f;
|
||||||
|
current.mL = origin.mL;
|
||||||
|
current.mA = chroma * std::cos(origin.mHue * kRadiansPerDegree);
|
||||||
|
current.mB = chroma * std::sin(origin.mHue * kRadiansPerDegree);
|
||||||
|
|
||||||
|
if (minInGamut && isInSRGBGamut(current)) {
|
||||||
|
min = chroma;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
clipped = clippedOklab(current);
|
||||||
|
delta = deltaEOK(clipped, current);
|
||||||
|
if (delta < kJND) {
|
||||||
|
if (kJND - delta < kGamutMapEpsilon) {
|
||||||
|
mapped = clipped;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
minInGamut = false;
|
||||||
|
min = chroma;
|
||||||
|
} else {
|
||||||
|
max = chroma;
|
||||||
|
}
|
||||||
|
mapped = clipped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LinearSRGBColor linear =
|
||||||
|
OklabToLinearSRGB(mapped.mL, mapped.mA, mapped.mB);
|
||||||
|
return LinearSRGBToColor(linear.mR, linear.mG, linear.mB, alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline nscolor
|
inline nscolor
|
||||||
OklchToSRGBColor(float aL, float aChroma, float aHue, float aAlpha)
|
OklchToSRGBColor(float aL, float aChroma, float aHue, float aAlpha)
|
||||||
{
|
{
|
||||||
double hueRadians = aHue * kRadiansPerDegree;
|
float chroma = std::max(aChroma, 0.0f);
|
||||||
float a = aChroma * std::cos(hueRadians);
|
double hueRadians = NormalizeHue(aHue) * kRadiansPerDegree;
|
||||||
float b = aChroma * std::sin(hueRadians);
|
float a = chroma * std::cos(hueRadians);
|
||||||
|
float b = chroma * std::sin(hueRadians);
|
||||||
return OklabToSRGBColor(aL, a, b, aAlpha);
|
return OklabToSRGBColor(aL, a, b, aAlpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1238,8 +1238,9 @@ protected:
|
||||||
bool ParseHSLColor(float& aHue, float& aSaturation, float& aLightness,
|
bool ParseHSLColor(float& aHue, float& aSaturation, float& aLightness,
|
||||||
float& aOpacity);
|
float& aOpacity);
|
||||||
bool ParseLCHColor(nscolor& aColor);
|
bool ParseLCHColor(nscolor& aColor);
|
||||||
bool ParseOKLabColor(nscolor& aColor);
|
bool ParseOKLabColor(float& aL, float& aA, float& aB, float& aAlpha);
|
||||||
bool ParseOKLCHColor(nscolor& aColor);
|
bool ParseOKLCHColor(float& aL, float& aChroma, float& aHue,
|
||||||
|
float& aAlpha);
|
||||||
bool ParseOKLabComponent(float& aComponent, float aPercentScale,
|
bool ParseOKLabComponent(float& aComponent, float aPercentScale,
|
||||||
Maybe<char> aSeparator);
|
Maybe<char> aSeparator);
|
||||||
|
|
||||||
|
|
@ -7474,7 +7475,7 @@ CSSParserImpl::ParseColor(nsCSSValue& aValue)
|
||||||
return CSSParseResult::Error;
|
return CSSParseResult::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for supported color spaces: srgb or hsl
|
// Check for supported color spaces.
|
||||||
if (!GetToken(true) || mToken.mType != eCSSToken_Ident) {
|
if (!GetToken(true) || mToken.mType != eCSSToken_Ident) {
|
||||||
SkipUntil(')');
|
SkipUntil(')');
|
||||||
return CSSParseResult::Error;
|
return CSSParseResult::Error;
|
||||||
|
|
@ -7485,6 +7486,10 @@ CSSParserImpl::ParseColor(nsCSSValue& aValue)
|
||||||
colorSpace = mozilla::css::ColorMixColorSpace::sRGB;
|
colorSpace = mozilla::css::ColorMixColorSpace::sRGB;
|
||||||
} else if (mToken.mIdent.LowerCaseEqualsLiteral("hsl")) {
|
} else if (mToken.mIdent.LowerCaseEqualsLiteral("hsl")) {
|
||||||
colorSpace = mozilla::css::ColorMixColorSpace::HSL;
|
colorSpace = mozilla::css::ColorMixColorSpace::HSL;
|
||||||
|
} else if (mToken.mIdent.LowerCaseEqualsLiteral("oklab")) {
|
||||||
|
colorSpace = mozilla::css::ColorMixColorSpace::Oklab;
|
||||||
|
} else if (mToken.mIdent.LowerCaseEqualsLiteral("oklch")) {
|
||||||
|
colorSpace = mozilla::css::ColorMixColorSpace::Oklch;
|
||||||
} else {
|
} else {
|
||||||
SkipUntil(')');
|
SkipUntil(')');
|
||||||
return CSSParseResult::Error;
|
return CSSParseResult::Error;
|
||||||
|
|
@ -7633,16 +7638,19 @@ CSSParserImpl::ParseColor(nsCSSValue& aValue)
|
||||||
return CSSParseResult::Error;
|
return CSSParseResult::Error;
|
||||||
}
|
}
|
||||||
else if (mToken.mIdent.LowerCaseEqualsLiteral("oklab")) {
|
else if (mToken.mIdent.LowerCaseEqualsLiteral("oklab")) {
|
||||||
if (ParseOKLabColor(rgba)) {
|
float l, a, b, alpha;
|
||||||
aValue.SetColorValue(rgba);
|
if (ParseOKLabColor(l, a, b, alpha)) {
|
||||||
|
aValue.SetFloatColorValue(l, a, b, alpha, eCSSUnit_OklabColor);
|
||||||
return CSSParseResult::Ok;
|
return CSSParseResult::Ok;
|
||||||
}
|
}
|
||||||
SkipUntil(')');
|
SkipUntil(')');
|
||||||
return CSSParseResult::Error;
|
return CSSParseResult::Error;
|
||||||
}
|
}
|
||||||
else if (mToken.mIdent.LowerCaseEqualsLiteral("oklch")) {
|
else if (mToken.mIdent.LowerCaseEqualsLiteral("oklch")) {
|
||||||
if (ParseOKLCHColor(rgba)) {
|
float l, chroma, hue, alpha;
|
||||||
aValue.SetColorValue(rgba);
|
if (ParseOKLCHColor(l, chroma, hue, alpha)) {
|
||||||
|
aValue.SetFloatColorValue(l, chroma, hue, alpha,
|
||||||
|
eCSSUnit_OklchColor);
|
||||||
return CSSParseResult::Ok;
|
return CSSParseResult::Ok;
|
||||||
}
|
}
|
||||||
SkipUntil(')');
|
SkipUntil(')');
|
||||||
|
|
@ -7894,7 +7902,8 @@ CSSParserImpl::ParseLCHColor(nscolor& aColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
CSSParserImpl::ParseOKLabColor(nscolor& aColor)
|
CSSParserImpl::ParseOKLabColor(float& aL, float& aA, float& aB,
|
||||||
|
float& aAlpha)
|
||||||
{
|
{
|
||||||
const char commaSeparator = ',';
|
const char commaSeparator = ',';
|
||||||
float l, a, b, alpha;
|
float l, a, b, alpha;
|
||||||
|
|
@ -7912,12 +7921,16 @@ CSSParserImpl::ParseOKLabColor(nscolor& aColor)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
aColor = OklabToSRGBColor(l, a, b, alpha);
|
aL = mozilla::clamped(l, 0.0f, 1.0f);
|
||||||
|
aA = a;
|
||||||
|
aB = b;
|
||||||
|
aAlpha = alpha;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
CSSParserImpl::ParseOKLCHColor(nscolor& aColor)
|
CSSParserImpl::ParseOKLCHColor(float& aL, float& aChroma, float& aHue,
|
||||||
|
float& aAlpha)
|
||||||
{
|
{
|
||||||
const char commaSeparator = ',';
|
const char commaSeparator = ',';
|
||||||
float l, chroma, hue, alpha;
|
float l, chroma, hue, alpha;
|
||||||
|
|
@ -7935,7 +7948,10 @@ CSSParserImpl::ParseOKLCHColor(nscolor& aColor)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
aColor = OklchToSRGBColor(l, chroma, hue, alpha);
|
aL = mozilla::clamped(l, 0.0f, 1.0f);
|
||||||
|
aChroma = std::max(chroma, 0.0f);
|
||||||
|
aHue = NormalizeHue(hue);
|
||||||
|
aAlpha = alpha;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
#include "gfxFontConstants.h"
|
#include "gfxFontConstants.h"
|
||||||
#include "imgIRequest.h"
|
#include "imgIRequest.h"
|
||||||
#include "imgRequestProxy.h"
|
#include "imgRequestProxy.h"
|
||||||
|
#include "nsCSSNonSRGBColorSpace.h"
|
||||||
#include "nsIDocument.h"
|
#include "nsIDocument.h"
|
||||||
#include "nsNetUtil.h"
|
#include "nsNetUtil.h"
|
||||||
#include "nsPresContext.h"
|
#include "nsPresContext.h"
|
||||||
|
|
@ -1743,6 +1744,12 @@ nsCSSValue::AppendToString(nsCSSPropertyID aProperty, nsAString& aResult,
|
||||||
case mozilla::css::ColorMixColorSpace::HSL:
|
case mozilla::css::ColorMixColorSpace::HSL:
|
||||||
aResult.AppendLiteral("hsl");
|
aResult.AppendLiteral("hsl");
|
||||||
break;
|
break;
|
||||||
|
case mozilla::css::ColorMixColorSpace::Oklab:
|
||||||
|
aResult.AppendLiteral("oklab");
|
||||||
|
break;
|
||||||
|
case mozilla::css::ColorMixColorSpace::Oklch:
|
||||||
|
aResult.AppendLiteral("oklch");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// append color1 and color2
|
// append color1 and color2
|
||||||
|
|
@ -2050,6 +2057,8 @@ nsCSSValue::AppendToString(nsCSSPropertyID aProperty, nsAString& aResult,
|
||||||
case eCSSUnit_PercentageRGBAColor: break;
|
case eCSSUnit_PercentageRGBAColor: break;
|
||||||
case eCSSUnit_HSLColor: break;
|
case eCSSUnit_HSLColor: break;
|
||||||
case eCSSUnit_HSLAColor: break;
|
case eCSSUnit_HSLAColor: break;
|
||||||
|
case eCSSUnit_OklabColor: break;
|
||||||
|
case eCSSUnit_OklchColor: break;
|
||||||
case eCSSUnit_ComplexColor: break;
|
case eCSSUnit_ComplexColor: break;
|
||||||
case eCSSUnit_ColorMix: break;
|
case eCSSUnit_ColorMix: break;
|
||||||
case eCSSUnit_Percent: aResult.Append(char16_t('%')); break;
|
case eCSSUnit_Percent: aResult.Append(char16_t('%')); break;
|
||||||
|
|
@ -2258,6 +2267,8 @@ nsCSSValue::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
|
||||||
case eCSSUnit_PercentageRGBAColor:
|
case eCSSUnit_PercentageRGBAColor:
|
||||||
case eCSSUnit_HSLColor:
|
case eCSSUnit_HSLColor:
|
||||||
case eCSSUnit_HSLAColor:
|
case eCSSUnit_HSLAColor:
|
||||||
|
case eCSSUnit_OklabColor:
|
||||||
|
case eCSSUnit_OklchColor:
|
||||||
n += mValue.mFloatColor->SizeOfIncludingThis(aMallocSizeOf);
|
n += mValue.mFloatColor->SizeOfIncludingThis(aMallocSizeOf);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -3254,6 +3265,16 @@ nsCSSValueFloatColor::GetColorValue(nsCSSUnit aUnit) const
|
||||||
NSToIntRound(mozilla::clamped(mAlpha, 0.0f, 1.0f) * 255.0f));
|
NSToIntRound(mozilla::clamped(mAlpha, 0.0f, 1.0f) * 255.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (aUnit == eCSSUnit_OklabColor) {
|
||||||
|
return css::OklabToSRGBColor(mComponent1, mComponent2, mComponent3,
|
||||||
|
mAlpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aUnit == eCSSUnit_OklchColor) {
|
||||||
|
return css::OklchToSRGBColor(mComponent1, mComponent2, mComponent3,
|
||||||
|
mAlpha);
|
||||||
|
}
|
||||||
|
|
||||||
// HSL color
|
// HSL color
|
||||||
MOZ_ASSERT(aUnit == eCSSUnit_HSLColor ||
|
MOZ_ASSERT(aUnit == eCSSUnit_HSLColor ||
|
||||||
aUnit == eCSSUnit_HSLAColor);
|
aUnit == eCSSUnit_HSLAColor);
|
||||||
|
|
@ -3282,8 +3303,14 @@ nsCSSValueFloatColor::AppendToString(nsCSSUnit aUnit, nsAString& aResult) const
|
||||||
bool showAlpha = (mAlpha != 1.0f);
|
bool showAlpha = (mAlpha != 1.0f);
|
||||||
bool isHSL = (aUnit == eCSSUnit_HSLColor ||
|
bool isHSL = (aUnit == eCSSUnit_HSLColor ||
|
||||||
aUnit == eCSSUnit_HSLAColor);
|
aUnit == eCSSUnit_HSLAColor);
|
||||||
|
bool isOklab = aUnit == eCSSUnit_OklabColor;
|
||||||
|
bool isOklch = aUnit == eCSSUnit_OklchColor;
|
||||||
|
|
||||||
if (isHSL) {
|
if (isOklab) {
|
||||||
|
aResult.AppendLiteral("oklab");
|
||||||
|
} else if (isOklch) {
|
||||||
|
aResult.AppendLiteral("oklch");
|
||||||
|
} else if (isHSL) {
|
||||||
aResult.AppendLiteral("hsl");
|
aResult.AppendLiteral("hsl");
|
||||||
} else {
|
} else {
|
||||||
aResult.AppendLiteral("rgb");
|
aResult.AppendLiteral("rgb");
|
||||||
|
|
@ -3293,23 +3320,39 @@ nsCSSValueFloatColor::AppendToString(nsCSSUnit aUnit, nsAString& aResult) const
|
||||||
} else {
|
} else {
|
||||||
aResult.Append('(');
|
aResult.Append('(');
|
||||||
}
|
}
|
||||||
if (isHSL) {
|
if (isOklab || isOklch) {
|
||||||
|
aResult.AppendFloat(mComponent1);
|
||||||
|
aResult.Append(' ');
|
||||||
|
aResult.AppendFloat(mComponent2);
|
||||||
|
aResult.Append(' ');
|
||||||
|
aResult.AppendFloat(mComponent3);
|
||||||
|
} else if (isHSL) {
|
||||||
aResult.AppendFloat(mComponent1 * 360.0f);
|
aResult.AppendFloat(mComponent1 * 360.0f);
|
||||||
aResult.AppendLiteral(", ");
|
aResult.AppendLiteral(", ");
|
||||||
} else {
|
} else {
|
||||||
aResult.AppendFloat(mComponent1 * 100.0f);
|
aResult.AppendFloat(mComponent1 * 100.0f);
|
||||||
aResult.AppendLiteral("%, ");
|
aResult.AppendLiteral("%, ");
|
||||||
}
|
}
|
||||||
|
if (!isOklab && !isOklch) {
|
||||||
aResult.AppendFloat(mComponent2 * 100.0f);
|
aResult.AppendFloat(mComponent2 * 100.0f);
|
||||||
aResult.AppendLiteral("%, ");
|
aResult.AppendLiteral("%, ");
|
||||||
aResult.AppendFloat(mComponent3 * 100.0f);
|
aResult.AppendFloat(mComponent3 * 100.0f);
|
||||||
|
}
|
||||||
if (showAlpha) {
|
if (showAlpha) {
|
||||||
|
if (isOklab || isOklch) {
|
||||||
|
aResult.AppendLiteral(" / ");
|
||||||
|
} else {
|
||||||
aResult.AppendLiteral("%, ");
|
aResult.AppendLiteral("%, ");
|
||||||
|
}
|
||||||
aResult.AppendFloat(mAlpha);
|
aResult.AppendFloat(mAlpha);
|
||||||
aResult.Append(')');
|
aResult.Append(')');
|
||||||
|
} else {
|
||||||
|
if (isOklab || isOklch) {
|
||||||
|
aResult.Append(')');
|
||||||
} else {
|
} else {
|
||||||
aResult.AppendLiteral("%)");
|
aResult.AppendLiteral("%)");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
|
|
|
||||||
|
|
@ -525,8 +525,10 @@ enum nsCSSUnit {
|
||||||
// allowed.
|
// allowed.
|
||||||
eCSSUnit_HSLColor = 89, // (nsCSSValueFloatColor*)
|
eCSSUnit_HSLColor = 89, // (nsCSSValueFloatColor*)
|
||||||
eCSSUnit_HSLAColor = 90, // (nsCSSValueFloatColor*)
|
eCSSUnit_HSLAColor = 90, // (nsCSSValueFloatColor*)
|
||||||
eCSSUnit_ComplexColor = 91, // (ComplexColorValue*)
|
eCSSUnit_OklabColor = 91, // (nsCSSValueFloatColor*)
|
||||||
eCSSUnit_ColorMix = 92, // (ColorMixValue*)
|
eCSSUnit_OklchColor = 92, // (nsCSSValueFloatColor*)
|
||||||
|
eCSSUnit_ComplexColor = 93, // (ComplexColorValue*)
|
||||||
|
eCSSUnit_ColorMix = 94, // (ColorMixValue*)
|
||||||
|
|
||||||
eCSSUnit_Percent = 100, // (float) 1.0 == 100%) value is percentage of something
|
eCSSUnit_Percent = 100, // (float) 1.0 == 100%) value is percentage of something
|
||||||
eCSSUnit_Number = 101, // (float) value is numeric (usually multiplier, different behavior than percent)
|
eCSSUnit_Number = 101, // (float) value is numeric (usually multiplier, different behavior than percent)
|
||||||
|
|
@ -738,6 +740,8 @@ public:
|
||||||
// eCSSUnit_PercentageRGBAColor -- rgba(%,%,%,float)
|
// eCSSUnit_PercentageRGBAColor -- rgba(%,%,%,float)
|
||||||
// eCSSUnit_HSLColor -- hsl(float,%,%)
|
// eCSSUnit_HSLColor -- hsl(float,%,%)
|
||||||
// eCSSUnit_HSLAColor -- hsla(float,%,%,float)
|
// eCSSUnit_HSLAColor -- hsla(float,%,%,float)
|
||||||
|
// eCSSUnit_OklabColor -- oklab(float,float,float,float)
|
||||||
|
// eCSSUnit_OklchColor -- oklch(float,float,float,float)
|
||||||
//
|
//
|
||||||
// - IsNumericColorUnit returns true for any of the above units.
|
// - IsNumericColorUnit returns true for any of the above units.
|
||||||
//
|
//
|
||||||
|
|
@ -750,7 +754,7 @@ public:
|
||||||
{ return eCSSUnit_RGBColor <= aUnit && aUnit <= eCSSUnit_ShortHexColorAlpha; }
|
{ return eCSSUnit_RGBColor <= aUnit && aUnit <= eCSSUnit_ShortHexColorAlpha; }
|
||||||
static bool IsFloatColorUnit(nsCSSUnit aUnit)
|
static bool IsFloatColorUnit(nsCSSUnit aUnit)
|
||||||
{ return eCSSUnit_PercentageRGBColor <= aUnit &&
|
{ return eCSSUnit_PercentageRGBColor <= aUnit &&
|
||||||
aUnit <= eCSSUnit_HSLAColor; }
|
aUnit <= eCSSUnit_OklchColor; }
|
||||||
static bool IsNumericColorUnit(nsCSSUnit aUnit)
|
static bool IsNumericColorUnit(nsCSSUnit aUnit)
|
||||||
{ return IsIntegerColorUnit(aUnit) || IsFloatColorUnit(aUnit); }
|
{ return IsIntegerColorUnit(aUnit) || IsFloatColorUnit(aUnit); }
|
||||||
|
|
||||||
|
|
@ -1933,6 +1937,9 @@ private:
|
||||||
// [0, 1] for hue represents
|
// [0, 1] for hue represents
|
||||||
// [0deg, 360deg].
|
// [0deg, 360deg].
|
||||||
//
|
//
|
||||||
|
// OklabColor stores L in mComponent1, a in mComponent2, b in mComponent3.
|
||||||
|
// OklchColor stores L in mComponent1, C in mComponent2, H in mComponent3.
|
||||||
|
//
|
||||||
// [-float::max(), float::max()] for PercentageRGBColor, PercentageRGBAColor.
|
// [-float::max(), float::max()] for PercentageRGBColor, PercentageRGBAColor.
|
||||||
// 1.0 means 100%.
|
// 1.0 means 100%.
|
||||||
float mComponent1;
|
float mComponent1;
|
||||||
|
|
@ -1999,7 +2006,9 @@ namespace css {
|
||||||
|
|
||||||
enum class ColorMixColorSpace {
|
enum class ColorMixColorSpace {
|
||||||
sRGB,
|
sRGB,
|
||||||
HSL
|
HSL,
|
||||||
|
Oklab,
|
||||||
|
Oklch
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ColorMixValue final
|
struct ColorMixValue final
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@
|
||||||
#include "nsIStyleRule.h"
|
#include "nsIStyleRule.h"
|
||||||
#include "nsBidiUtils.h"
|
#include "nsBidiUtils.h"
|
||||||
#include "nsStyleStructInlines.h"
|
#include "nsStyleStructInlines.h"
|
||||||
|
#include "nsCSSNonSRGBColorSpace.h"
|
||||||
#include "nsCSSProps.h"
|
#include "nsCSSProps.h"
|
||||||
#include "nsTArray.h"
|
#include "nsTArray.h"
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
|
|
@ -1151,9 +1152,144 @@ SetPairCoords(const nsCSSValue& aValue,
|
||||||
return cX;
|
return cX;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool SetColor(const nsCSSValue& aValue, const nscolor aParentColor,
|
static void
|
||||||
nsPresContext* aPresContext, nsStyleContext *aContext,
|
GetColorMixPremultipliedWeights(float aAlpha1, float aAlpha2,
|
||||||
nscolor& aResult, RuleNodeCacheConditions& aConditions)
|
float aWeight1, float aWeight2,
|
||||||
|
float& aPremultipliedWeight1,
|
||||||
|
float& aPremultipliedWeight2,
|
||||||
|
float& aAlpha)
|
||||||
|
{
|
||||||
|
float alphaWeight1 = aWeight1 * aAlpha1;
|
||||||
|
float alphaWeight2 = aWeight2 * aAlpha2;
|
||||||
|
|
||||||
|
aAlpha = alphaWeight1 + alphaWeight2;
|
||||||
|
if (aAlpha <= 0.0f) {
|
||||||
|
aPremultipliedWeight1 = 0.0f;
|
||||||
|
aPremultipliedWeight2 = 0.0f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
aPremultipliedWeight1 = alphaWeight1 / aAlpha;
|
||||||
|
aPremultipliedWeight2 = alphaWeight2 / aAlpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
static css::OklabColor
|
||||||
|
OklchToOklabColor(const css::OklchColor& aColor)
|
||||||
|
{
|
||||||
|
float hueRadians = aColor.mHue * css::kRadiansPerDegree;
|
||||||
|
return {
|
||||||
|
aColor.mL,
|
||||||
|
aColor.mChroma * std::cos(hueRadians),
|
||||||
|
aColor.mChroma * std::sin(hueRadians)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
GetColorMixColorComponents(const nsCSSValue& aValue, nscolor aResolvedColor,
|
||||||
|
css::OklabColor& aOklab,
|
||||||
|
css::OklchColor& aOklch,
|
||||||
|
float& aAlpha)
|
||||||
|
{
|
||||||
|
nsCSSUnit unit = aValue.GetUnit();
|
||||||
|
if (unit == eCSSUnit_OklabColor) {
|
||||||
|
const nsCSSValueFloatColor* color = aValue.GetFloatColorValue();
|
||||||
|
aOklab = { color->Comp1(), color->Comp2(), color->Comp3() };
|
||||||
|
aOklch = css::OklabToOklchColor(aOklab);
|
||||||
|
aAlpha = mozilla::clamped(color->Alpha(), 0.0f, 1.0f);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unit == eCSSUnit_OklchColor) {
|
||||||
|
const nsCSSValueFloatColor* color = aValue.GetFloatColorValue();
|
||||||
|
aOklch = { color->Comp1(), color->Comp2(), color->Comp3() };
|
||||||
|
aOklab = OklchToOklabColor(aOklch);
|
||||||
|
aAlpha = mozilla::clamped(color->Alpha(), 0.0f, 1.0f);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
aOklab = css::SRGBToOklabColor(aResolvedColor);
|
||||||
|
aOklch = css::OklabToOklchColor(aOklab);
|
||||||
|
aAlpha = NS_GET_A(aResolvedColor) / 255.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float
|
||||||
|
InterpolateColorMixHue(float aHue1, float aHue2, float aWeight1,
|
||||||
|
float aWeight2)
|
||||||
|
{
|
||||||
|
float hueDiff = aHue2 - aHue1;
|
||||||
|
if (hueDiff > 180.0f) {
|
||||||
|
aHue1 += 360.0f;
|
||||||
|
} else if (hueDiff < -180.0f) {
|
||||||
|
aHue2 += 360.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float hue = aHue1 * aWeight1 + aHue2 * aWeight2;
|
||||||
|
hue = std::fmod(hue, 360.0f);
|
||||||
|
if (hue < 0.0f) {
|
||||||
|
hue += 360.0f;
|
||||||
|
}
|
||||||
|
return hue;
|
||||||
|
}
|
||||||
|
|
||||||
|
static nscolor
|
||||||
|
MixColorsInOklab(const css::OklabColor& aColor1, float aAlpha1,
|
||||||
|
const css::OklabColor& aColor2, float aAlpha2,
|
||||||
|
float aWeight1, float aWeight2, float aAlphaMultiplier)
|
||||||
|
{
|
||||||
|
float premultipliedWeight1, premultipliedWeight2, alpha;
|
||||||
|
GetColorMixPremultipliedWeights(aAlpha1, aAlpha2, aWeight1, aWeight2,
|
||||||
|
premultipliedWeight1, premultipliedWeight2,
|
||||||
|
alpha);
|
||||||
|
alpha *= aAlphaMultiplier;
|
||||||
|
if (alpha <= 0.0f) {
|
||||||
|
return NS_RGBA(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return css::OklabToSRGBColor(
|
||||||
|
aColor1.mL * premultipliedWeight1 + aColor2.mL * premultipliedWeight2,
|
||||||
|
aColor1.mA * premultipliedWeight1 + aColor2.mA * premultipliedWeight2,
|
||||||
|
aColor1.mB * premultipliedWeight1 + aColor2.mB * premultipliedWeight2,
|
||||||
|
alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
static nscolor
|
||||||
|
MixColorsInOklch(css::OklchColor aColor1, float aAlpha1,
|
||||||
|
css::OklchColor aColor2, float aAlpha2,
|
||||||
|
float aWeight1, float aWeight2, float aAlphaMultiplier)
|
||||||
|
{
|
||||||
|
if (aColor1.mChroma <= css::kOklchPowerlessChromaEpsilon) {
|
||||||
|
aColor1.mHue = aColor2.mHue;
|
||||||
|
}
|
||||||
|
if (aColor2.mChroma <= css::kOklchPowerlessChromaEpsilon) {
|
||||||
|
aColor2.mHue = aColor1.mHue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float premultipliedWeight1, premultipliedWeight2, alpha;
|
||||||
|
GetColorMixPremultipliedWeights(aAlpha1, aAlpha2, aWeight1, aWeight2,
|
||||||
|
premultipliedWeight1, premultipliedWeight2,
|
||||||
|
alpha);
|
||||||
|
alpha *= aAlphaMultiplier;
|
||||||
|
if (alpha <= 0.0f) {
|
||||||
|
return NS_RGBA(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
float hue = InterpolateColorMixHue(aColor1.mHue, aColor2.mHue,
|
||||||
|
aWeight1, aWeight2);
|
||||||
|
return css::OklchToSRGBColor(
|
||||||
|
aColor1.mL * premultipliedWeight1 + aColor2.mL * premultipliedWeight2,
|
||||||
|
aColor1.mChroma * premultipliedWeight1 +
|
||||||
|
aColor2.mChroma * premultipliedWeight2,
|
||||||
|
hue,
|
||||||
|
alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
SetColor(const nsCSSValue& aValue,
|
||||||
|
const nscolor aParentColor,
|
||||||
|
nsPresContext* aPresContext,
|
||||||
|
nsStyleContext *aContext,
|
||||||
|
nscolor& aResult,
|
||||||
|
RuleNodeCacheConditions& aConditions)
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result = false;
|
||||||
nsCSSUnit unit = aValue.GetUnit();
|
nsCSSUnit unit = aValue.GetUnit();
|
||||||
|
|
@ -1236,21 +1372,30 @@ static bool SetColor(const nsCSSValue& aValue, const nscolor aParentColor,
|
||||||
const mozilla::css::ColorMixValue* colorMix = aValue.GetColorMixValue();
|
const mozilla::css::ColorMixValue* colorMix = aValue.GetColorMixValue();
|
||||||
if (colorMix) {
|
if (colorMix) {
|
||||||
nscolor color1, color2;
|
nscolor color1, color2;
|
||||||
// XXX: Avoid fatal recursion when either color resolves to NS_COLOR_CURRENTCOLOR.
|
bool resolvedColor1, resolvedColor2;
|
||||||
if (colorMix->mColor1.GetUnit() == eCSSUnit_EnumColor &&
|
|
||||||
colorMix->mColor1.GetIntValue() == NS_COLOR_CURRENTCOLOR) {
|
// XXX: This is a hack to avoid recursive calls to SetColor when either color resolves
|
||||||
|
// to NS_COLOR_CURRENTCOLOR, as it would result in re-evaluation of the color.
|
||||||
|
// Instead of recursing, we reach up to set either color to the parent color, instead.
|
||||||
|
if (colorMix->mColor1.GetUnit() == eCSSUnit_EnumColor && colorMix->mColor1.GetIntValue() == NS_COLOR_CURRENTCOLOR) {
|
||||||
color1 = aParentColor;
|
color1 = aParentColor;
|
||||||
|
resolvedColor1 = true;
|
||||||
} else {
|
} else {
|
||||||
SetColor(colorMix->mColor1, aParentColor, aPresContext, aContext, color1, aConditions);
|
resolvedColor1 =
|
||||||
|
SetColor(colorMix->mColor1, aParentColor, aPresContext, aContext,
|
||||||
|
color1, aConditions);
|
||||||
}
|
}
|
||||||
if (colorMix->mColor2.GetUnit() == eCSSUnit_EnumColor &&
|
if (colorMix->mColor2.GetUnit() == eCSSUnit_EnumColor &&
|
||||||
colorMix->mColor2.GetIntValue() == NS_COLOR_CURRENTCOLOR) {
|
colorMix->mColor2.GetIntValue() == NS_COLOR_CURRENTCOLOR) {
|
||||||
color2 = aParentColor;
|
color2 = aParentColor;
|
||||||
|
resolvedColor2 = true;
|
||||||
} else {
|
} else {
|
||||||
SetColor(colorMix->mColor2, aParentColor, aPresContext, aContext, color2, aConditions);
|
resolvedColor2 =
|
||||||
|
SetColor(colorMix->mColor2, aParentColor, aPresContext, aContext,
|
||||||
|
color2, aConditions);
|
||||||
}
|
}
|
||||||
if (color1 && color2) {
|
|
||||||
|
|
||||||
|
if (resolvedColor1 && resolvedColor2) {
|
||||||
// interpolate each RGBA component with proper percentage handling
|
// interpolate each RGBA component with proper percentage handling
|
||||||
float w1 = colorMix->mWeight1;
|
float w1 = colorMix->mWeight1;
|
||||||
float w2 = colorMix->mWeight2;
|
float w2 = colorMix->mWeight2;
|
||||||
|
|
@ -1267,10 +1412,19 @@ static bool SetColor(const nsCSSValue& aValue, const nscolor aParentColor,
|
||||||
w1 = w2 = 0.5f;
|
w1 = w2 = 0.5f;
|
||||||
sum = 1.0f;
|
sum = 1.0f;
|
||||||
}
|
}
|
||||||
|
float alphaMultiplier = std::min(sum, 1.0f);
|
||||||
|
|
||||||
float norm1 = w1 / sum;
|
float norm1 = w1 / sum;
|
||||||
float norm2 = w2 / sum;
|
float norm2 = w2 / sum;
|
||||||
|
|
||||||
|
css::OklabColor oklab1, oklab2;
|
||||||
|
css::OklchColor oklch1, oklch2;
|
||||||
|
float oklabAlpha1, oklabAlpha2;
|
||||||
|
GetColorMixColorComponents(colorMix->mColor1, color1, oklab1,
|
||||||
|
oklch1, oklabAlpha1);
|
||||||
|
GetColorMixColorComponents(colorMix->mColor2, color2, oklab2,
|
||||||
|
oklch2, oklabAlpha2);
|
||||||
|
|
||||||
if (colorMix->mColorSpace == mozilla::css::ColorMixColorSpace::HSL) {
|
if (colorMix->mColorSpace == mozilla::css::ColorMixColorSpace::HSL) {
|
||||||
// HSL color space mixing
|
// HSL color space mixing
|
||||||
float h1, s1, l1, h2, s2, l2;
|
float h1, s1, l1, h2, s2, l2;
|
||||||
|
|
@ -1342,10 +1496,23 @@ static bool SetColor(const nsCSSValue& aValue, const nscolor aParentColor,
|
||||||
|
|
||||||
// Convert back to RGB
|
// Convert back to RGB
|
||||||
nscolor hslResult = NS_HSL2RGB(h, s, l);
|
nscolor hslResult = NS_HSL2RGB(h, s, l);
|
||||||
uint8_t aInt = (uint8_t)mozilla::clamped(a * 255.0f + 0.5f, 0.0f, 255.0f);
|
uint8_t aInt = (uint8_t)mozilla::clamped(
|
||||||
|
a * alphaMultiplier * 255.0f + 0.5f, 0.0f, 255.0f);
|
||||||
|
|
||||||
aResult = NS_RGBA(NS_GET_R(hslResult), NS_GET_G(hslResult), NS_GET_B(hslResult), aInt);
|
aResult = NS_RGBA(NS_GET_R(hslResult), NS_GET_G(hslResult), NS_GET_B(hslResult), aInt);
|
||||||
result = true;
|
result = true;
|
||||||
|
} else if (colorMix->mColorSpace ==
|
||||||
|
mozilla::css::ColorMixColorSpace::Oklab) {
|
||||||
|
aResult = MixColorsInOklab(oklab1, oklabAlpha1,
|
||||||
|
oklab2, oklabAlpha2,
|
||||||
|
norm1, norm2, alphaMultiplier);
|
||||||
|
result = true;
|
||||||
|
} else if (colorMix->mColorSpace ==
|
||||||
|
mozilla::css::ColorMixColorSpace::Oklch) {
|
||||||
|
aResult = MixColorsInOklch(oklch1, oklabAlpha1,
|
||||||
|
oklch2, oklabAlpha2,
|
||||||
|
norm1, norm2, alphaMultiplier);
|
||||||
|
result = true;
|
||||||
} else {
|
} else {
|
||||||
// sRGB color space mixing with proper alpha premultiplication
|
// sRGB color space mixing with proper alpha premultiplication
|
||||||
float r1 = NS_GET_R(color1);
|
float r1 = NS_GET_R(color1);
|
||||||
|
|
@ -1395,7 +1562,8 @@ static bool SetColor(const nsCSSValue& aValue, const nscolor aParentColor,
|
||||||
uint8_t rInt = (uint8_t)mozilla::clamped(r + 0.5f, 0.0f, 255.0f);
|
uint8_t rInt = (uint8_t)mozilla::clamped(r + 0.5f, 0.0f, 255.0f);
|
||||||
uint8_t gInt = (uint8_t)mozilla::clamped(g + 0.5f, 0.0f, 255.0f);
|
uint8_t gInt = (uint8_t)mozilla::clamped(g + 0.5f, 0.0f, 255.0f);
|
||||||
uint8_t bInt = (uint8_t)mozilla::clamped(b + 0.5f, 0.0f, 255.0f);
|
uint8_t bInt = (uint8_t)mozilla::clamped(b + 0.5f, 0.0f, 255.0f);
|
||||||
uint8_t aInt = (uint8_t)mozilla::clamped(a * 255.0f + 0.5f, 0.0f, 255.0f);
|
uint8_t aInt = (uint8_t)mozilla::clamped(
|
||||||
|
a * alphaMultiplier * 255.0f + 0.5f, 0.0f, 255.0f);
|
||||||
|
|
||||||
aResult = NS_RGBA(rInt, gInt, bInt, aInt);
|
aResult = NS_RGBA(rInt, gInt, bInt, aInt);
|
||||||
result = true;
|
result = true;
|
||||||
|
|
|
||||||
|
|
@ -4333,6 +4333,10 @@ var gCSSProperties = {
|
||||||
"oklab(100% 0 0)",
|
"oklab(100% 0 0)",
|
||||||
"oklab(60% 0.1 -0.1 / 75%)",
|
"oklab(60% 0.1 -0.1 / 75%)",
|
||||||
"oklch(70% 0.2 180deg / 40%)",
|
"oklch(70% 0.2 180deg / 40%)",
|
||||||
|
"color-mix(in oklab, red, blue)",
|
||||||
|
"color-mix(in oklch, red, blue)",
|
||||||
|
"color-mix(in oklab, oklab(0.1 0.2 0.3), oklab(0.5 0.6 0.7))",
|
||||||
|
"color-mix(in oklch, oklch(0.1 0.2 30), oklch(0.5 0.6 70))",
|
||||||
],
|
],
|
||||||
invalid_values: [
|
invalid_values: [
|
||||||
"#f",
|
"#f",
|
||||||
|
|
|
||||||
|
|
@ -406,14 +406,19 @@ var noframe_container = document.getElementById("content");
|
||||||
["lch(150% 50 60 / 1)", "rgb(255, 255, 255)"],
|
["lch(150% 50 60 / 1)", "rgb(255, 255, 255)"],
|
||||||
["oklab(0 0 0 / 1)", "rgb(0, 0, 0)"],
|
["oklab(0 0 0 / 1)", "rgb(0, 0, 0)"],
|
||||||
["oklab(0 0 0 / 0.5)", "rgba(0, 0, 0, 0.5)"],
|
["oklab(0 0 0 / 0.5)", "rgba(0, 0, 0, 0.5)"],
|
||||||
["oklab(150% 0.5 0.2 / 1)", "rgb(255, 0, 0)"],
|
["oklab(150% 0.5 0.2 / 1)", "rgb(255, 255, 255)"],
|
||||||
["oklch(0 0 0 / 1)", "rgb(0, 0, 0)"],
|
["oklch(0 0 0 / 1)", "rgb(0, 0, 0)"],
|
||||||
["oklch(0 0 0 / 0.5)", "rgba(0, 0, 0, 0.5)"],
|
["oklch(0 0 0 / 0.5)", "rgba(0, 0, 0, 0.5)"],
|
||||||
["oklch(0.0001% 0.2 45 / 1)", "rgb(0, 0, 0)"],
|
["oklch(0.0001% 0.2 45 / 1)", "rgb(0, 0, 0)"],
|
||||||
["oklch(99.9999% 0.2 45 / 1)", "rgb(255, 207, 132)"],
|
["oklch(99.9999% 0.2 45 / 1)", "rgb(255, 251, 242)"],
|
||||||
["oklch(0% 1.1 60 / 1)", "rgb(0, 0, 0)"],
|
["oklch(0% 1.1 60 / 1)", "rgb(0, 0, 0)"],
|
||||||
["oklch(100% 110 60 / 1)", "rgb(0, 255, 0)"],
|
["oklch(100% 110 60 / 1)", "rgb(255, 255, 255)"],
|
||||||
["oklch(150% 0.5 50 / 1)", "rgb(255, 0, 0)"],
|
["oklch(150% 0.5 50 / 1)", "rgb(255, 255, 255)"],
|
||||||
|
["color-mix(in oklab, red, blue)", "rgb(140, 83, 162)"],
|
||||||
|
["color-mix(in oklch, red, blue)", "rgb(183, 0, 190)"],
|
||||||
|
["color-mix(in oklab, red, transparent)", "rgba(255, 0, 0, 0.5)"],
|
||||||
|
["color-mix(in oklab, oklab(0.1 0.2 0.3), oklab(0.5 0.6 0.7))", "rgb(83, 24, 0)"],
|
||||||
|
["color-mix(in oklch, oklch(0.1 0.2 30), oklch(0.5 0.6 70))", "rgb(84, 23, 0)"],
|
||||||
];
|
];
|
||||||
|
|
||||||
var p = document.createElement("p");
|
var p = document.createElement("p");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue