skip to Main Content

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

}

2

Answers


  1. Actually i made this task small time ago.

    If we get color as 255,255,255,0.123
    So we create reg to check /(((25[0-5])|(2[0-4]d)|(1?d{0,2})|0), *){3}(0|(0.d{1,3})|1)$/

    So we can make encode and decode functions

    function encode(string $colorString)
    {
        $colorArray = explode(',', $colorString);
        $colorArray[3] *= 255;
        $colorBytes = array_map(
            fn($part) =>  pack('C', (int)trim($part)),
            $colorArray
        );
        return implode($colorBytes);
    }
    function decode($hexColor)
    {
        //$hexColor = stream_get_contents($hexColor); // If $hexColor is stream
        $colorArray = array_values(unpack('C*', $hexColor));
        return implode(
            ',',
            [$colorArray[0], $colorArray[1], $colorArray[2], round($colorArray[3]/255, 3)]
        );
    }
    

    I save so my colors in a DB. When I need to save more colors I write it in one hex string and when I decode I divide it into chunk of 4 bytes

    Example I have [‘255,123,125,1’, ‘32,123,23,0.15’]

    foreach (['255,123,125,1', '32,123,23,0.15'] as $color) {
        $x = $this->encode($color);
        print('#' . strtoupper(bin2hex($x)) . "n");
    }
    

    result will:

    #FF7B7DFF
    #207B1726
    

    Try this, may be it make target on your solution

    Login or Signup to reply.
  2. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search