Repository navigation
[css-color-4] Update Lab_to_XYZ sample code to avoid issues in implementations that use floats as internal storage. #14629
Copy link
Copy link
Closed
Labels
Closed Accepted as Obvious BugfixFixed an obvious error with the obvious solution.Fixed an obvious error with the obvious solution.css-color-4Current WorkCurrent Work
Description
csswg-drafts/css-color-4/conversions.js
Lines 339 to 360 in 4052790
| function Lab_to_XYZ(Lab) { | |
| // Convert Lab to D50-adapted XYZ | |
| // http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html | |
| var κ = 24389/27; // 29^3/3^3 | |
| var ε = 216/24389; // 6^3/29^3 | |
| var f = []; | |
| // compute f, starting with the luminance-related term | |
| f[1] = (Lab[0] + 16)/116; | |
| f[0] = Lab[1]/500 + f[1]; | |
| f[2] = f[1] - Lab[2]/200; | |
| // compute xyz | |
| var xyz = [ | |
| Math.pow(f[0],3) > ε ? Math.pow(f[0],3) : (116*f[0]-16)/κ, | |
| Lab[0] > κ * ε ? Math.pow((Lab[0]+16)/116,3) : Lab[0]/κ, | |
| Math.pow(f[2],3) > ε ? Math.pow(f[2],3) : (116*f[2]-16)/κ | |
| ]; | |
| // Compute XYZ by scaling xyz by reference white | |
| return xyz.map((value, i) => value * D50[i]); | |
| } |
see: WebKit/WebKit#77513 (comment)
Upon review of the WebKit implementation I found that a significant number of issues could be traced back to this algorithm.
Might be better to already hint at this issue in the sample algorithm and to give a shape that better translates to implementations that use floats?
function Lab_to_XYZ(Lab) {
// Convert Lab to D50-adapted XYZ
// http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html
var κ = 24389/27; // 29^3/3^3
var ε = 216/24389; // 6^3/29^3
var f = [];
// Keep L out of L+16 for near-black precision
var lightness116 = Lab[0]/116;
var a500 = Lab[1]/500;
var b200 = Lab[2]/200;
// compute f, starting with the luminance-related term
f[1] = lightness116 + (16/116);
f[0] = f[1] + a500;
f[2] = f[1] - b200;
// compute xyz
var xyz = [
Math.pow(f[0],3) > ε ? Math.pow(f[0],3) : (lightness116 + a500) * (116/κ),
Lab[0] > κ * ε ? Math.pow((Lab[0]+16)/116,3) : Lab[0]/κ,
Math.pow(f[2],3) > ε ? Math.pow(f[2],3) : (lightness116 - b200) * (116/κ)
];
// Compute XYZ by scaling xyz by reference white
return xyz.map((value, i) => value * D50[i]);
}Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
Closed Accepted as Obvious BugfixFixed an obvious error with the obvious solution.Fixed an obvious error with the obvious solution.css-color-4Current WorkCurrent Work
Activity
[-][css-color-4] Update `Lab_to_XYZ` to avoid issues in implementations that use floats as internal storage.[/-][+][css-color-4] Update `Lab_to_XYZ` sample code to avoid issues in implementations that use floats as internal storage.[/+]svgeesus commentedon Oct 20, 2026
Good point: although the sample code is JavaScript, which uses F64 for the
numbertype, it could be implemented in other languages and might use F32. This change is better with F32 and makes no difference for F64, so it is a good change.