Search code examples
htmlcsspsdpsd.js

How to convert color object from parsed PSD into usable RGBA


I'm parsing a PSD getting normal RGBA values and recently I encountered this strange float color values:

{
  "red": 1.0343483686447144,
  "green": 0.9487225413322449,
  "blue": -0.12634865939617157,
  "alpha": 100,
  "rgba": "rgba(1.0343483686447144, 0.9487225413322449, -0.12634865939617157, 1)"
}

Here is my parsing function:

function getRGBColor(color, opacity = 100) {
   if ("Clr " in color) {
      color = color["Clr "];
   }

   var type = color.class.id;
   var red = color["Rd  "];
   var green = color["Grn "];
   var blue = color["Bl  "];

   if ("blueFloat" in color) {
      red = color.redFloat;
      green = color.greenFloat;
      blue = color.blueFloat;
   }

   var rgba = {
      red: red, 
      green: green,
      blue: blue,
      alpha: opacity,
      rgba: `rgba(${red}, ${green}, ${blue}, ${opacity/100})`
   }

   return rgba;
}

It's supposed to be yellow.

The object is coming from strokeColorContent.

     var vectorMask = node.get("vectorMask")?.export();
     var vectorStroke = node.get("vectorStroke")?.data;
     var vectorStrokeContent = node.get("vectorStrokeContent")?.data;
     var fillColor = getRGBColor(vectorStrokeContent);

enter image description here

Related question:
How to convert PSD vector path to SVG path data

Update:
I've updated the CSS to use the lab() color space as suggested and it seems to work:

lab(94 -12 103)

I haven't confirmed the alpha value yet but this seems to work:

lab(94 -12 103 / 1)  

It seems that, in most browsers, lab() is being supported (not currently in Firefox but in Firefox 113).

To get the rgba() value there needs to be a formula from lab to rgba.


Solution

  • So apparently, the psd.js color output object confuses RGB with LAB values.

    Unfortunately, you can only convert LAB to to a more generic RGB approximation.

    You can't precisely convert LAB to RGB (without a target color space definition)

    The whole idea behind the LAB color system is to provide a system that represents all colors distinguishable by the human eye as a reference color space for all kind of color conversions.

    Workaround: generic RGB approximation:

    Provided, your psd used a more generic RGB color profile e.g similar to sRGB, you can use antimatter15's rgb-lab library

    let psdCols = {
      "red": 1.0343483686447144,
      "green": 0.9487225413322449,
      "blue": -0.12634865939617157,
      "alpha": 100,
    }
    
    
    let labRGBAVals =  [[psdCols.green,  psdCols.blue, psdCols.red].map(val=>+(val*100).toFixed(0)) ,psdCols.alpha / 100].flat();
    
    console.log('LAB values: ', labRGBAVals.join(' '));
    
    let rgba = lab2rgb(labRGBAVals).map(val=>+(val).toFixed(0) );
    
    labToRgb.style.backgroundColor = `rgba( ${rgba.join(', ')} )`
    console.log('RGB values: ', rgba)
    
    
    /**
    * based on 
    * https://github.com/antimatter15/rgb-lab/blob/master/color.js
    */
    function lab2rgb(lab){
      let y = (lab[0] + 16) / 116,
          x = lab[1] / 500 + y,
          z = y - lab[2] / 200,
          r, g, b;
    
      x = 0.95047 * ((x * x * x > 0.008856) ? x * x * x : (x - 16/116) / 7.787);
      y = 1.00000 * ((y * y * y > 0.008856) ? y * y * y : (y - 16/116) / 7.787);
      z = 1.08883 * ((z * z * z > 0.008856) ? z * z * z : (z - 16/116) / 7.787);
    
      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;
    
      r = (r > 0.0031308) ? (1.055 * Math.pow(r, 1/2.4) - 0.055) : 12.92 * r;
      g = (g > 0.0031308) ? (1.055 * Math.pow(g, 1/2.4) - 0.055) : 12.92 * g;
      b = (b > 0.0031308) ? (1.055 * Math.pow(b, 1/2.4) - 0.055) : 12.92 * b;
    
      return [Math.max(0, Math.min(1, r)) * 255, 
              Math.max(0, Math.min(1, g)) * 255, 
              Math.max(0, Math.min(1, b)) * 255]
    }
    .color {
      width: 200px;
      height: 200px;
      border: 1px solid #ccc;
    }
    
    #rgb {
      background: rgb(255, 242, 0)
    }
    <h3>rgba(255, 242, 0, 1)</h3>
    <div class="color" id="rgb"></div>
    <h3>LAB*a (95, -13, 103, 1) to RGB</h3>
    <div class="color" id="labToRgb"></div>

    Ideally, you could parse a "output-intent" (target) color profile

    E.g an .icc file and then convert LAB colors to meet the desired target color space.