Search code examples
powershellrgbhsl

Powershell: HSL to RGB


I need to convert HSL color values to RGB, or to be more precise HSL-values to a System.Drawing.Color object with Powershell. There are a few solutions in other prog.-languages out there (like LINK). But while it looks simple, I dont get it converted it into Powershell.

Function HSLtoRGB ($H,$S,$L) {
    $H = [double]($H / 360)
    $S = [double]($S / 100)
    $L = [double]($L / 100)

     if ($s -eq 0) {
        $r = $g = $b = $l
     }
    else {
        if ($l -lt 0.5){
           $q = $l * (1 + $s) 
        } 
        else {
          $q =  $l + $s - $l * $s
        }
        $p = (2 * $L) - $q
        $r = (Hue2rgb $p $q ($h + 1/3))
        $g = (Hue2rgb $p $q $h )
        $b = (Hue2rgb $p $q ($h - 1/3))
    }

     $r = [Math]::Round($r * 255)
    $g = [Math]::Round($g * 255)
    $b = [Math]::Round($b * 255)

return ($r,$g,$b)
}


function Hue2rgb ($p, $q, $t) {
    if ($t -lt 0) { $t++ }
    if ($t -gt 0) { $t-- }
    if ($t -lt 1/6) { return ( $p + ($q + $p) * 6 * $t ) }
    if ($t -lt 1/2) { return $q }    
    if ($t -lt 2/3) { return ($p + ($q - $p) * (2/3 - $t) * 6 ) }
     return $p
}


HSLtoRGB 63 45 40       #  result should be R 145  G 148  B 56

Solution

  • Let's start with the line you're having trouble with translating:

    $q =    l < 0.5 ? l * (1 + s) : l + s - l * s;    #could not translate this line
    

    This construct:

    statement ? someValue : anotherValue;
    

    is known as a ternary operation. It basically means:

    if(statement){
        someValue
    } else {
        anotherValue
    }
    

    So in PowerShell that becomes:

    $q = if($l -lt 0.5){
        $l * (1 + $s) 
    } else {
        $l + $s - $l * $s
    }
    

    Your translation of the inline Hue2Rgb function has two typos that greatly change the calculation:

    function Hue2rgb ($p, $q, $t) {
        if ($t -lt 0) { $t++ }
        if ($t -gt 0) { $t-- } # This condition should be ($t -gt 1)
        if ($t -lt 1/6) { return ( $p + ($q + $p) * 6 * $t ) } # The innermost calculation should be ($q - $p) not ($q + $p)
        if ($t -lt 1/2) { return $q }    
        if ($t -lt 2/3) { return ($p + ($q - $p) * (2/3 - $t) * 6 ) }
        return $p
    }
    

    Regarding the input values, if you take a look at the comments in the original script:

    * Assumes h, s, and l are contained in the set [0, 1] and
    * returns r, g, and b in the set [0, 255].
    

    So if you want to pass your input values as degrees (hue) and percentages (saturation + luminance), you'll have to handle a conversion to a relative value between 0 and 1:

    Function HSLtoRGB ($H,$S,$L) {
        $H = [double]($H / 360)
        $S = [double]($S / 100)
        $L = [double]($L / 100)
    
        # rest of script
    }
    

    Lastly, you can use Color.FromArgb() to return an actual Color object:

    $r = [Math]::Round($r * 255)
    $g = [Math]::Round($g * 255)
    $b = [Math]::Round($b * 255)
    
    return [System.Drawing.Color]:FromArgb($r,$g,$b)