mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-25 17:25:08 +02:00
671 lines
23 KiB
C++
671 lines
23 KiB
C++
/*
|
|
* Copyright (c) 2026, Tim Ledbetter <tim.ledbetter@ladybird.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <AK/Math.h>
|
|
#include <LibGfx/Color.h>
|
|
#include <LibGfx/ColorConversion.h>
|
|
#include <math.h>
|
|
|
|
namespace Gfx {
|
|
|
|
ColorComponents color_to_srgb(Color color)
|
|
{
|
|
return {
|
|
color.red() / 255.0f,
|
|
color.green() / 255.0f,
|
|
color.blue() / 255.0f,
|
|
color.alpha() / 255.0f,
|
|
};
|
|
}
|
|
|
|
Color srgb_to_color(ColorComponents const& components)
|
|
{
|
|
return Color(
|
|
clamp(lroundf(components[0] * 255.0f), 0L, 255L),
|
|
clamp(lroundf(components[1] * 255.0f), 0L, 255L),
|
|
clamp(lroundf(components[2] * 255.0f), 0L, 255L),
|
|
clamp(lroundf(components.alpha() * 255.0f), 0L, 255L));
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#predefined-sRGB
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents srgb_to_linear_srgb(ColorComponents const& srgb)
|
|
{
|
|
auto to_linear = [](float c) {
|
|
float sign = c < 0 ? -1.0f : 1.0f;
|
|
float absolute = abs(c);
|
|
|
|
if (absolute <= 0.04045f)
|
|
return c / 12.92f;
|
|
|
|
return sign * static_cast<float>(pow((absolute + 0.055f) / 1.055f, 2.4));
|
|
};
|
|
|
|
return { to_linear(srgb[0]), to_linear(srgb[1]), to_linear(srgb[2]), srgb.alpha() };
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#predefined-sRGB
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents linear_srgb_to_srgb(ColorComponents const& linear)
|
|
{
|
|
auto to_srgb = [](float c) {
|
|
float sign = c < 0 ? -1.0f : 1.0f;
|
|
float absolute = abs(c);
|
|
|
|
if (absolute > 0.0031308f)
|
|
return sign * static_cast<float>(1.055 * pow(absolute, 1.0 / 2.4) - 0.055);
|
|
|
|
return 12.92f * c;
|
|
};
|
|
|
|
return { to_srgb(linear[0]), to_srgb(linear[1]), to_srgb(linear[2]), linear.alpha() };
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#predefined-display-p3
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents linear_display_p3_to_xyz65(ColorComponents const& p3)
|
|
{
|
|
float x = 0.48657095f * p3[0] + 0.26566769f * p3[1] + 0.19821729f * p3[2];
|
|
float y = 0.22897456f * p3[0] + 0.69173852f * p3[1] + 0.07928691f * p3[2];
|
|
float z = 0.00000000f * p3[0] + 0.04511338f * p3[1] + 1.04394437f * p3[2];
|
|
|
|
return { x, y, z, p3.alpha() };
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#predefined-display-p3
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents display_p3_to_linear_display_p3(ColorComponents const& p3)
|
|
{
|
|
auto to_linear = [](float c) {
|
|
float sign = c < 0 ? -1.0f : 1.0f;
|
|
float absolute = abs(c);
|
|
|
|
if (absolute <= 0.04045f)
|
|
return c / 12.92f;
|
|
|
|
return sign * static_cast<float>(pow((absolute + 0.055f) / 1.055f, 2.4));
|
|
};
|
|
|
|
return { to_linear(p3[0]), to_linear(p3[1]), to_linear(p3[2]), p3.alpha() };
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#predefined-a98-rgb
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents a98rgb_to_xyz65(ColorComponents const& a98)
|
|
{
|
|
auto to_linear = [](float c) {
|
|
float sign = c < 0 ? -1.0f : 1.0f;
|
|
float absolute = abs(c);
|
|
|
|
return sign * static_cast<float>(pow(absolute, 563.0 / 256.0));
|
|
};
|
|
|
|
auto linear_r = to_linear(a98[0]);
|
|
auto linear_g = to_linear(a98[1]);
|
|
auto linear_b = to_linear(a98[2]);
|
|
|
|
float x = 0.57666904f * linear_r + 0.18555824f * linear_g + 0.18822865f * linear_b;
|
|
float y = 0.29734498f * linear_r + 0.62736357f * linear_g + 0.07529146f * linear_b;
|
|
float z = 0.02703136f * linear_r + 0.07068885f * linear_g + 0.99133754f * linear_b;
|
|
|
|
return { x, y, z, a98.alpha() };
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#predefined-prophoto-rgb
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents pro_photo_rgb_to_xyz50(ColorComponents const& prophoto)
|
|
{
|
|
auto to_linear = [](float c) -> float {
|
|
float sign = c < 0 ? -1.0f : 1.0f;
|
|
float absolute = abs(c);
|
|
|
|
if (absolute <= 16.0f / 512.0f)
|
|
return c / 16.0f;
|
|
|
|
return sign * static_cast<float>(pow(absolute, 1.8));
|
|
};
|
|
|
|
auto linear_r = to_linear(prophoto[0]);
|
|
auto linear_g = to_linear(prophoto[1]);
|
|
auto linear_b = to_linear(prophoto[2]);
|
|
|
|
float x = 0.79776664f * linear_r + 0.13518130f * linear_g + 0.03134773f * linear_b;
|
|
float y = 0.28807483f * linear_r + 0.71183523f * linear_g + 0.00008994f * linear_b;
|
|
float z = 0.00000000f * linear_r + 0.00000000f * linear_g + 0.82510460f * linear_b;
|
|
|
|
return { x, y, z, prophoto.alpha() };
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#predefined-rec2020
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents rec2020_to_xyz65(ColorComponents const& rec2020)
|
|
{
|
|
auto to_linear = [](float c) -> float {
|
|
constexpr auto alpha = 1.09929682680944;
|
|
constexpr auto beta = 0.018053968510807;
|
|
|
|
float sign = c < 0 ? -1.0f : 1.0f;
|
|
float absolute = abs(c);
|
|
|
|
if (absolute < beta * 4.5)
|
|
return c / 4.5f;
|
|
|
|
return sign * static_cast<float>(pow((absolute + alpha - 1) / alpha, 1 / 0.45));
|
|
};
|
|
|
|
auto linear_r = to_linear(rec2020[0]);
|
|
auto linear_g = to_linear(rec2020[1]);
|
|
auto linear_b = to_linear(rec2020[2]);
|
|
|
|
float x = 0.63695805f * linear_r + 0.14461690f * linear_g + 0.16888098f * linear_b;
|
|
float y = 0.26270021f * linear_r + 0.67799807f * linear_g + 0.05930172f * linear_b;
|
|
float z = 0.00000000f * linear_r + 0.02807269f * linear_g + 1.06098506f * linear_b;
|
|
|
|
return { x, y, z, rec2020.alpha() };
|
|
}
|
|
|
|
ColorComponents xyz50_to_linear_srgb(ColorComponents const& xyz)
|
|
{
|
|
// See commit description for these values.
|
|
float r = +3.134136f * xyz[0] - 1.617386f * xyz[1] - 0.490662f * xyz[2];
|
|
float g = -0.978795f * xyz[0] + 1.916254f * xyz[1] + 0.033443f * xyz[2];
|
|
float b = +0.071955f * xyz[0] - 0.228977f * xyz[1] + 1.405386f * xyz[2];
|
|
|
|
return { r, g, b, xyz.alpha() };
|
|
}
|
|
|
|
ColorComponents xyz65_to_linear_srgb(ColorComponents const& xyz)
|
|
{
|
|
// See commit description for these values.
|
|
float r = +3.240970f * xyz[0] - 1.537383f * xyz[1] - 0.498611f * xyz[2];
|
|
float g = -0.969244f * xyz[0] + 1.875968f * xyz[1] + 0.041555f * xyz[2];
|
|
float b = +0.055630f * xyz[0] - 0.203977f * xyz[1] + 1.056972f * xyz[2];
|
|
|
|
return { r, g, b, xyz.alpha() };
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents lab_to_xyz50(ColorComponents const& lab)
|
|
{
|
|
constexpr auto kappa = 24389.0 / 27.0;
|
|
constexpr auto epsilon = 216.0 / 24389.0;
|
|
|
|
float L = lab[0];
|
|
float a = lab[1];
|
|
float b = lab[2];
|
|
|
|
float f1 = (L + 16.0f) / 116.0f;
|
|
float f0 = a / 500.0f + f1;
|
|
float f2 = f1 - b / 200.0f;
|
|
|
|
auto compute = [](float f) -> float {
|
|
float cubed = f * f * f;
|
|
if (cubed > epsilon)
|
|
return cubed;
|
|
return static_cast<float>((116.0 * f - 16.0) / kappa);
|
|
};
|
|
|
|
float x = compute(f0);
|
|
float y = L > kappa * epsilon ? static_cast<float>(pow((L + 16.0) / 116.0, 3)) : static_cast<float>(L / kappa);
|
|
float z = compute(f2);
|
|
|
|
// D50
|
|
constexpr float x_n = 0.3457f / 0.3585f;
|
|
constexpr float y_n = 1.0f;
|
|
constexpr float z_n = (1.0f - 0.3457f - 0.3585f) / 0.3585f;
|
|
|
|
return { x_n * x, y_n * y, z_n * z, lab.alpha() };
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#predefined-sRGB-linear
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents linear_srgb_to_xyz65(ColorComponents const& components)
|
|
{
|
|
float red = components[0];
|
|
float green = components[1];
|
|
float blue = components[2];
|
|
return {
|
|
0.4123907993f * red + 0.3575843394f * green + 0.1804807884f * blue,
|
|
0.2126390059f * red + 0.7151686788f * green + 0.0721923154f * blue,
|
|
0.0193308187f * red + 0.1191947798f * green + 0.9505321522f * blue,
|
|
components.alpha(),
|
|
};
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents xyz65_to_xyz50(ColorComponents const& components)
|
|
{
|
|
float x = components[0];
|
|
float y = components[1];
|
|
float z = components[2];
|
|
return {
|
|
+1.0479298208f * x + 0.0229468746f * y - 0.0501922295f * z,
|
|
+0.0296278156f * x + 0.9904344482f * y - 0.0170738250f * z,
|
|
-0.0092430581f * x + 0.0150551448f * y + 0.7518742814f * z,
|
|
components.alpha(),
|
|
};
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents xyz50_to_xyz65(ColorComponents const& components)
|
|
{
|
|
float x = components[0];
|
|
float y = components[1];
|
|
float z = components[2];
|
|
return {
|
|
+0.9554734528f * x - 0.0230985368f * y + 0.0632593086f * z,
|
|
-0.0283697094f * x + 1.0099954580f * y + 0.0210415381f * z,
|
|
+0.0123140016f * x - 0.0205076964f * y + 1.3303659366f * z,
|
|
components.alpha(),
|
|
};
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents xyz65_to_oklab(ColorComponents const& components)
|
|
{
|
|
float x = components[0];
|
|
float y = components[1];
|
|
float z = components[2];
|
|
|
|
float long_cone = cbrtf(0.8190224379967030f * x + 0.3619062600528904f * y - 0.1288737815209879f * z);
|
|
float medium_cone = cbrtf(0.0329836539323885f * x + 0.9292868615863434f * y + 0.0361446663506424f * z);
|
|
float short_cone = cbrtf(0.0481771893596242f * x + 0.2642395317527308f * y + 0.6335478284694309f * z);
|
|
|
|
return {
|
|
0.2104542683093140f * long_cone + 0.7936177747023054f * medium_cone - 0.0040720430116193f * short_cone,
|
|
1.9779985324311684f * long_cone - 2.4285922420485799f * medium_cone + 0.4505937096174110f * short_cone,
|
|
0.0259040424655478f * long_cone + 0.7827717124575296f * medium_cone - 0.8086757549230774f * short_cone,
|
|
components.alpha(),
|
|
};
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents oklab_to_xyz65(ColorComponents const& components)
|
|
{
|
|
float lightness = components[0];
|
|
float a = components[1];
|
|
float b = components[2];
|
|
|
|
float long_cone = lightness + 0.3963377774f * a + 0.2158037573f * b;
|
|
float medium_cone = lightness - 0.1055613458f * a - 0.0638541728f * b;
|
|
float short_cone = lightness - 0.0894841775f * a - 1.2914855480f * b;
|
|
|
|
long_cone = long_cone * long_cone * long_cone;
|
|
medium_cone = medium_cone * medium_cone * medium_cone;
|
|
short_cone = short_cone * short_cone * short_cone;
|
|
|
|
return {
|
|
+1.2268798758459243f * long_cone - 0.5578149944602171f * medium_cone + 0.2813910456659647f * short_cone,
|
|
-0.0405757452148008f * long_cone + 1.1122868032803170f * medium_cone - 0.0717110580655164f * short_cone,
|
|
-0.0763729366746601f * long_cone - 0.4214933324022432f * medium_cone + 1.5869240198367816f * short_cone,
|
|
components.alpha(),
|
|
};
|
|
}
|
|
|
|
static ColorComponents rectangular_to_polar(ColorComponents const& components)
|
|
{
|
|
float a = components[1];
|
|
float b = components[2];
|
|
|
|
float chroma = sqrtf(a * a + b * b);
|
|
float hue = atan2f(b, a) * 180.0f / AK::Pi<float>;
|
|
if (hue < 0.0f)
|
|
hue += 360.0f;
|
|
|
|
return { components[0], chroma, hue, components.alpha() };
|
|
}
|
|
|
|
static ColorComponents polar_to_rectangular(ColorComponents const& components)
|
|
{
|
|
float chroma = components[1];
|
|
float hue_radians = components[2] * AK::Pi<float> / 180.0f;
|
|
|
|
return {
|
|
components[0],
|
|
chroma * cosf(hue_radians),
|
|
chroma * sinf(hue_radians),
|
|
components.alpha(),
|
|
};
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#lab-to-lch
|
|
ColorComponents oklab_to_oklch(ColorComponents const& components) { return rectangular_to_polar(components); }
|
|
|
|
// https://drafts.csswg.org/css-color-4/#lch-to-lab
|
|
ColorComponents oklch_to_oklab(ColorComponents const& components) { return polar_to_rectangular(components); }
|
|
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents xyz50_to_lab(ColorComponents const& components)
|
|
{
|
|
constexpr auto kappa = 24389.0 / 27.0;
|
|
constexpr auto epsilon = 216.0 / 24389.0;
|
|
|
|
// D50
|
|
constexpr float x_n = 0.3457f / 0.3585f;
|
|
constexpr float y_n = 1.0f;
|
|
constexpr float z_n = (1.0f - 0.3457f - 0.3585f) / 0.3585f;
|
|
|
|
auto f = [](float value) -> float {
|
|
if (value > epsilon)
|
|
return cbrtf(value);
|
|
return static_cast<float>((kappa * value + 16.0) / 116.0);
|
|
};
|
|
|
|
float fx = f(components[0] / x_n);
|
|
float fy = f(components[1] / y_n);
|
|
float fz = f(components[2] / z_n);
|
|
|
|
return {
|
|
116.0f * fy - 16.0f,
|
|
500.0f * (fx - fy),
|
|
200.0f * (fy - fz),
|
|
components.alpha(),
|
|
};
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#lab-to-lch
|
|
ColorComponents lab_to_lch(ColorComponents const& components) { return rectangular_to_polar(components); }
|
|
|
|
// https://drafts.csswg.org/css-color-4/#lch-to-lab
|
|
ColorComponents lch_to_lab(ColorComponents const& components) { return polar_to_rectangular(components); }
|
|
|
|
// https://drafts.csswg.org/css-color-4/#rgb-to-hsl
|
|
ColorComponents srgb_to_hsl(ColorComponents const& components)
|
|
{
|
|
float red = components[0];
|
|
float green = components[1];
|
|
float blue = components[2];
|
|
|
|
float maximum = max(max(red, green), blue);
|
|
float minimum = min(min(red, green), blue);
|
|
float chroma = maximum - minimum;
|
|
float lightness = (minimum + maximum) / 2.0f;
|
|
|
|
float hue = 0.0f;
|
|
float saturation = 0.0f;
|
|
|
|
if (chroma != 0.0f) {
|
|
if (lightness == 0.0f || lightness == 1.0f)
|
|
saturation = 0.0f;
|
|
else
|
|
saturation = (maximum - lightness) / min(lightness, 1.0f - lightness);
|
|
|
|
if (maximum == red)
|
|
hue = (green - blue) / chroma + (green < blue ? 6.0f : 0.0f);
|
|
else if (maximum == green)
|
|
hue = (blue - red) / chroma + 2.0f;
|
|
else
|
|
hue = (red - green) / chroma + 4.0f;
|
|
hue *= 60.0f;
|
|
|
|
if (saturation < 0.0f) {
|
|
hue += 180.0f;
|
|
saturation = fabsf(saturation);
|
|
}
|
|
|
|
if (hue >= 360.0f)
|
|
hue -= 360.0f;
|
|
}
|
|
|
|
return { hue, saturation, lightness, components.alpha() };
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#hwb-to-rgb
|
|
ColorComponents hwb_to_srgb(ColorComponents const& components)
|
|
{
|
|
float hue = components[0];
|
|
float whiteness = components[1];
|
|
float blackness = components[2];
|
|
|
|
if (whiteness + blackness >= 1.0f) {
|
|
float gray = whiteness / (whiteness + blackness);
|
|
return { gray, gray, gray, components.alpha() };
|
|
}
|
|
|
|
auto rgb = hsl_to_srgb({ hue, 1.0f, 0.5f, components.alpha() });
|
|
float scale = 1.0f - whiteness - blackness;
|
|
|
|
return {
|
|
rgb[0] * scale + whiteness,
|
|
rgb[1] * scale + whiteness,
|
|
rgb[2] * scale + whiteness,
|
|
components.alpha(),
|
|
};
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#rgb-to-hwb
|
|
ColorComponents srgb_to_hwb(ColorComponents const& components)
|
|
{
|
|
float red = components[0];
|
|
float green = components[1];
|
|
float blue = components[2];
|
|
|
|
float maximum = max(max(red, green), blue);
|
|
float minimum = min(min(red, green), blue);
|
|
float chroma = maximum - minimum;
|
|
|
|
float hue = 0.0f;
|
|
if (chroma != 0.0f) {
|
|
if (maximum == red)
|
|
hue = ((green - blue) / chroma) + (green < blue ? 6.0f : 0.0f);
|
|
else if (maximum == green)
|
|
hue = (blue - red) / chroma + 2.0f;
|
|
else
|
|
hue = (red - green) / chroma + 4.0f;
|
|
hue *= 60.0f;
|
|
if (hue >= 360.0f)
|
|
hue -= 360.0f;
|
|
}
|
|
|
|
return { hue, minimum, 1.0f - maximum, components.alpha() };
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#predefined-display-p3
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents linear_display_p3_to_display_p3(ColorComponents const& components)
|
|
{
|
|
auto to_gamma = [](float c) {
|
|
float sign = c < 0 ? -1.0f : 1.0f;
|
|
float absolute = abs(c);
|
|
|
|
if (absolute > 0.0031308f)
|
|
return sign * static_cast<float>(1.055 * pow(absolute, 1.0 / 2.4) - 0.055);
|
|
|
|
return 12.92f * c;
|
|
};
|
|
|
|
return { to_gamma(components[0]), to_gamma(components[1]), to_gamma(components[2]), components.alpha() };
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#predefined-display-p3-linear
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents xyz65_to_linear_display_p3(ColorComponents const& components)
|
|
{
|
|
float x = components[0];
|
|
float y = components[1];
|
|
float z = components[2];
|
|
return {
|
|
+2.4934969119f * x - 0.9313836179f * y - 0.4027107845f * z,
|
|
-0.8294889696f * x + 1.7626640603f * y + 0.0236246858f * z,
|
|
+0.0358458302f * x - 0.0761723893f * y + 0.9568845240f * z,
|
|
components.alpha(),
|
|
};
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#predefined-a98-rgb
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents a98_rgb_to_linear_a98_rgb(ColorComponents const& components)
|
|
{
|
|
auto to_linear = [](float c) {
|
|
float sign = c < 0.0f ? -1.0f : 1.0f;
|
|
return sign * static_cast<float>(pow(abs(c), 563.0 / 256.0));
|
|
};
|
|
|
|
return { to_linear(components[0]), to_linear(components[1]), to_linear(components[2]), components.alpha() };
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#predefined-a98-rgb
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents linear_a98_rgb_to_a98_rgb(ColorComponents const& components)
|
|
{
|
|
auto to_gamma = [](float c) {
|
|
float sign = c < 0.0f ? -1.0f : 1.0f;
|
|
return sign * static_cast<float>(pow(abs(c), 256.0 / 563.0));
|
|
};
|
|
|
|
return { to_gamma(components[0]), to_gamma(components[1]), to_gamma(components[2]), components.alpha() };
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#predefined-a98-rgb
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents linear_a98_rgb_to_xyz65(ColorComponents const& components)
|
|
{
|
|
float red = components[0];
|
|
float green = components[1];
|
|
float blue = components[2];
|
|
return {
|
|
0.57666904f * red + 0.18555824f * green + 0.18822865f * blue,
|
|
0.29734498f * red + 0.62736357f * green + 0.07529146f * blue,
|
|
0.02703136f * red + 0.07068885f * green + 0.99133754f * blue,
|
|
components.alpha(),
|
|
};
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#predefined-a98-rgb
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents xyz65_to_linear_a98_rgb(ColorComponents const& components)
|
|
{
|
|
float x = components[0];
|
|
float y = components[1];
|
|
float z = components[2];
|
|
return {
|
|
+2.0415879038f * x - 0.5650069743f * y - 0.3473784579f * z,
|
|
-0.9692436363f * x + 1.8759675015f * y + 0.0415550574f * z,
|
|
+0.0134442806f * x - 0.1183623922f * y + 1.0151749944f * z,
|
|
components.alpha(),
|
|
};
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#predefined-prophoto-rgb
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents prophoto_rgb_to_linear_prophoto_rgb(ColorComponents const& components)
|
|
{
|
|
auto to_linear = [](float c) -> float {
|
|
float sign = c < 0.0f ? -1.0f : 1.0f;
|
|
float absolute = abs(c);
|
|
if (absolute <= 16.0f / 512.0f)
|
|
return c / 16.0f;
|
|
return sign * static_cast<float>(pow(absolute, 1.8));
|
|
};
|
|
|
|
return { to_linear(components[0]), to_linear(components[1]), to_linear(components[2]), components.alpha() };
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#predefined-prophoto-rgb
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents linear_prophoto_rgb_to_prophoto_rgb(ColorComponents const& components)
|
|
{
|
|
auto to_gamma = [](float c) -> float {
|
|
float sign = c < 0.0f ? -1.0f : 1.0f;
|
|
float absolute = abs(c);
|
|
if (absolute <= 1.0f / 512.0f)
|
|
return c * 16.0f;
|
|
return sign * static_cast<float>(pow(absolute, 1.0 / 1.8));
|
|
};
|
|
|
|
return { to_gamma(components[0]), to_gamma(components[1]), to_gamma(components[2]), components.alpha() };
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#predefined-prophoto-rgb
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents linear_prophoto_rgb_to_xyz50(ColorComponents const& components)
|
|
{
|
|
float red = components[0];
|
|
float green = components[1];
|
|
float blue = components[2];
|
|
return {
|
|
0.79776664f * red + 0.13518130f * green + 0.03134773f * blue,
|
|
0.28807483f * red + 0.71183523f * green + 0.00008994f * blue,
|
|
0.00000000f * red + 0.00000000f * green + 0.82510460f * blue,
|
|
components.alpha(),
|
|
};
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#predefined-prophoto-rgb
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents xyz50_to_linear_prophoto_rgb(ColorComponents const& components)
|
|
{
|
|
float x = components[0];
|
|
float y = components[1];
|
|
float z = components[2];
|
|
return {
|
|
+1.3457989731f * x - 0.2555802200f * y - 0.0511188540f * z,
|
|
-0.5446224939f * x + 1.5082327413f * y + 0.0205274474f * z,
|
|
+0.0000000000f * x + 0.0000000000f * y + 1.2119675456f * z,
|
|
components.alpha(),
|
|
};
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#predefined-rec2020
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents rec2020_to_linear_rec2020(ColorComponents const& components)
|
|
{
|
|
auto to_linear = [](float c) -> float {
|
|
float sign = c < 0.0f ? -1.0f : 1.0f;
|
|
float absolute = abs(c);
|
|
|
|
return sign * static_cast<float>(pow(absolute, 2.4));
|
|
};
|
|
|
|
return { to_linear(components[0]), to_linear(components[1]), to_linear(components[2]), components.alpha() };
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#predefined-rec2020
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents linear_rec2020_to_rec2020(ColorComponents const& components)
|
|
{
|
|
auto to_gamma = [](float c) -> float {
|
|
float sign = c < 0.0f ? -1.0f : 1.0f;
|
|
float absolute = abs(c);
|
|
|
|
return sign * static_cast<float>(pow(absolute, 1.0 / 2.4));
|
|
};
|
|
|
|
return { to_gamma(components[0]), to_gamma(components[1]), to_gamma(components[2]), components.alpha() };
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#predefined-rec2020
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents linear_rec2020_to_xyz65(ColorComponents const& components)
|
|
{
|
|
float red = components[0];
|
|
float green = components[1];
|
|
float blue = components[2];
|
|
return {
|
|
0.63695805f * red + 0.14461690f * green + 0.16888098f * blue,
|
|
0.26270021f * red + 0.67799807f * green + 0.05930172f * blue,
|
|
0.00000000f * red + 0.02807269f * green + 1.06098506f * blue,
|
|
components.alpha(),
|
|
};
|
|
}
|
|
|
|
// https://drafts.csswg.org/css-color-4/#predefined-rec2020
|
|
// https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
ColorComponents xyz65_to_linear_rec2020(ColorComponents const& components)
|
|
{
|
|
float x = components[0];
|
|
float y = components[1];
|
|
float z = components[2];
|
|
return {
|
|
+1.7166511880f * x - 0.3556707838f * y - 0.2533662814f * z,
|
|
-0.6666843518f * x + 1.6164812366f * y + 0.0157685458f * z,
|
|
+0.0176398574f * x - 0.0427706133f * y + 0.9421031212f * z,
|
|
components.alpha(),
|
|
};
|
|
}
|
|
|
|
}
|