Creating ‘mask layers’ using PHP GD
This morning I came across a question on stackoverflow that puzzled me a bit:
Is there a reasonably straightforward way to copy a circular area from one image resource to another? Something like imagecopymerge except with circles or ovals etc?
If possible, I want to avoid having to use pre-created image files (any oval shape should be possible), and if there’s transparency colours involved they should naturally leave the rest of the image alone.Reason I’m asking, I have a few classes that allow to apply image operations inside a “selected area” of an image, which works by first deleting that area from a copy of the image, then overlaying the copy back on the original. But what if you want to select a rectangle, and then inside that deselect a circle, and have the operations only affect the area that’s left?
I’ve done it a thousand times in Photoshop, but never using PHP. So I set about coming up with a solution (reformatted from my original post on stackoverflow.):
- Start with original image – $img
$img = imagecreatefromjpeg("./original.jpg"); $img_magicpink = imagecolorallocatealpha($img, 255, 0, 255, 127); // (Get its dimensions for copying) list($w, $h) = getimagesize("./original.jpg"); - Copy that image to a png – $copy
$copy = imagecreatefromjpeg("./original.jpg"); imagealphablending($copy, true); $copy_magicpink = imagecolorallocate($copy, 255, 0, 255); imagecolortransparent($copy, $copy_magicpink); - Create a mask png image of the area you want in the circle/ellipse (a ‘magicpink’ image with a black shape on it, with black set to the colour of alpha transparency) – $mask
$mask = imagecreatetruecolor($w, $h); imagealphablending($mask, true); // Set the masking colours $mask_black = imagecolorallocate($mask, 0, 0, 0); $mask_magicpink = imagecolorallocate($mask, 255, 0, 255); imagecolortransparent($mask, $mask_black); imagefill($mask, 0, 0, $mask_magicpink); // Draw the circle for the mask $circle_x = $w/2; $circle_y = $h/2; $circle_w = 150; $circle_h = 150; imagefilledellipse($mask, $circle_x, $circle_y, $circle_w, $circle_h, $mask_black); - Copy $mask over the top of $copy maintaining the Alpha transparency
imagecopymerge($copy, $mask, 0, 0, 0, 0, $w, $h, 100); - Change what you need to on $copy
// My example is turning the original image gray and leaving the masked area as colour $x = imagesx($img); $y = imagesy($img); $gray = imagecreatetruecolor($x, $y); imagecolorallocate($gray, 0, 0, 0); for ($i = 0; $i < $x; $i++) { for ($j = 0; $j < $y; $j++) { $rgb = imagecolorat($img, $i, $j); $r = ( $rgb >> 16 ) & 0xFF; $g = ( $rgb >> 8 ) & 0xFF; $b = $rgb & 0xFF; //for gray mode $r = $g = $b $color = max(array($r, $g, $b)); $gray_color = imagecolorexact($img, $color, $color, $color); imagesetpixel($gray, $i, $j, $gray_color); } } - Copy $copy back over $img maintaining the Alpha transparency
imagecopymergegray($gray, $copy, 0, 0, 0, 0, $w, $h, 100); imagealphablending($gray, true); imagecolortransparent($gray, $mask_magicpink); header('Content-Type: image/png'); imagepng($gray); imagedestroy($gray);

3 Comments
Jump to comment form | comments rss [?]