Converting CSS Lch to RGB

1.7k views Asked by At

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?

3

There are 3 answers

0
Patrick J. S. On BEST ANSWER

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

1
Guénaël Dequeker On

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

0
Benjamin Frugoni On

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);
  }