Skip to content

Commit 9c21060

Browse files
committed
[css-color] code appendix
1 parent 4a09738 commit 9c21060

File tree

1 file changed

+257
-0
lines changed

1 file changed

+257
-0
lines changed

css-color/Overview.bs

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2447,6 +2447,10 @@ Guaranteeing Adequate Contrast: the ''contrast'' adjuster</h3>
24472447
<p>
24482448
To compute the <dfn>luminance</dfn> of a color:
24492449

2450+
<p class="issue">The code below is only for sRGB, and duplicates the
2451+
more general code in the conversions appendix.
2452+
</p>
2453+
24502454
<ol class="lang-javascript">
24512455
<li>
24522456
Scale the red, green, and blue channels of the color to the range [0,1].
@@ -4269,7 +4273,260 @@ The CSSColor Object</h3>
42694273

42704274
</dl>
42714275

4276+
<h2 id="color-conversion-code">Sample code for color conversions</h2>
4277+
4278+
<pre class="lang-javascript">
4279+
<!-- imported 11 May 2016-->
4280+
// sRGB-related functions
4281+
4282+
function lin_sRGB(RGB) {
4283+
// convert an array of sRGB values in the range 0.0 - 1.0
4284+
// to linear light (un-companded) form.
4285+
// https://en.wikipedia.org/wiki/SRGB
4286+
return RGB.map(function (val) {
4287+
if (val < 0.04045) {
4288+
return val / 12.92;
4289+
}
4290+
4291+
return Math.pow((val + 0.055) / 1.055, 2.4);
4292+
});
4293+
}
4294+
4295+
function gam_sRGB(RGB) {
4296+
// convert an array of linear-light sRGB values in the range 0.0-1.0
4297+
// to gamma corrected form
4298+
// https://en.wikipedia.org/wiki/SRGB
4299+
return RGB.map(function (val) {
4300+
if (val > 0.0031308) {
4301+
return 1.055 * Math.pow(val, 1/2.4) - 0.055;
4302+
}
4303+
4304+
return 12.92 * val;
4305+
});
4306+
}
4307+
4308+
function lin_sRGB_to_XYZ(rgb) {
4309+
// convert an array of linear-light sRGB values to CIE XYZ
4310+
// using sRGB's own white, D65 (no chromatic adaptation)
4311+
// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
4312+
var M = math.matrix([
4313+
[0.4124564, 0.3575761, 0.1804375],
4314+
[0.2126729, 0.7151522, 0.0721750],
4315+
[0.0193339, 0.1191920, 0.9503041]
4316+
]);
4317+
4318+
return math.multiply(M, rgb).valueOf();
4319+
}
4320+
4321+
function XYZ_to_lin_sRGB(XYZ) {
4322+
// convert XYZ to linear-light sRGB
4323+
var M = math.matrix([
4324+
[ 3.2404542, -1.5371385, -0.4985314],
4325+
[-0.9692660, 1.8760108, 0.0415560],
4326+
[ 0.0556434, -0.2040259, 1.0572252]
4327+
]);
4328+
4329+
return math.multiply(M, XYZ).valueOf();
4330+
}
4331+
4332+
// DCI P3-related functions
4333+
42724334

