Я пытаюсь создать 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 на самом деле не вариант.
Как я могу это сделать?
Сообщение phalacee (в «PHP / GD, как скопировать круг из одного изображения в другое?») похоже на правильные строки, но мне нужно использовать изображение как маску, а не форму.
Мэтт,
Если вы сделаете свой 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; } ?>
Вот небольшое обновление для этого скрипта – я обнаружил, что если исходное изображение имеет прозрачность, маска (используя вышеприведенный скрипт) отображает обратный пиксель вместо прозрачного пикселя исходного изображения. Приведенный ниже расширенный сценарий учитывает прозрачность исходного изображения и сохраняет его.
// 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)) ...
и я не уверен, что альфа-смешение из файла маски будет хорошо работать с вашим методом, так как оно используется только тогда, когда значения rgba равны 0.
Сценарий Жюля тоже очень хорош, хотя он ожидает, что маска будет представлять собой маску масок (это довольно распространенная практика).
В запросе Мэтта он был после сценария, который захватывает только альфа-прозрачность из существующего изображения и применяет его к другому изображению. Вот простой мод сценария Жюля, чтобы захватить альфу из изображения маски и сохранить альфа исходного изображения.
<?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, которая поддерживает альфа-маски 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)); }
Ниже приведено небольшое обновление для первой функции. Поскольку у вас уже есть прозрачное изображение, вам не нужно копировать маскированные пиксели. Это немного облегчит выполнение.
Другой способ получить аналогичный эффект – вставить файл 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 );
Примечание. Вышеприведенный код не проверен, но, надеюсь, сделает то, что вам нужно.