0

I want to convert an RGBA value to HEX in PHP. I have found good help for RGB to HEX (e.g. Convert RGB to hex color values in PHP). Now I have made an attempt to change the A value as well (e.g rgba(80,80,80,0.5) to maybe #D3D3D3 (6 digits)). Assuming, of course, that the background is white.

Below the attempt. Does anyone have any tips on how I could do this better? How do I get a matching colour but lighter?

public static function convertRGBAtoHEX6(string $rgba): string
{

    if ( strpos( $rgba, '#' ) === 0 ) {
        return $rgba;
    }

    preg_match( '/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i', $rgba, $by_color );
    
    if(isset($by_color[4])) 
    {
        $by_color[4] = 2 - $by_color[4];

        $by_color[1] = $by_color[1] * $by_color[4];
        $by_color[2] = $by_color[2] * $by_color[4];
        $by_color[3] = $by_color[3] * $by_color[4];
    }

    return sprintf( '#%02x%02x%02x', $by_color[1], $by_color[2], $by_color[3] );

}
  • You cannot do it. You should keep A in output, or get information of the background colour. The alpha channel define how to mix foreground colour with background colour. – Giacomo Catenazzi Aug 25 '23 at 08:38
  • The transparency channel is treated in the same way as the RGB channels. You get a [8-Digit Hex Code](https://css-tricks.com/8-digit-hex-codes/). – KIKO Software Aug 25 '23 at 08:42
  • You sort of can do this. https://caniuse.com/css-rrggbbaa – Markus Zeller Aug 25 '23 at 08:48
  • @KIKOSoftware I know that, but this is not supported in emails. So I just look for a color as similar as possible if the background were white. – Gratia-Mira Aug 25 '23 at 09:41

1 Answers1

1

I came up with this solution:

<?php

$rgba = 'rgba(80,80,80,0.5)';

function blendChannels(float $alpha, int $channel1, int $channel2): int
{
    // blend 2 channels
    return intval(($channel1 * $alpha) + ($channel2 * (1.0 - $alpha)));
}

function convertRGBAtoHEX6(string $rgba): string
{
    // sanitize
    $rgba = strtolower(trim($rgba));
    // check
    if (substr($rgba, 0, 5) != 'rgba(') {
        return $rgba;
    }
    // extract channels
    $channels = explode(',', substr($rgba, 5, strpos($rgba, ')') - 5));
    // compute rgb with white background
    $alpha = $channels[3];
    $r = blendChannels($alpha, $channels[0], 0xFF);
    $g = blendChannels($alpha, $channels[1], 0xFF);
    $b = blendChannels($alpha, $channels[2], 0xFF);
    return sprintf('#%02x%02x%02x', $r, $g, $b);
}

echo convertRGBAtoHEX6($rgba);

See: https://3v4l.org/ick2o

The result is: "#a7a7a7".

The code is basically the same as yours with the exception of the blendChannels() function and I got rid of that horrible regular expression.

The blendChannels() function takes the needed amount from the two supplied channels. If alpha is 1.0 the output is equal to channel 1, if alpha is 0.0 the outout is equal to channel 2, and if alpha is 0.5 an equal amount is taken from both channels. Do this for all 3 channels and you're done.

As mentioned by Sammitch, in a comment, the sRGB color space is not linear. To get the best results the blendChannels() function should take this into account:

function blendChannels(float $alpha, int $channel1, int $channel2): int
{
    $gamma = 2.2;
    // blend 2 channels
    return intval(pow((pow($channel1,  $gamma) * $alpha) + 
                      (pow($channel2,  $gamma) * (1.0 - $alpha)), 1 /  $gamma));
}

You can change the gamma to whatever you need.

You can see the difference between the various calculations best as a gradient, in this case blending black and white with a variable alpha value.

enter image description here

The difference between Linear and Quadratic is fairly stark, and demonstrates part of the reason for this approach, in that our eye distinguishes subtle differences better in brighter colors than darker. The Gamma correction is quite subtle in the gradient, but would likely be more apparent when applied to images.

Sammitch
  • 30,782
  • 7
  • 50
  • 77
KIKO Software
  • 15,283
  • 3
  • 18
  • 33
  • 1
    Note: RGB is not a linear color space, each value is the square root of the actual color value, so the calculation should be `intval(sqrt((pow($channel1, 2) * $alpha) + (pow($channel2, 2) * (1.0 - $alpha))))`, otherwise the colors may look slightly "off" – Sammitch Aug 25 '23 at 18:59
  • 1
    @Sammitch; Yes, [you are right](https://stackoverflow.com/questions/12524623/what-are-the-practical-differences-when-working-with-colors-in-a-linear-vs-a-no). This would create a visually more accurate result for the sRGB color space. Doing it with [a gamma of 2.2 or 2.4](https://en.wikipedia.org/wiki/SRGB) is even better. – KIKO Software Aug 25 '23 at 20:20
  • Oooh, TIL. Thx. – Sammitch Aug 25 '23 at 21:19