4335+
function lin_P3(RGB) {
4336+
// convert an array of DCI P3 RGB values in the range 0.0 - 1.0
4337+
// to linear light (un-companded) form.
4338+
4339+
return RGB.map(function (val) {
4340+
return Math.pow(val, 2.6);
4341+
});
4342+
}
4343+
4344+
function gam_P3(RGB) {
4345+
// convert an array of linear-light P3 RGB in the range 0.0-1.0
4346+
// to gamma corrected form
4347+
4348+
return RGB.map(function (val) {
4349+
return Math.pow(val, 1/2.6);
4350+
}
4351+
});
4352+
}
4353+
4354+
function lin_P3_to_XYZ(rgb) {
4355+
// convert an array of linear-light P3 values to CIE XYZ
4356+
// using D65 (no chromatic adaptation)
4357+
// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
4358+
var M = math.matrix([
4359+
[0.4865709486482162, 0.26566769316909306, 0.1982172852343625],
4360+
[0.2289745640697488, 0.6917385218365064, 0.079286914093745],
4361+
[0.0000000000000000, 0.04511338185890264, 1.043944368900976]
4362+
]);
4363+
// 0 was computed as -3.972075516933488e-17
4364+
4365+
return math.multiply(M, rgb).valueOf();
4366+
}
4367+
4368+
function XYZ_to_lin_P3(XYZ) {
4369+
// convert XYZ to linear-light P3
4370+
var M = math.matrix([
4371+
[ 2.493496911941425, -0.9313836179191239, -0.40271078445071684],
4372+
[-0.8294889695615747, 1.7626640603183463, 0.023624685841943577],
4373+
[ 0.03584583024378447, -0.07617238926804182, 0.9568845240076872]
4374+
]);
4375+
4376+
return math.multiply(M, XYZ).valueOf();
4377+
}
4378+
4379+
//Rec.2020-related functions
4380+
4381+
function lin_2020(RGB) {
4382+
// convert an array of Rec.2020 RGB values in the range 0.0 - 1.0
4383+
// to linear light (un-companded) form.
4384+
const α = 1.09929682680944 ;
4385+
const β = 0.018053968510807;
4386+
4387+
return RGB.map(function (val) {
4388+
if (val < β * 4.5 ) {
4389+
return val / 4.5;
4390+
}
4391+
4392+
return Math.pow((val + α -1 ) / α, 2.4);
4393+
});
4394+
}
4395+
//check with standard this really is 2.4 and 1/2.4, not 0.45 was wikipedia claims
4396+
4397+
function gam_2020(RGB) {
4398+
// convert an array of linear-light Rec.2020 RGB in the range 0.0-1.0
4399+
// to gamma corrected form
4400+
const α = 1.09929682680944 ;
4401+
const β = 0.018053968510807;
4402+
4403+
return RGB.map(function (val) {
4404+
if (val > β ) {
4405+
return α * Math.pow(val, 1/2.4) - (α - 1);
4406+
}
4407+
4408+
return 4.5 * val;
4409+
});
4410+
}
4411+
4412+
function lin_2020_to_XYZ(rgb) {
4413+
// convert an array of linear-light rec.2020 values to CIE XYZ
4414+
// using D65 (no chromatic adaptation)
4415+
// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
4416+
var M = math.matrix([
4417+
[0.6369580483012914, 0.14461690358620832, 0.1688809751641721],
4418+
[0.2627002120112671, 0.6779980715188708, 0.05930171646986196],
4419+
[0.000000000000000, 0.028072693049087428, 1.060985057710791]
4420+
]);
4421+
// 0 is actually calculated as 4.994106574466076e-17
4422+
4423+
return math.multiply(M, rgb).valueOf();
4424+
}
4425+
4426+
function XYZ_to_lin_2020(XYZ) {
4427+
// convert XYZ to linear-light rec.2020
4428+
var M = math.matrix([
4429+
[1.7166511879712674, -0.35567078377639233, -0.25336628137365974],
4430+
[-0.6666843518324892, 1.6164812366349395, 0.01576854581391113],
4431+
[0.017639857445310783, -0.042770613257808524, 0.9421031212354738]
4432+
]);
4433+
4434+
return math.multiply(M, XYZ).valueOf();
4435+
}
4436+
4437+
// Chromatic adaptation
4438+
4439+
function D65_to_D50(XYZ) {
4440+
// Bradford chromatic adaptation from D65 to D50
4441+
// The matrix below is the result of three operations:
4442+
// - convert from XYZ to retinal cone domain
4443+
// - scale components from one reference white to another
4444+
// - convert back to XYZ
4445+
// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
4446+
var M = math.matrix([
4447+
[ 1.0478112, 0.0228866, -0.0501270],
4448+
[ 0.0295424, 0.9904844, -0.0170491],
4449+
[-0.0092345, 0.0150436, 0.7521316]
4450+
]);
4451+
4452+
return math.multiply(M, XYZ).valueOf();
4453+
}
4454+
4455+
function D50_to_D65(XYZ) {
4456+
// Bradford chromatic adaptation from D50 to D65
4457+
var M = math.matrix([
4458+
[ 0.9555766, -0.0230393, 0.0631636],
4459+
[-0.0282895, 1.0099416, 0.0210077],
4460+
[ 0.0122982, -0.0204830, 1.3299098]
4461+
]);
4462+
4463+
return math.multiply(M, XYZ).valueOf();
4464+
}
4465+
4466+
// Lab and LCH
4467+
4468+
function XYZ_to_Lab(XYZ) {
4469+
// Assuming XYZ is relative to D50, convert to CIE Lab
4470+
// from CIE standard, which now defines these as a rational fraction
4471+
var ε = 216/24389; // 6^3/29^3
4472+
var κ = 24389/27; // 29^3/3^3
4473+
var white = [0.9642, 1.0000, 0.8249]; // D50 reference white
4474+
4475+
// compute xyz, which is XYZ scaled relative to reference white
4476+
var xyz = XYZ.map((value, i) => value / white[i]);
4477+
4478+
// now compute f
4479+
var f = xyz.map(value => value > ε ? Math.cbrt(value) : (κ * value + 16)/116);
4480+
4481+
return [
4482+
(116 * f[1]) - 16, // L
4483+
500 * (f[0] - f[1]), // a
4484+
200 * (f[1] - f[2]) // b
4485+
];
4486+
}
4487+
4488+
function Lab_to_XYZ(Lab) {
4489+
// Convert Lab to D50-adapted XYZ
4490+
var κ = 24389/27; // 29^3/3^3
4491+
var ε = 216/24389; // 6^3/29^3
4492+
var white = [0.9642, 1.0000, 0.8249]; // D50 reference white
4493+
var f = [];
4494+
4495+
// compute f, starting with the luminance-related term
4496+
f[1] = (Lab[0] + 16)/116;
4497+
f[0] = Lab[1]/500 + f[1];
4498+
f[2] = f[1] - Lab[2]/200;
4499+
4500+
// compute xyz
4501+
var xyz = [
4502+
f[0] > ε ? Math.pow(f[0],3) : (116*f[0]-16)/κ,
4503+
Lab[0] > κ * ε ? Math.pow((Lab[0]+16)/116,3) : Lab[0]/κ,
4504+
f[2] > ε ? Math.pow(f[2],3) : (116*f[2]-16)/κ
4505+
];
4506+
4507+
// Compute XYZ by scaling xyz by reference white
4508+
return xyz.map((value, i) => value * white[i]);
4509+
}
4510+
4511+
function Lab_to_LCH(Lab) {
4512+
// Convert to polar form
4513+
return [
4514+
Lab[0], // L is still L
4515+
Math.sqrt(Math.pow(Lab[1], 2) + Math.pow(Lab[2], 2)), // Chroma
4516+
Math.atan2(Lab[2], Lab[1]) * 180 / Math.PI // Hue, in degrees
4517+
];
4518+
}
4519+
4520+
function LCH_to_Lab(LCH) {
4521+
// Convert from polar form
4522+
return [
4523+
LCH[0], // L is still L
4524+
LCH[1] * Math.cos(LCH[2] * Math.PI / 180), // a
4525+
LCH[1] * Math.sin(LCH[2] * Math.PI / 180) // b
4526+
];
4527+
}
4528+
4529+
</pre>
42734530

42744531
<h2 id="system-colors" class="no-num">
42754532
Appendix A: Deprecated CSS System Colors</h2>

0 commit comments

Comments
 (0)