Search code examples
phppngtransparencygdalpha-transparency

imagecopymerge does not work correctly with partially transparent images


I have an image that has opacity:

enter image description here

When I try load it with:

$img = imagecreatefrompng('cheek-01.png');
header('Content-Type: image/png');
imagepng($img);

it will become:

enter image description here

I want to add (merge) this image with another image (I know how can merge two png images). What is the problem and how can I load it with GD library without losing opacity?

Update

My goal is merge some images where some of them have alpha (different opacity, like the above image), but when I merge them the alpha information is lost.

<?php
$background = imagecreatetruecolor(3508, 2480);
// set background to white
$white = imagecolorallocate($background, 255, 255, 255);
imagefill($background, 0, 0, $white);
$dest = $background;
$items = array(
    'cloth-1763-1249' => 'cloth-01.png',
    'skin-167-59' => "skin-01.png",
    'cheek-2247-1193' => 'cheek-09.png',
    'hair-167-59' => 'hair-07.png'
);
foreach ($items as $i => $item) {
    $src = imagecreatefrompng($item);
    imagealphablending($src, true);
    imagesavealpha($src, true);
    imagecolortransparent($src, 2130706432);
    $src_x = imagesx($src);
    $src_y = imagesy($src);
    $list = explode('-', $i);
    //var_dump($list);
    imagecopymerge($dest, $src, intval($list[1]), intval($list[2]), 0, 0, $src_x, $src_y, 100);
    imagealphablending($dest, true);
    imagesavealpha($dest, true);
}
header('Content-Type: image/png');
imagepng($dest);

Solution

  • You need to enable alpha blending and imagecopymerge was never designed to respect the alpha channel (people tried to fix this back in 2009, but the PHP devs ignored this and said it was intended behaviour). According to the PHP documentation comment section this should work.

    <?php 
      /** 
      * PNG ALPHA CHANNEL SUPPORT for imagecopymerge(); 
      * by Sina Salek 
      * 
      * Bugfix by Ralph Voigt (bug which causes it 
      * to work only for $src_x = $src_y = 0. 
      * Also, inverting opacity is not necessary.) 
      * 08-JAN-2011 
      * 
      **/ 
      function imagecopymerge_alpha($dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct){ 
        // creating a cut resource 
        $cut = imagecreatetruecolor($src_w, $src_h); 
    
        // copying relevant section from background to the cut resource 
        imagecopy($cut, $dst_im, 0, 0, $dst_x, $dst_y, $src_w, $src_h); 
    
        // copying relevant section from watermark to the cut resource 
        imagecopy($cut, $src_im, 0, 0, $src_x, $src_y, $src_w, $src_h); 
    
        // insert cut resource to destination image 
        imagecopymerge($dst_im, $cut, $dst_x, $dst_y, 0, 0, $src_w, $src_h, $pct); 
      }
      // ...Do your previous stuff...
      imagecopymerge_alpha(....);
    
      // do the other stuff
    
      /* Output image to browser */
      header('Content-type: image/png');
      imagepng($imgPng); 
    ?>