PHP GD 使用一张图片遮住另一张图片,包括透明度
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7203160/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
PHP GD Use one image to mask another image, including transparency
提问by Matt
I am trying to create a PHP script that takes an image:
我正在尝试创建一个获取图像的 PHP 脚本:
http://i.stack.imgur.com/eNvlM.png
http://i.stack.imgur.com/eNvlM.png
and then applies a PNG image:
然后应用一个 PNG 图像:
http://i.stack.imgur.com/iJr2I.png
http://i.stack.imgur.com/iJr2I.png
as a mask.
作为面具。
The end result needs to maintain transparency:
最终结果需要保持透明度:
http://i.stack.imgur.com/u0l0I.png
http://i.stack.imgur.com/u0l0I.png
If at all possible I want to do this in GD, ImageMagick is not really an option right now.
如果可能的话,我想在 GD 中执行此操作,那么 ImageMagick 现在并不是一个真正的选择。
How would I go about this?
我该怎么办?
phalacee's post (in "PHP/GD, how to copy a circle from one image to another?")seems to be along the right lines but I specifically need to use an image as a mask, not a shape.
phalacee 的帖子(在“PHP/GD,如何将一个圆从一个图像复制到另一个?”)似乎是正确的,但我特别需要使用图像作为蒙版,而不是形状。
回答by Jules_Text
Matt,
马特,
If you make your png with the oval white fill on black background instead of black fill with transparent background the following function does it.
如果您在黑色背景上使用椭圆形白色填充制作 png,而不是使用透明背景制作黑色填充,则以下功能会执行此操作。
<?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;
}
?>
回答by user1436297
Here's a little upgrade to this script - I found that if the source image has transparency itself, the mask (using the script above) plots a back pixel instead of the source image's transparent pixel. The below extended script takes source image transparency into consideration, and preserves it.
这是对该脚本的一些升级 - 我发现如果源图像本身具有透明度,则遮罩(使用上面的脚本)会绘制后像素而不是源图像的透明像素。下面的扩展脚本考虑了源图像的透明度,并保留了它。
// 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;
}
回答by niall.campbell
I like your script, good idea to remove extra colour information when the pixel is totally transparent. I should point out just a small error (IMO) though if anyone wants to use this method.
我喜欢你的脚本,当像素完全透明时删除额外的颜色信息是个好主意。如果有人想使用这种方法,我应该指出一个小错误(IMO)。
$color = imagecolorsforindex( $source, imagecolorat( $source, $x, $y ) );
should be
应该
$color = imagecolorsforindex( $picture, imagecolorat( $picture, $x, $y ) );
also I'm not 100% sure why you're checking rgb values here if the pixel is 100% transparent
如果像素是 100% 透明的,我也不是 100% 确定为什么要在这里检查 rgb 值
if(($alpha['red'] == 0) && ($alpha['green'] == 0) && ($alpha['blue'] == 0) && ($alpha['alpha'] == 0))
...
and I'm not sure alpha blending from the mask file would work well with your method, as its only used when rgba values all equal 0.
而且我不确定来自掩码文件的 alpha 混合是否适用于您的方法,因为它仅在 rgba 值都等于 0 时使用。
Jules's script is pretty good too, though it expects the mask to be a grey scale representation of a mask (which is pretty common practice).
Jules 的脚本也很不错,尽管它希望蒙版是蒙版的灰度表示(这是很常见的做法)。
In Matt's query he was after a script that grabs just the alpha transparency from an existing image and applies it to another image. Here's a simple mod of Jules's script just to grab the alpha from the mask image, and preserve the alpha of the source image.
在 Matt 的查询中,他使用了一个脚本,该脚本仅从现有图像中获取 alpha 透明度并将其应用于另一个图像。这是 Jules 脚本的一个简单 mod,只是为了从蒙版图像中获取 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;
}
?>
回答by itsjavi
There's a library called WideImage that supports alpha masks http://wideimage.sourceforge.net/documentation/manipulating-images/
有一个名为 WideImage 的库支持 alpha 蒙版http://wideimage.sourceforge.net/documentation/manipulating-images/
回答by Gardner
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));
}
Here is a little upgrade for the first function. As you already have a transparent image, you don't need to copy the masked pixels. This will help the execution a little.
这是第一个功能的小升级。由于您已经拥有透明图像,因此无需复制蒙版像素。这将有助于执行。
回答by Shaun Cockerill
A different way to get a similar effect is to paste the png file onto a new image with a unique background colour to temporarily remove transparency, and set the transparent colour of the png image to the black circle colour instead. Then when you place it over the jpeg image, you set the new transparent colour to the colour of the mask.
获得类似效果的另一种方法是将 png 文件粘贴到具有唯一背景颜色的新图像上以暂时去除透明度,并将 png 图像的透明颜色设置为黑色圆圈颜色。然后当你把它放在 jpeg 图像上时,你将新的透明颜色设置为蒙版的颜色。
// Load the Black Circle PNG image
$png = imagecreatefrompng( 'mask.png' );
$width = imagesx( $png );
$height = imagesy( $png );
// Create a mask image
$mask = imagecreatetruecolor( $width, $height );
// We'll use Magenta as our new transparent colour - set it as the solid background colour.
$magenta = imagecolorallocate( $mask, 255, 0, 255 );
imagefill( $mask, 0, 0, $magenta );
// Copy the png image onto the mask. Destroy it to free up memory.
imagecopyresampled( $mask, $png, 0, 0, 0, 0, $width, $height, $width, $height );
imagedestroy( $png );
// Set the black portion of the mask to transparent.
$black = imagecolorallocate( $mask, 0, 0, 0 );
imagecolortransparent( $mask, $black );
// Load JPEG image.
$jpg = imagecreatefromjpeg( 'image.jpg' );
$j_width = imagesx( $jpg );
$j_height = imagesx( $jpg );
// Enable alpha blending and copy the png image
imagealphablending( $jpg, true );
imagecopyresampled( $jpg, $mask, 0, 0, 0, 0, $j_width, $j_height, $width, $height );
imagedestroy( $mask );
// Set the new transparent colour and output new image to browser as a png.
$magenta = imagecolorallocate( $jpg, 255, 0, 255 );
imagecolortransparent( $jpg, $magenta );
imagepng( $jpg );
If resampling or semi-transparent pixels are getting you down, instead of using a png as a mask, you can disable blending and draw a transparent shape onto $mask
image instead.
如果重采样或半透明像素让您失望,您可以禁用混合并在$mask
图像上绘制透明形状,而不是使用 png 作为蒙版。
// Load JPEG Image.
$jpg = imagecreatefromjpeg( 'image.jpg' );
$width = imagesx( $jpg );
$height = imagesx( $jpg );
// Create mask at same size with an opaque background.
$mask = imagecreatetruecolor( $width, $height );
$magenta = imagecolorallocate( $mask, 255, 0, 255 );
imagefill( $mask, 0, 0, $magenta );
// Disable alpha blending and draw a transparent shape onto the mask.
$transparent = imagecolorallocatealpha( $mask, 255, 255, 255, 127 );
imagealphablending( $mask, false );
imagefilledellipse( $mask, round( $width / 2 ), round( $height / 2 ), $width, $height, $transparent );
// Paste the mask onto the original image and set the new transparent colour.
imagealphablending( $jpg, true );
imagecopyresampled( $jpg, $mask, 0, 0, 0, 0, $width, $height, $width, $height );
imagedestroy( $mask );
$magenta = imagecolorallocate( $jpg, 255, 0, 255 );
imagecolortransparent( $jpg, $magenta );
// Output new image to browser as a png.
imagepng( $jpg );
Note: The above code is untested, but should hopefully do what you need it to.
注意:上面的代码未经测试,但应该可以满足您的需求。