merlincam
merlincam

Reputation: 1948

How can I replace one color with another in a png 24 alpha transparent image with GD

I have tried:

$index = imagecolorresolve ( $im,  0,0,0 ); // get black
imagecolorset($im, $index, 255, 0, 255); // SET NEW COLOR

This seems to work with png 8 but not 24, and if I do it with 8 it turns out all weird because of the anti-aliasing.

Here is the full test code I'm using. (this is just test code, so be gentle).

function LoadPNG($imgname, $color = false)
{        
    $im = @imagecreatefrompng($imgname);
    imagealphablending($im, false); 

    if($color) {
      $index = imagecolorresolve ( $im,  0,0,0 ); // get black
      imagecolorset($im, $index, 255, 0, 255); // SET NEW COLOR
    }

    imageAlphaBlending($im, true);
    imageSaveAlpha($im, true);

    return $im;
}

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

$img = LoadPNG("head.png", "red");

imagepng($img);
imagedestroy($img);

Upvotes: 2

Views: 12252

Answers (5)

A good way to doing it is using paintOpaqueImage(), which also permit using color tolerance

$targetColor = "#0074AD";
$fill = "#0074AA";
$tolerance = 30000;

$im = new Imagick( "yourimage.png");
if ($im->paintOpaqueImage ( $targetColor , $fill , $tolerance) ){

    $im->writeImage("yourimage.png"); 

}

You can see the tolenrance doc in http://www.imagemagick.org/script/command-line-options.php#fuzz

Upvotes: 2

tony gil
tony gil

Reputation: 9564

this function will replace either 1 or all colors for 1 new color, maintaining transparency levels (otherwise borders will probably look awful, if PARTIAL transparency was used to draw borders).

COMPLETE ANSWER TO SIMILAR POST

<?php 

function colorizeKeepAplhaChannnel( $inputFilePathIn, $targetRedIn, $targetGreenIn, $targetBlueIn, $outputFilePathIn ) {
    $im_src = imagecreatefrompng( $inputFilePathIn );
    $im_dst = imagecreatefrompng( $inputFilePathIn );
    $width = imagesx($im_src);
    $height = imagesy($im_src);

    // Note this: FILL IMAGE WITH TRANSPARENT BG
    imagefill($im_dst, 0, 0, IMG_COLOR_TRANSPARENT);
    imagesavealpha($im_dst,true);
    imagealphablending($im_dst, true);

    $flagOK = 1;
    for( $x=0; $x<$width; $x++ ) {
        for( $y=0; $y<$height; $y++ ) {
            $rgb = imagecolorat( $im_src, $x, $y );
            $colorOldRGB = imagecolorsforindex($im_src, $rgb);
            $alpha = $colorOldRGB["alpha"];
            $colorNew = imagecolorallocatealpha($im_src, $targetRedIn, $targetGreenIn, $targetBlueIn, $alpha);

            $flagFoundColor = true;
            // uncomment next 3 lines to substitute only 1 color (in this case, BLACK/greys)
/*
            $colorOld = imagecolorallocatealpha($im_src, $colorOldRGB["red"], $colorOldRGB["green"], $colorOldRGB["blue"], 0); // original color WITHOUT alpha channel
            $color2Change = imagecolorallocatealpha($im_src, 0, 0, 0, 0); // opaque BLACK - change to desired color
            $flagFoundColor = ($color2Change == $colorOld);
*/

            if ( false === $colorNew ) {
                //echo( "FALSE COLOR:$colorNew alpha:$alpha<br/>" );
                $flagOK = 0; 
            } else if ($flagFoundColor) {
                imagesetpixel( $im_dst, $x, $y, $colorNew );
                //echo "x:$x y:$y col=$colorNew alpha:$alpha<br/>";
            } 
        }
    }
    $flagOK2 = imagepng($im_dst, $outputFilePathIn);

    if ($flagOK && $flagOK2) {
        echo ("<strong>Congratulations, your conversion was successful </strong><br/>new file $outputFilePathIn<br/>");
    } else if ($flagOK2 && !$flagOK) {
        echo ("<strong>ERROR, your conversion was UNsuccessful</strong><br/>Please verify if your PNG is truecolor<br/>input file $inputFilePathIn<br/>");
    } else if (!$flagOK2 && $flagOK) {
        $dirNameOutput = dirname($outputFilePathIn)."/";
        echo ("<strong>ERROR, your conversion was successful, but could not save file</strong><br/>Please verify that you have PERMISSION to save to directory $dirName <br/>input file $inputFilePathIn<br/>");
    } else {
        $dirNameOutput = dirname($outputFilePathIn)."/";
        echo ("<strong>ERROR, your conversion was UNsuccessful AND could not save file</strong><br/>Please verify if your PNG is truecolor<br/>Please verify that you have PERMISSION to save to directory $dirName <br/>input file $inputFilePathIn<br/>");
    }

    echo ("TargetName:$outputFilePathIn wid:$width height:$height CONVERTED:|$flagOK| SAVED:|$flagOK2|<br/>");
    imagedestroy($im_dst);
    imagedestroy($im_src);
}

