Skip to content

Commit

Permalink
Use exact values for some matrices in conversions.js
Browse files Browse the repository at this point in the history
These color space conversion matrices have exact rational values that
can be computed from the numbers provided in the spec. Using exact
values is more succinct for most of these matrices, and also makes it a
nice reference implementation for other languages. This example code
already uses exact inline formulations for a number of other things,
like D50 and D65 definitions, so this is similar to that.

I only did the XYZ conversion matrices for srgb, display-p3, a98-rgb,
and rec2020.
- I don't have code to easily compute the D65/D50 conversions or
  OKLab/OKLCH as I was only interested in the predefined color spaces.
- The rational forms of prophoto-rgb's matrices exceed the precision of
  JavaScript math. I could include them as comments though.

Source to compute these: https://github.com/kainino0x/exact_css_xyz_matrices
using this Rust crate: https://crates.io/crates/rgb_derivation
as described for sRGB on this page: https://mina86.com/2019/srgb-xyz-matrix/
but using the numbers from this spec.

I used these in the WebGPU conformance test suite:
gpuweb/cts#1089
WebGPU needed only srgb and display-p3, but it was easy to extend to the
other predefined color spaces. (WebGPU may add some of those color
spaces eventually anyway.)
  • Loading branch information
kainino0x committed May 27, 2022
1 parent 096d87d commit e835926
Showing 1 changed file with 24 additions and 25 deletions.
49 changes: 24 additions & 25 deletions css-color-4/conversions.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ function lin_sRGB_to_XYZ(rgb) {
// using sRGB's own white, D65 (no chromatic adaptation)

var M = [
[ 0.41239079926595934, 0.357584339383878, 0.1804807884018343 ],
[ 0.21263900587151027, 0.715168678767756, 0.07219231536073371 ],
[ 0.01933081871559182, 0.11919477979462598, 0.9505321522496607 ]
[ 506752 / 1228815, 87881 / 245763, 12673 / 70218 ],
[ 87098 / 409605, 175762 / 245763, 12673 / 175545 ],
[ 7918 / 409605, 87881 / 737289, 1001167 / 1053270 ],
];
return multiplyMatrices(M, rgb);
}
Expand All @@ -63,9 +63,9 @@ function XYZ_to_lin_sRGB(XYZ) {
// convert XYZ to linear-light sRGB

var M = [
[ 3.2409699419045226, -1.537383177570094, -0.4986107602930034 ],
[ -0.9692436362808796, 1.8759675015077202, 0.04155505740717559 ],
[ 0.05563007969699366, -0.20397695888897652, 1.0569715142428786 ]
[ 12831 / 3959, -329 / 214, -1974 / 3959 ],
[ -851781 / 878810, 1648619 / 878810, 36519 / 878810 ],
[ 705 / 12673, -2585 / 12673, 705 / 667 ],
];

return multiplyMatrices(M, XYZ);
Expand Down Expand Up @@ -93,21 +93,20 @@ function lin_P3_to_XYZ(rgb) {
// using D65 (no chromatic adaptation)
// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
var M = [
[0.4865709486482162, 0.26566769316909306, 0.1982172852343625],
[0.2289745640697488, 0.6917385218365064, 0.079286914093745],
[0.0000000000000000, 0.04511338185890264, 1.043944368900976]
[ 608311 / 1250200, 189793 / 714400, 198249 / 1000160 ],
[ 35783 / 156275, 247089 / 357200, 198249 / 2500400 ],
[ 0 / 1, 32229 / 714400, 5220557 / 5000800 ],
];
// 0 was computed as -3.972075516933488e-17

return multiplyMatrices(M, rgb);
}

function XYZ_to_lin_P3(XYZ) {
// convert XYZ to linear-light P3
var M = [
[ 2.493496911941425, -0.9313836179191239, -0.40271078445071684],
[-0.8294889695615747, 1.7626640603183463, 0.023624685841943577],
[ 0.03584583024378447, -0.07617238926804182, 0.9568845240076872]
[ 446124 / 178915, -333277 / 357830, -72051 / 178915 ],
[ -14852 / 17905, 63121 / 35810, 423 / 17905 ],
[ 11844 / 330415, -50337 / 660830, 316169 / 330415 ],
];

return multiplyMatrices(M, XYZ);
Expand Down Expand Up @@ -211,9 +210,9 @@ function lin_a98rgb_to_XYZ(rgb) {
// from the chromaticity coordinates of R G B W
// see matrixmaker.html
var M = [
[ 0.5766690429101305, 0.1855582379065463, 0.1882286462349947 ],
[ 0.29734497525053605, 0.6273635662554661, 0.07529145849399788 ],
[ 0.02703136138641234, 0.07068885253582723, 0.9913375368376388 ]
[ 573536 / 994567, 263643 / 1420810, 187206 / 994567 ],
[ 591459 / 1989134, 6239551 / 9945670, 374412 / 4972835 ],
[ 53769 / 1989134, 351524 / 4972835, 4929758 / 4972835 ],
];

return multiplyMatrices(M, rgb);
Expand All @@ -222,9 +221,9 @@ function lin_a98rgb_to_XYZ(rgb) {
function XYZ_to_lin_a98rgb(XYZ) {
// convert XYZ to linear-light a98-rgb
var M = [
[ 2.0415879038107465, -0.5650069742788596, -0.34473135077832956 ],
[ -0.9692436362808795, 1.8759675015077202, 0.04155505740717557 ],
[ 0.013444280632031142, -0.11836239223101838, 1.0151749943912054 ]
[ 1829569 / 896150, -506331 / 896150, -308931 / 896150 ],
[ -851781 / 878810, 1648619 / 878810, 36519 / 878810 ],
[ 16779 / 1248040, -147721 / 1248040, 1266979 / 1248040 ],
];

return multiplyMatrices(M, XYZ);
Expand Down Expand Up @@ -278,9 +277,9 @@ function lin_2020_to_XYZ(rgb) {
// using D65 (no chromatic adaptation)
// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
var M = [
[0.6369580483012914, 0.14461690358620832, 0.1688809751641721],
[0.2627002120112671, 0.6779980715188708, 0.05930171646986196],
[0.000000000000000, 0.028072693049087428, 1.060985057710791]
[ 63426534 / 99577255, 20160776 / 139408157, 47086771 / 278816314 ],
[ 26158966 / 99577255, 472592308 / 697040785, 8267143 / 139408157 ],
[ 0 / 1, 19567812 / 697040785, 295819943 / 278816314 ],
];
// 0 is actually calculated as 4.994106574466076e-17

Expand All @@ -290,9 +289,9 @@ function lin_2020_to_XYZ(rgb) {
function XYZ_to_lin_2020(XYZ) {
// convert XYZ to linear-light rec2020
var M = [
[1.7166511879712674, -0.35567078377639233, -0.25336628137365974],
[-0.6666843518324892, 1.6164812366349395, 0.01576854581391113],
[0.017639857445310783, -0.042770613257808524, 0.9421031212354738]
[ 30757411 / 17917100, -6372589 / 17917100, -4539589 / 17917100 ],
[ -19765991 / 29648200, 47925759 / 29648200, 467509 / 29648200 ],
[ 792561 / 44930125, -1921689 / 44930125, 42328811 / 44930125 ],
];

return multiplyMatrices(M, XYZ);
Expand Down

0 comments on commit e835926

Please sign in to comment.