Search code examples
phpimagecolorsgdpalette

PHP GD palette colors


In PHP GD how can you convert a truecolor image to a palette without losing any colors. With imagetruecolortopallete it doesn't work. I have a function that runs through all the colors and filters them (eg. grayscale). and It doesn't retain all colors, such as this picture of a Lamborghini-

Picture

alt text

This is my code

$im = imagecreatefrompng("lamborghini.png");
$degrees = 0;

$img = imagecreatetruecolor(imagesx($im), imagesy($im));
imagecopy($img, $im, 0, 0, 0, 0, imagesx($im), imagesy($im));
imagetruecolortopalette($img, true, 256);
$t = imagecolorstotal($img);
for ($i = 0; $i < $t; $i++) {
  $rgb = imagecolorsforindex($img, $i);
  $hsv =rgbtohsv($rgb['red'], $rgb['green'], $rgb['blue']);
  $h = $degrees;
  $s = $hsv['s'];
  $v = $hsv['v'];
  while ($h > 360) {$h -= 360;};
        $nrgb = hsvtorgb($h, $s, $v);
  imagecolorset($img, $i, $nrgb['r'], $nrgb['g'], $nrgb['b']);
}
imagecopy($im, $img, 0, 0, 0, 0, imagesx($img), imagesy($img));

header('Content-type: image/png');

imagepng($im);
imagedestroy($im);

And it looks like this

alt text

You can see it loses colors. Is there any solution?

Also I don't think it has to do with my code but how imagetruecolortopalette outputs it


Solution

  • Check the return on imagecolorstotal, you are always getting 256 colors as the return no matter how high you set the number of colors to be dithered to. PNG-8 & GIF formats only support palettes of up to 256 colors. So even if you can use more than 256 in a palette, you'd have to save it back as true color for anyone to be able to use it, thus making the whole conversion process a waste of time. In other words imagetruecolortopallete has an upper limit of 256 colors, you cannot go higher.

    Here's how you can do it in truecolor, though it's resource intensive. Maybe look into imagemagick if you want to do this more efficiently.

    $im = imagecreatefrompng("lamb.png");
    $img = imagecreatetruecolor(imagesx($im), imagesy($im));
    $degrees = 0;
    if ($degrees > 360) {$degrees = $degrees % 360 ;}
    
    foreach (range(0, imagesx($im) - 1) as $x ) {
        foreach (range(0, imagesy($im) - 1) as $y) {
            $rgb = imagecolorat($im, $x, $y);
            $r = ($rgb >> 16) & 0xFF;
            $g = ($rgb >> 8) & 0xFF;
            $b = $rgb & 0xFF;
            $hsv = rgbtohsv($r,$g,$b);
            $rgb = hsvtorgb($degrees, $hsv['s'], $hsv['v']);
            imagesetpixel($img, $x,$y,imagecolorallocate($img, $rgb['r'], $rgb['g'], $rgb['b']));
        }
    }
    
    imagepng($img, 'lamb2.png');
    

    Edit: Adding rgbtohsv & hsvtorgb functions. I did not write these functions.

    function rgbtohsv ($R, $G, $B) {
     // HSV Results:Number 0-1
    $HSL = array();
    
    $var_R = ($R / 255);
    $var_G = ($G / 255);
    $var_B = ($B / 255);
    
    $var_Min = min($var_R, $var_G, $var_B);
    $var_Max = max($var_R, $var_G, $var_B);
    $del_Max = $var_Max - $var_Min;
    
    $V = $var_Max;
    
    if ($del_Max == 0)
    {
    $H = 0;
    $S = 0;
    }
    else
    {
    $S = $del_Max / $var_Max;
    
    $del_R = ( ( ( $var_Max  - $var_R ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
    $del_G = ( ( ( $var_Max  - $var_G ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
    $del_B = ( ( ( $var_Max  - $var_B ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
    
    if ($var_R == $var_Max) $H = $del_B - $del_G;
    else if ($var_G == $var_Max) $H = ( 1 / 3 ) + $del_R - $del_B;
    else if ($var_B == $var_Max) $H = ( 2 / 3 ) + $del_G - $del_R;
    
    if ($H<0) $H++;
    if ($H>1) $H--;
    }
    
    $HSL['h'] = $H;
    $HSL['s'] = $S;
    $HSL['v'] = $V;
    
    return $HSL;
    }
    
    function hsvtorgb ($H, $S, $V) 
    {
    $RGB = array();
    
    if($S == 0)
    {
    $R = $G = $B = $V * 255;
    }
    else
    {
    $var_H = $H * 6;
    $var_i = floor( $var_H );
    $var_1 = $V * ( 1 - $S );
    $var_2 = $V * ( 1 - $S * ( $var_H - $var_i ) );
    $var_3 = $V * ( 1 - $S * (1 - ( $var_H - $var_i ) ) );
    
    if ($var_i == 0) { $var_R = $V ; $var_G = $var_3 ; $var_B = $var_1 ; }
    else if ($var_i == 1) { $var_R = $var_2 ; $var_G = $V ; $var_B = $var_1 ; }
    else if ($var_i == 2) { $var_R = $var_1 ; $var_G = $V ; $var_B = $var_3 ; }
    else if ($var_i == 3) { $var_R = $var_1 ; $var_G = $var_2 ; $var_B = $V ; }
    else if ($var_i == 4) { $var_R = $var_3 ; $var_G = $var_1 ; $var_B = $V ; }
    else { $var_R = $V ; $var_G = $var_1 ; $var_B = $var_2 ; }
    
    $R = $var_R * 255;
    $G = $var_G * 255;
    $B = $var_B * 255;
    }
    
    $RGB['r'] = $R;
    $RGB['g'] = $G;
    $RGB['b'] = $B;
    
    return $RGB;
    }