I wanted to convert a CSS Lch color string like:
--my-color: lch(20% 8.5 220.0);
To an RGB hex code. I tried using tools like chroma's Lch parser, but all of them seem to use absolute values for the first ordinate (the 20% in my case).
Is there some standard way to convert that 20% into the lightness values used by most Lch conversion tools?
I wanted to convert a CSS Lch color string like:
--my-color: lch(20% 8.5 220.0);
To an RGB hex code. I tried using tools like chroma's Lch parser, but all of them seem to use absolute values for the first ordinate (the 20% in my case).
Is there some standard way to convert that 20% into the lightness values used by most Lch conversion tools?
Share Improve this question edited Sep 17, 2020 at 8:20 mkrieger1 23.5k7 gold badges64 silver badges82 bronze badges asked Sep 17, 2020 at 0:58 user2662833user2662833 1,0731 gold badge12 silver badges21 bronze badges 2- first w3/TR/css-color-4/#lch-to-lab then w3/TR/css-color-4/#lab-to-rgb – Kaiido Commented Sep 17, 2020 at 1:26
- You can just scale, if you have different ranges. BTW, it is not a good question writing "used by most ...". We have no idea on which tools you consider (and your field). You should always specify range and colour space (in question, but remember to write it also in the code, or next time it will take huge amount of time to find out errors and your definition). – Giao Catenazzi Commented Sep 17, 2020 at 7:19
4 Answers
Reset to default 4A JS-native CanvasRenderingContext2D instance can read any CSS-formatted color (as a string) and write it in a RGBA buffer (0 to 255) for you :
const myCssColor = "lch(20% 8.5 220.0)";
function cssColor_to_rgba255Color(string) {
const canvas = document.createElement("canvas");
canvas.width = canvas.height = 1;
const ctx = canvas.getContext("2d", {willReadFrequently: true});
ctx.fillStyle = string;
ctx.fillRect(0, 0, 1, 1);
return ctx.getImageData(0, 0, 1, 1).data;
}
const rgba = cssColor_to_rgba255Color(myCssColor);
console.log( rgba[0], rgba[1], rgba[2], rgba[3] );
// 33 51 56 255
This function may have poor performance but can be greatly optimized by always recycling (using clearRect) the same "ctx" object.
The w3c is working on a draft and there is example code in javascript attached (it's a bit too long to post here and you'll need higher math to do that (at least sin
and the power of 2.4
).
I can give you this function that I wrote for myself from LCH to HEX, or RGB if you want. I wrote it in PHP, so it's easy to adapt to any other language
function lch2hex($l, $c, $h) {
$a=round($c*cos(deg2rad($h)));
$b=round($c*sin(deg2rad($h)));
unset($c,$h);
// Reference white values for D65 Light Europe Observer
// $xw = 0.95047;
// $yw = 1.00000;
// $zw = 1.08883;
// Reference white values for CIE 1964 10° Standard Observer
$xw = 0.948110;
$yw = 1.00000;
$zw = 1.07304;
// Compute intermediate values
$fy = ($l + 16) / 116;
$fx = $fy + ($a / 500);
$fz = $fy - ($b / 200);
// Compute XYZ values
$x = round($xw * (($fx ** 3 > 0.008856) ? $fx ** 3 : (($fx - 16 / 116) / 7.787)),5);
$y = round($yw * (($fy ** 3 > 0.008856) ? $fy ** 3 : (($fy - 16 / 116) / 7.787)),5);
$z = round($zw * (($fz ** 3 > 0.008856) ? $fz ** 3 : (($fz - 16 / 116) / 7.787)),5);
unset($l,$a,$b,$xw,$yw,$zw,$fy,$fx,$fz);
$r = $x * 3.2406 - $y * 1.5372 - $z * 0.4986;
$g = -$x * 0.9689 + $y * 1.8758 + $z * 0.0415;
$b = $x * 0.0557 - $y * 0.2040 + $z * 1.0570;
unset($x,$y,$z);
$r = $r > 0.0031308 ? 1.055 * pow($r, 1 / 2.4) - 0.055 : 12.92 * $r;
$g = $g > 0.0031308 ? 1.055 * pow($g, 1 / 2.4) - 0.055 : 12.92 * $g;
$b = $b > 0.0031308 ? 1.055 * pow($b, 1 / 2.4) - 0.055 : 12.92 * $b;
$r = round(max(min($r, 1), 0) * 255);
$g = round(max(min($g, 1), 0) * 255);
$b = round(max(min($b, 1), 0) * 255);
return '#' . sprintf('%02X%02X%02X', $r, $g, $b);
}
You can now use OffscreenCanvas to create the canvas context:
function cssColor_to_rgba255Color(string) {
const ctx = new OffscreenCanvas(1, 1).getContext('2d')
ctx.fillStyle = string;
ctx.fillRect(0, 0, 1, 1);
return ctx.getImageData(0, 0, 1, 1).data;
}
const input = document.querySelector('input')
const span = document.querySelector('span')
input.addEventListener('input', updateOutput)
updateOutput()
function updateOutput() {
span.textContent = cssColor_to_rgba255Color(input.value).join(' ')
}
<input value="lch(20% 8.5 220.0)" />
<br/>
<br/>
RGBA: <span></span>