PHP的GD使用一个图像来掩盖另一个图像,包括透明度

我正在尝试创build一个需要映像的PHP脚本:

此搜索
http://i.stack.imgur.com/eNvlM.png

然后应用一个PNG图像:

面具
http://i.stack.imgur.com/iJr2I.png

作为面具。

最终结果需要保持透明度:

结果
http://i.stack.imgur.com/u0l0I.png

如果可能的话,我想在GD中做到这一点,ImageMagick现在不是一个真正的select。

我将如何去做这件事?

phalacee的post(在“PHP / GD,如何从一个图像复制到另一个圆?”)似乎沿着正确的线条,但我特别需要使用图像作为面具,而不是一个形状。

马特,

如果你用黑色背景上的椭圆形白色填充而不是透明背景的黑色填充,你可以使用下面的函数。

<?php // Load source and mask $source = imagecreatefrompng( '1.png' ); $mask = imagecreatefrompng( '2.png' ); // Apply mask to source imagealphamask( $source, $mask ); // Output header( "Content-type: image/png"); imagepng( $source ); function imagealphamask( &$picture, $mask ) { // Get sizes and set up new picture $xSize = imagesx( $picture ); $ySize = imagesy( $picture ); $newPicture = imagecreatetruecolor( $xSize, $ySize ); imagesavealpha( $newPicture, true ); imagefill( $newPicture, 0, 0, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) ); // Resize mask if necessary if( $xSize != imagesx( $mask ) || $ySize != imagesy( $mask ) ) { $tempPic = imagecreatetruecolor( $xSize, $ySize ); imagecopyresampled( $tempPic, $mask, 0, 0, 0, 0, $xSize, $ySize, imagesx( $mask ), imagesy( $mask ) ); imagedestroy( $mask ); $mask = $tempPic; } // Perform pixel-based alpha map application for( $x = 0; $x < $xSize; $x++ ) { for( $y = 0; $y < $ySize; $y++ ) { $alpha = imagecolorsforindex( $mask, imagecolorat( $mask, $x, $y ) ); $alpha = 127 - floor( $alpha[ 'red' ] / 2 ); $color = imagecolorsforindex( $picture, imagecolorat( $picture, $x, $y ) ); imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, $color[ 'red' ], $color[ 'green' ], $color[ 'blue' ], $alpha ) ); } } // Copy back to original picture imagedestroy( $picture ); $picture = $newPicture; } ?> 

这里是对这个脚本的一个小升级 – 我发现如果源图像本身具有透明度,则使用上面的脚本绘制一个背部像素,而不是源图像的透明像素。 下面的扩展脚本考虑了源图像的透明度,并保留它。

 // Load source and mask $source = imagecreatefrompng( '1.png' ); $mask = imagecreatefrompng( '2.png' ); // Apply mask to source imagealphamask( $source, $mask ); // Output header( "Content-type: image/png"); imagepng( $source ); function imagealphamask( &$picture, $mask ) { // Get sizes and set up new picture $xSize = imagesx( $picture ); $ySize = imagesy( $picture ); $newPicture = imagecreatetruecolor( $xSize, $ySize ); imagesavealpha( $newPicture, true ); imagefill( $newPicture, 0, 0, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) ); // Resize mask if necessary if( $xSize != imagesx( $mask ) || $ySize != imagesy( $mask ) ) { $tempPic = imagecreatetruecolor( $xSize, $ySize ); imagecopyresampled( $tempPic, $mask, 0, 0, 0, 0, $xSize, $ySize, imagesx( $mask ), imagesy( $mask ) ); imagedestroy( $mask ); $mask = $tempPic; } // Perform pixel-based alpha map application for( $x = 0; $x < $xSize; $x++ ) { for( $y = 0; $y < $ySize; $y++ ) { $alpha = imagecolorsforindex( $mask, imagecolorat( $mask, $x, $y ) ); if(($alpha['red'] == 0) && ($alpha['green'] == 0) && ($alpha['blue'] == 0) && ($alpha['alpha'] == 0)) { // It's a black part of the mask imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) ); // Stick a black, but totally transparent, pixel in. } else { // Check the alpha state of the corresponding pixel of the image we're dealing with. $alphaSource = imagecolorsforindex( $source, imagecolorat( $source, $x, $y ) ); if(($alphaSource['alpha'] == 127)) { imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) ); // Stick a black, but totally transparent, pixel in. } else { $color = imagecolorsforindex( $source, imagecolorat( $source, $x, $y ) ); imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, $color[ 'red' ], $color[ 'green' ], $color[ 'blue' ], $color['alpha'] ) ); // Stick the pixel from the source image in } } } } // Copy back to original picture imagedestroy( $picture ); $picture = $newPicture; } 

