PHP GD Используйте одно изображение для маскировки другого изображения, включая прозрачность
Я пытаюсь создать PHP script, который принимает изображение:
![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 (в "PHP/GD, как скопировать круг из одного изображения в другое?" ), кажется, идет по правильной линии, но мне особенно нужно использовать изображение как маску, а не форму.
Ответы
Ответ 1
Мэтт,
Если вы сделаете свой 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;
}
?>
Ответ 2
В этом небольшом обновлении до этого script - я обнаружил, что если исходное изображение имеет прозрачность, то маска (используя script выше) отображает обратный пиксель вместо прозрачного пикселя исходного изображения. Ниже расширенный script учитывает прозрачность исходного изображения и сохраняет его.
// 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 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;
}
Ответ 3
Мне нравится ваш script, хорошая идея удалить дополнительную информацию о цвете, когда пиксель полностью прозрачен. Я должен указать лишь небольшую ошибку (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))
...
и я не уверен, что альфа-смешение из файла маски будет хорошо работать с вашим методом, так как оно используется только тогда, когда значения rgba равны 0.
Жюль script тоже хорош, хотя он ожидает, что маска будет отображением маской в виде серого цвета (что довольно распространенная практика).
В запросе Мэт он был после script, который захватывает только альфа-прозрачность из существующего изображения и применяет его к другому изображению. Здесь простой мод Jules script просто для захвата альфы из изображения маски и сохранения альфы исходного изображения.
<?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 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;
}
?>
Ответ 4
Там есть библиотека под названием WideImage, которая поддерживает альфа-маски http://wideimage.sourceforge.net/documentation/manipulating-images/
Ответ 5
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));
}
Ниже приведено небольшое обновление для первой функции. Поскольку у вас уже есть прозрачное изображение, вам не нужно копировать маскированные пиксели. Это немного облегчит выполнение.
Ответ 6
Другой способ получить аналогичный эффект - вставить файл 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 );
Если передискретизация или полупрозрачные пиксели вас сбивают, вместо использования png в качестве маски вы можете отключить смешение и нарисовать прозрачную форму на $mask
изображении.
// 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 );
Примечание. Вышеприведенный код не проверен, но, надеюсь, сделает то, что вам нужно.