$targetRed = 255;
$targetGreen = 255;
$targetBlue = 0;

//$inputFileName = 'frameSquareBlack_88x110.png';
$inputFileName = 'testMe.png';
$dirName = "../img/profilePics/";
$nameTemp = basename($inputFileName, ".png");
$outputFileName = $nameTemp."_$targetRed"."_$targetGreen"."_$targetBlue.png";
$inputFilePath = $dirName.$inputFileName;
$outputFilePath = $dirName.$outputFileName;

//echo "inputFileName:$inputFilePath<br>outputName:$outputFilePath<br>";
colorizeKeepAplhaChannnel( $inputFilePath, $targetRed, $targetGreen, $targetBlue, $outputFilePath);
?>
<br/><br/>
Original <br/>
<img src="<?php echo $inputFilePath; ?>">
<br /><br />Colorized<br/>
<img src="<?php echo $outputFilePath; ?>">
<br />

Upvotes: 2

Amed
Amed

Reputation: 321

This function work like a charm :

public function ImageToBlackAndWhite($im) {
    for ($x = imagesx($im); $x--;) {
        for ($y = imagesy($im); $y--;) {
            $rgb = imagecolorat($im, $x, $y);
            $r = ($rgb >> 16) & 0xFF;
            $g = ($rgb >> 8 ) & 0xFF;
            $b = $rgb & 0xFF;
            $gray = ($r + $g + $b) / 3;
            if ($gray < 0xFF) {

                imagesetpixel($im, $x, $y, 0xFFFFFF);
            }else
                imagesetpixel($im, $x, $y, 0x000000);
        }
    }
    imagefilter($im, IMG_FILTER_NEGATE);

}

Upvotes: 1

Benjamin Crouzier
Benjamin Crouzier

Reputation: 41925

Based on the solution of inti, i made a script that works:

$imgname = "yourimage.png";
$im = imagecreatefrompng($imgname);
imagealphablending($im, false);
for ($x = imagesx($im); $x--;) {
    for ($y = imagesy($im); $y--;) {
        $rgb = imagecolorat($im, $x, $y);
        $c = imagecolorsforindex($im, $rgb);
        if ($c['red'] < 40 && $c['green'] < 40 && $c['blue'] < 40) { // dark colors
            // here we use the new color, but the original alpha channel
            $colorB = imagecolorallocatealpha($im, 255, 0, 255, $c['alpha']);
            imagesetpixel($im, $x, $y, $colorB);
        }
    }
}
imageAlphaBlending($im, true);
imageSaveAlpha($im, true);
header('Content-Type: image/png');
imagepng($im);
imagedestroy($im);

I'd like a way to optimize it, because it is quite slow

Upvotes: 9

aorcsik
aorcsik

Reputation: 15552

You can try the following:

  • cycle all points
  • get the color of that point
  • if it matches your colorA, set that pixel to the desired colorB

Code:

for ($x=imagesx($im); $x--; ) {
    for ($y=imagesy($im); $y--; ) {
        $c = imagecolorat($im, $x, $y);
        if ($c[0] == 0 && $c[1] == 0 && $c[2] == 0) {
            // here we use the new color, but the original alpha channel
            $colorB = imagecolorallocatealpha($im, 255, 0, 255, $c[3]);
            imagesetpixel($im, $x, $y, $colorB);
        }
    }
}

Hope this helps!

Upvotes: 4

Related Questions