Search code examples
phpimagegdphp-gd

PHP GD Complex stacking multiple layers


I have an image stacking process that I am trying to accomplish with PHP GD. What I have is the following:

3 Images:

  1. Masked clipart
  2. Clipart texture
  3. Final background

My masked clipart has black in place of where transparency will be after the texture is applied to the clipart and is transparent for layering over a texture.

The following code sorta works for this:

$im = imagecreatetruecolor($width,$height);
imagecopy($im, $texture, 0, 0, 0, 0, $width, $height);
imagecopy($im, $clipart, 0, 0, 0, 0, $width, $height);
imagecolortransparent($im, imagecolorclosest($clipart, 0, 0, 0));

Output is an image with transparent background and the clipart with the texture applied.

However, when I open that file in photoshop, the transparent areas are black which also brings me to the rest of this function:

Now that I have this image, I need to layer it on top of the "Final background" image which will make all the transparency of the previous output now be this "Final background" texture. My thoughts were something like:

$im = imagecreatetruecolor($width,$height);
imagecopy($im, $texture, 0, 0, 0, 0, $width, $height);
imagecopy($im, $clipart, 0, 0, 0, 0, $width, $height);    
imagecolortransparent($im, imagecolorclosest($clipart, 0, 0, 0));

$img = imagecreatetruecolor($width,$height);
imagecopy($img, $background, 0, 0, 0, 0, $width, $height);
imagecopy($img, $im, 0, 0, 0, 0, $width, $height);
imagedestroy($im);

return $img;

The problem with this is that is outputs the image with a black background instead of my final texture layer. I believe this code for the final layering may actually work fine and that the black background from the final output and in photoshop is from missing some alpha line in the first part. I have tried to play around with:

imagealphablending( $im, false );
imagesavealpha( $im, true );

Mixing and matching the true / false and alternating only using one of them didn't seem to matter.

If anyone could shed some light on my mistakes here, it would be greatly appreciated.


UPDATE

The images: https://i.sstatic.net/XyDo1.jpg

The code:

// Layer clipart over texture and convert black to transparent (works)
$im = imagecreatetruecolor($width,$height);
imagecopy($im, $texture, 0, 0, 0, 0, $width, $height);
imagecopy($im, $clipart, 0, 0, 0, 0, $width, $height);    
imagecolortransparent($im, imagecolorclosest($clipart, 0, 0, 0));

// Layer above image with transparency over background (non-working)
$img = imagecreatetruecolor($width,$height);
imagecopy($img, $background, 0, 0, 0, 0, $width, $height);
imagecopy($img, $im, 0, 0, 0, 0, $width, $height);

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

//imagepng($im); // Correctly outputs first step
imagejpeg($img); // Incorrectly outputs final result
imagedestroy($im);
imagedestroy($img);

Solution

  • As noted on the imagecolortransparent() manual page:

    Transparency is copied only with imagecopymerge() and true color images, not with imagecopy() or pallete images.

    So, changing the following two lines (16 & 17 in index.php)...

    imagecopy($img, $background, 0, 0, 0, 0, $width, $height);
    imagecopy($img, $im, 0, 0, 0, 0, $width, $height);
    

    ...to this...

    imagecopymerge($img, $background, 0, 0, 0, 0, $width, $height, 100);
    imagecopymerge($img, $im, 0, 0, 0, 0, $width, $height, 100);
    

    ...gives (very close to) the desired result:

    Result