title: PHP GD How to circular crop 3 square images and merge into 1 image maintaining transparency
body: <p>I have 2 source images and I want to:</p>
  
  <ol>
  <li>Do a circular crop of each image, with the outside of the circle  transparent</li>
  <li>Merge/copy all images back onto a destination transparent image.</li>
  </ol>
  
  <p>I have tried <a href="https://stackoverflow.com/questions/12953262/transparent-circle-cropped-image-with-php">many examples</a>, but can't seem to maintain transparency on the final image.</p>
  
  <p>I'm trying to achieve something like this:
  <img src="https://i.stack.imgur.com/Xgzrk.png" alt="enter image description here"></p>
  
  <p>This is an example of the output I am getting:
  <img src="https://i.stack.imgur.com/TbCsx.png" alt="enter image description here"></p>
  
  <p>Here's my circle_crop function:</p>
  
  <pre><code>    function create_circle( $img_path ) {
      // Attribution: by NerdsOfTech
  
      // Step 1 - Start with image as layer 1 (canvas).
      if (! $img1 = $this-&gt;imageCreateFromAny( $img_path )) {
          return FALSE;
      }
  
      $x=imagesx($img1);
      $y=imagesy($img1);
  
  
      // Step 2 - Create a blank image.
      $img2 = imagecreatetruecolor($x, $y);
  
      $bg = imagecolorallocate($img2, 255,0,255, 127); // wierdo pink background
      // $bg = imagecolorallocate($img2, 0, 0, 0, 127 ); // white background
  
      imagefill($img2, 0, 0, $bg);
      imagecolortransparent($img2, $bg);
  
      // Step 3 - Create the ellipse OR circle mask.
      $e = imagecolorallocate($img2, 255, 255, 255); // black mask color
  
      // Draw a ellipse mask
      imagefilledellipse ($img2, ($x/2), ($y/2), $x, $y, $e);
  
      // OR
      // Draw a circle mask
      // $r = $x &lt;= $y ? $x : $y; // use smallest side as radius &amp; center shape
      // imagefilledellipse ($img2, ($x/2), ($y/2), $r, $r, $e);
  
      // Step 4 - Make shape color transparent
      imagecolortransparent($img2, $e);
  
      // Step 5 - Merge the mask into canvas with 100 percent opacity
      imagecopymerge($img1, $img2, 0, 0, 0, 0, $x, $y, 100);
  
      // Step 6 - Make outside border color around circle transparent
      imagecolortransparent($img1, $bg);
  
      /* Clean up memory */
      imagedestroy($img2);
  
      return $img1;
  }
  </code></pre>
  
  <p>Here's the code I pass in an array of image URL's and loop through calling the circle_crop function to return a cropped image and merge it onto my destination image.</p>
  
  <pre><code>function generate_collage( $img_name_path_array, $effect = 'POLAROID' ) {
      $base_img_width = 800;
      $base_img_height = 650;
  
      if (empty($img_name_path_array)) {
          error_log('Image name_path_array is blank?'.PHP_EOL);
          return FALSE;
      }
      $effect = strtoupper($effect);
  
      /* Create canvas */
      $collage_img = imagecreatetruecolor($base_img_width, $base_img_height);
      imagealphablending($collage_img, false);
      imagesavealpha($collage_img,true);
      /* Create alpha channel for transparent layer */
      $trans_col=imagecolorallocatealpha($collage_img,255,255,255, 127);
      /* Create overlapping transparent layer */
      imagefilledrectangle($collage_img,0,0,$base_img_width,$base_img_height,$trans_col);
  
      /* Continue to keep layers transparent */
      imagealphablending($collage_img,true);
  
      $size_reduction = .80;
      $start_size = 100;
  
  
      foreach ($img_name_path_array as $image_array ) {
          $img_text = $image_array[0];
          $img_path = $image_array[1];
          if (! empty($img_path)) {
              switch ($effect) {
                  /* Add other collage image effects here */
                  case 'POLAROID' : {
                      $temp_img = $this-&gt;create_polaroid($img_path, $img_text, TRUE);
                      break;
                  }
                  case 'CIRCLES' : {
                      // $temp_img = $this-&gt;circle_crop($img_path);
                      $temp_img = $this-&gt;create_circle($img_path);
                      break;
                  }
                  default : {
                      /* Default to polaroid for now */
                      $temp_img = $this-&gt;create_polaroid($img_path, $img_text, TRUE);
                      break;
                  }
              }
  
              if ($temp_img) {
                  /* Get original height and width paramaters */
                  $source_w = imagesx($temp_img);
                  $source_h = imagesy($temp_img);
  
                  /* Randomise X and Y coordinates */
                  $random_x_pos = rand(0, (int) ($base_img_width * .66));
                  $random_y_pos = rand(0, (int) ($base_img_height * .3));
  
                  /* Randomise image size */
                  $start_size = ($start_size * $size_reduction);
                  $random_img_size_ratio = $start_size / 100;
  
                  /* Add generated image to base collage image */
                  imagecopyresampled($collage_img, $temp_img, $random_x_pos, $random_y_pos, 0, 0, ($base_img_width * $random_img_size_ratio), ($base_img_height * $random_img_size_ratio), $source_w, $source_h);
  
                  imagecolortransparent($collage_img, $trans_col);
                  /* Keep transparent when saving */
                  imagesavealpha($collage_img,true);
  
                  /* Memory clean up */
                  imagedestroy($temp_img);
  
                  // break;
              }
          }
      }
  
  
      /* Now display PNG to browser */
      $this-&gt;show_png_from_image_object($collage_img);
  }
  </code></pre>
  
  <p>Here's my display function:</p>
  
  <pre><code>function show_png_from_image_object( $img_obj ) {
      header ( 'Content-Type: image/png' );
  
      /* Display PNG with max compression */
      imagepng ( $img_obj, NULL,  9, PNG_ALL_FILTERS);
      imagedestroy ( $img_obj );
  }
  </code></pre>
  
  <p>I've pulled my hair out for 2 days, so any pointers would be greatly appreciated.</p>
  
  <p>Thanks, Jason.</p>