Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[css-color-4] Use exact values for some matrices in conversions.js #7320

Merged
merged 1 commit into from
Aug 23, 2022

Conversation

kainino0x
Copy link
Contributor

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'm not really an expert in this, so if this doesn't make sense (e.g. the
source values aren't actually exact to begin with) let me know.
I checked that these ratios generate values very close to the current
ones, usually differing in the last few digits, but double-checking
can't hurt.

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.)

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.)
@kainino0x
Copy link
Contributor Author

kainino0x commented May 27, 2022

Diff in decimal format, for easier verification (I have verified it):

lin_sRGB_to_XYZ

-		[ 0.41239079926595934, 0.357584339383878,   0.1804807884018343  ],
+		[ 0.4123907992659595,  0.35758433938387796, 0.1804807884018343  ],
-		[ 0.21263900587151027, 0.715168678767756,   0.07219231536073371 ],
+		[ 0.21263900587151036, 0.7151686787677559,  0.07219231536073371 ],
-		[ 0.01933081871559182, 0.11919477979462598, 0.9505321522496607  ]
+		[ 0.01933081871559185, 0.11919477979462599, 0.9505321522496606  ]

XYZ_to_lin_sRGB

-		[  3.2409699419045226,  -1.537383177570094,   -0.4986107602930034  ],
+		[  3.2409699419045213,  -1.5373831775700935,  -0.4986107602930033  ],
-		[ -0.9692436362808796,   1.8759675015077202,   0.04155505740717559 ],
+		[ -0.9692436362808798,   1.8759675015077206,   0.04155505740717561 ],
-		[  0.05563007969699366, -0.20397695888897652,  1.0569715142428786  ]
+		[  0.05563007969699361, -0.20397695888897657,  1.0569715142428786  ]

lin_P3_to_XYZ

-		[ 0.4865709486482162,  0.26566769316909306,  0.1982172852343625 ],
+		[ 0.48657094864821626, 0.26566769316909294,  0.1982172852343625 ],
-		[ 0.2289745640697488,  0.6917385218365064,   0.079286914093745  ],
+		[ 0.22897456406974884, 0.6917385218365062,   0.079286914093745  ],
-		[ 0.0000000000000000,  0.04511338185890264,  1.043944368900976  ]
+		[ 0,                   0.045113381858902575, 1.0439443689009757 ]

XYZ_to_lin_P3

-		[  2.493496911941425,    -0.9313836179191239, -0.40271078445071684  ],
+		[  2.4934969119414245,   -0.9313836179191236, -0.40271078445071684  ],
-		[ -0.8294889695615747,    1.7626640603183463,  0.023624685841943577 ],
+		[ -0.829488969561575,     1.7626640603183468,  0.02362468584194359  ],
-		[  0.03584583024378447,  -0.07617238926804182, 0.9568845240076872   ]
+		[  0.035845830243784335, -0.07617238926804171, 0.9568845240076873   ]

lin_a98rgb_to_XYZ

-		[ 0.5766690429101305,   0.1855582379065463,   0.1882286462349947  ],
+		[ 0.5766690429101308,   0.18555823790654627,  0.18822864623499472 ],
-		[ 0.29734497525053605,  0.6273635662554661,   0.07529145849399788 ],
+		[ 0.29734497525053616,  0.627363566255466,    0.07529145849399789 ],
-		[ 0.02703136138641234,  0.07068885253582723,  0.9913375368376388  ]
+		[ 0.027031361386412378, 0.07068885253582714,  0.9913375368376389  ]

XYZ_to_lin_a98rgb

-		[  2.0415879038107465,    -0.5650069742788596,   -0.34473135077832956 ],
+		[  2.041587903810746,     -0.5650069742788596,   -0.3447313507783295  ],
-		[ -0.9692436362808795,     1.8759675015077202,    0.04155505740717557 ],
+		[ -0.9692436362808798,     1.8759675015077206,    0.04155505740717561 ],
-		[  0.013444280632031142,  -0.11836239223101838,   1.0151749943912054  ]
+		[  0.013444280632031024,  -0.11836239223101824,   1.0151749943912054  ]

lin_2020_to_XYZ

-		[ 0.6369580483012914,  0.14461690358620832,  0.1688809751641721   ],
+		[ 0.6369580483012913,  0.14461690358620838,  0.16888097516417205  ],
-		[ 0.2627002120112671,  0.6779980715188708,   0.05930171646986196  ],
+		[ 0.26270021201126703, 0.677998071518871,    0.059301716469861945 ],
-		[ 0.000000000000000,   0.028072693049087428, 1.060985057710791    ]
+		[ 0,                   0.028072693049087508, 1.0609850577107909  ]

XYZ_to_lin_2020

-		[  1.7166511879712674,   -0.35567078377639233,  -0.25336628137365974 ],
+		[  1.7166511879712676,   -0.3556707837763924,   -0.2533662813736598  ],
-		[ -0.6666843518324892,    1.6164812366349395,    0.01576854581391113 ],
+		[ -0.666684351832489,     1.616481236634939,     0.01576854581391113 ],
-		[  0.017639857445310783, -0.042770613257808524,  0.9421031212354738  ]
+		[  0.017639857445310915, -0.042770613257808655,  0.942103121235474   ]

@svgeesus
Copy link
Contributor

svgeesus commented May 29, 2022

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.

In general using exact rational values is a better approach, agreed.

I am curious where the D65 values 0.312713, 0.329016 come from.

About a year ago there was a strong effort to recalculate everything using a consistent white point everywhere, which reduced residual errors substantially. I am reluctant to redo that without further details.

Edit: Although I see your rust code uses the same four-digit D65 values as CSS Color 4.

@kainino0x
Copy link
Contributor Author

Edit: Although I see your rust code uses the same four-digit D65 values as CSS Color 4.

Yes, I took all of the numbers including D50/D65 from this spec, and then verified that that had the same results (because they had also been used to compute the original matrices).

@kainino0x
Copy link
Contributor Author

I'll admit I'm not at all familiar with these specs. However, this change is only to the sample JavaScript code, and it only writes the same exact floating point numbers in a rational format rather than a decimal format. It's also easy to convert back to decimal format - just paste the code into a JavaScript interpreter.

@kainino0x
Copy link
Contributor Author

If they're incorrect because BT.709 requires decimal rounding at certain points of the computation, then that would have already been wrong before my change, but a legitimate concern, I think.

@kainino0x
Copy link
Contributor Author

Interesting. If the values of these matrices are wrong (again, I didn't change them, so if so they were already wrong), then either the derivation is wrong, or the numbers in the CSS spec are wrong and the normative part of the spec also needs to be fixed. I'm just a drive-by contributor, so I'd advise this discussion be continued over in #5922 which is the actual relevant place. I'm happy to help re-derive the rational forms if some spec change is required, though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants