PHP GD使用一个图像来屏蔽另一个图像,包括透明度

时间:2011-08-26 10:08:32

标签: php gd

我正在尝试创建一个带有图像的PHP脚本:

image1
http://i.stack.imgur.com/eNvlM.png

然后应用PNG图像:

mask
http://i.stack.imgur.com/iJr2I.png

作为面具。

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

result
http://i.stack.imgur.com/u0l0I.png

如果可能的话我想在GD中这样做,ImageMagick现在不是一个真正的选择。

我该怎么做?

phalacee's post (in "PHP/GD, how to copy a circle from one image to another?")似乎沿着正确的方向行,但我特别需要使用图像作为遮罩,而不是形状。

5 个答案:

答案 0 :(得分:51)

马特,

如果你在黑色背景上使用椭圆形白色填充而不是使用透明背景的黑色填充,则可以使用以下功能。

<?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;
}

?>

答案 1 :(得分:11)

这是对此脚本的一点升级 - 我发现如果源图像本身具有透明度,则掩码(使用上面的脚本)绘制后像素而不是源图像的透明像素。下面的扩展脚本会考虑源图像透明度,并保留它。

// 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;
}

答案 2 :(得分:10)

我喜欢你的脚本,当像素完全透明时,最好删除额外的颜色信息。如果有人想要使用这种方法,我应该指出一个小错误(IMO)。

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

应该是

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

如果像素100%透明,我也不能100%确定为什么要检查rgb值

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

并且我不确定掩码文件中的alpha混合是否适用于您的方法,因为它仅在rgba值等于0时使用。

Jules的剧本也很不错,但它希望蒙版是面具的灰度表示(这是很常见的做法)。

在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;
}

?>

答案 3 :(得分:3)

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

答案 4 :(得分:1)

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));
}

这是第一个功能的一点升级。由于您已有透明图像,因此无需复制蒙版像素。这将有助于执行一点。