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

  1. 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");
  2. 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);
  3. 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);

  4. Copy $mask over the top of $copy maintaining the Alpha transparency
    imagecopymerge($copy, $mask, 0, 0, 0, 0, $w, $h, 100);
  5. 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);
  6. 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');

You may also like...

6 Responses

  1. image masking says:

    Great bit of information there. Thanks.

  2. Mark says:

    I am trying to solve a similar problem only with polygons rather than circles… Your method is great until someone uses “Magic Pink” in their image… Any ideas of a universal solution?



  3. Jason says:

    You can always try using a for loop to go through an array of unlikely colours and use imagecolorexact() to find out if any of them are in use, and when you find one that isn’t use it instead of “Magic Pink”

  4. george says:

    Can you provide the code for trapezium instead of circle?

  5. George: I will try to do this. I don’t have a lot of free time to play with code at the moment, but all it really requires is drawing the shape and then filling it with magic pink. There are examples of that on the website

  6. Robert says:

    I didn’t analyzed it but here is a similar realization:

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.