我喜欢你的脚本,当像素是完全透明的时候,去掉多余的颜色信息是个好主意。 如果有人想使用这种方法,我应该指出一个小错误(IMO)。

 $color = imagecolorsforindex( $source, imagecolorat( $source, $x, $y ) ); 

应该

 $color = imagecolorsforindex( $picture, imagecolorat( $picture, $x, $y ) ); 

我也不是100%确定为什么你在这里检查rgb值,如果像素是100%透明

 if(($alpha['red'] == 0) && ($alpha['green'] == 0) && ($alpha['blue'] == 0) && ($alpha['alpha'] == 0)) ... 

而且我不确定掩码文件中的alpha混合是否适合您的方法,因为只有当rgba值全部等于0时才使用它。

Jules的脚本也相当不错,不过它希望mask是一个掩码的灰度表示(这是很常见的做法)。

在Matt的查询中,他是在一个脚本后面抓取现有图像的alpha透明度并将其应用于另一个图像。 这里有一个简单的Jules脚本,只是为了从屏蔽图像中获取alpha,并保留源图像的alpha值。

 <?php // Load source and mask $source = imagecreatefrompng( '1.png' ); $mask = imagecreatefrompng( '2.png' ); // Apply mask to source imagealphamask( $source, $mask ); // Output header( "Content-type: image/png"); imagepng( $source ); function imagealphamask( &$picture, $mask ) { // Get sizes and set up new picture $xSize = imagesx( $picture ); $ySize = imagesy( $picture ); $newPicture = imagecreatetruecolor( $xSize, $ySize ); imagesavealpha( $newPicture, true ); imagefill( $newPicture, 0, 0, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) ); // Resize mask if necessary if( $xSize != imagesx( $mask ) || $ySize != imagesy( $mask ) ) { $tempPic = imagecreatetruecolor( $xSize, $ySize ); imagecopyresampled( $tempPic, $mask, 0, 0, 0, 0, $xSize, $ySize, imagesx( $mask ), imagesy( $mask ) ); imagedestroy( $mask ); $mask = $tempPic; } // Perform pixel-based alpha map application for( $x = 0; $x < $xSize; $x++ ) { for( $y = 0; $y < $ySize; $y++ ) { $alpha = imagecolorsforindex( $mask, imagecolorat( $mask, $x, $y ) ); //small mod to extract alpha, if using a black(transparent) and white //mask file instead change the following line back to Jules's original: //$alpha = 127 - floor($alpha['red'] / 2); //or a white(transparent) and black mask file: //$alpha = floor($alpha['red'] / 2); $alpha = $alpha['alpha']; $color = imagecolorsforindex( $picture, imagecolorat( $picture, $x, $y ) ); //preserve alpha by comparing the two values if ($color['alpha'] > $alpha) $alpha = $color['alpha']; //kill data for fully transparent pixels if ($alpha == 127) { $color['red'] = 0; $color['blue'] = 0; $color['green'] = 0; } imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, $color[ 'red' ], $color[ 'green' ], $color[ 'blue' ], $alpha ) ); } } // Copy back to original picture imagedestroy( $picture ); $picture = $newPicture; } ?> 

有一个名为WideImage的库,支持alpha蒙版http://wideimage.sourceforge.net/documentation/manipulating-images/

 for ($y = 0; $y < $ySize; $y++) { $alpha = imagecolorsforindex($mask, imagecolorat($mask, $x, $y)); $alpha = 127 - floor($alpha['red'] / 2); if (127 == $alpha) { continue; } $color = imagecolorsforindex($picture, imagecolorat($picture, $x, $y)); imagesetpixel($newPicture, $x, $y, imagecolorallocatealpha( $newPicture, $color['red'], $color['green'], $color['blue'], $alpha)); } 

这是第一个function的小升级。 由于您已经有一个透明的图像,您不需要复制蒙版像素。 这将有助于执行一点。