PHP GD Use one image to mask another image, including transparency PHP GD Use one image to mask another image, including transparency php php

PHP GD Use one image to mask another image, including transparency


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.

<?php// Load source and mask$source = imagecreatefrompng( '1.png' );$mask = imagecreatefrompng( '2.png' );// Apply mask to sourceimagealphamask( $source, $mask );// Outputheader( "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;}?>


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 sourceimagealphamask( $source, $mask );// Outputheader( "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 necessaryif( $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 applicationfor( $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 pictureimagedestroy( $picture );$picture = $newPicture;}


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.

$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

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.

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).

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.

<?php// Load source and mask$source = imagecreatefrompng( '1.png' );$mask = imagecreatefrompng( '2.png' );// Apply mask to sourceimagealphamask( $source, $mask );// Outputheader( "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;}?>