Transparent Circle Cropped Image with PHP

13,437

Solution 1

There is several things to note :

As @DainisAbols suggested, it should be better to take an unusual color to for your transparency. Here, you are using black :

    $red = imagecolorallocate($mask, 0, 0, 0);
    imagecopymerge($image, $mask, 0, 0, 0, 0, $this->dst_w, $this->dst_h,100);
    imagecolortransparent($image, $red);

Even if your var is called red, your R-G-B value is 0-0-0. Uncommon colors include flashy blue (0-0-255), flashy green (0-255-0), flashy yellow (255-255-0), flashy cyan (0-255-255) and flashy pink (255-0-255). Red is quite common everywhere and is not that flashy so I exclude it from those special colors.

Then, even if your images here are both true color's ones, that's a good practice to allocate a color for each image. In the example above, you create a $red variable containing black for $mask, but you're using it as transparency color in $image.

Finally, you are drawing an ellipse which have the same radius as your image size, so you need to imagefill each corners of your image and not only the top-left's one. In your example it works, but this is only because you selected black to be the transparent color.

Here is a full implementation.

<?php

class CircleCrop
{

    private $src_img;
    private $src_w;
    private $src_h;
    private $dst_img;
    private $dst_w;
    private $dst_h;

    public function __construct($img)
    {
        $this->src_img = $img;
        $this->src_w = imagesx($img);
        $this->src_h = imagesy($img);
        $this->dst_w = imagesx($img);
        $this->dst_h = imagesy($img);
    }

    public function __destruct()
    {
        if (is_resource($this->dst_img))
        {
            imagedestroy($this->dst_img);
        }
    }

    public function display()
    {
        header("Content-type: image/png");
        imagepng($this->dst_img);
        return $this;
    }

    public function reset()
    {
        if (is_resource(($this->dst_img)))
        {
            imagedestroy($this->dst_img);
        }
        $this->dst_img = imagecreatetruecolor($this->dst_w, $this->dst_h);
        imagecopy($this->dst_img, $this->src_img, 0, 0, 0, 0, $this->dst_w, $this->dst_h);
        return $this;
    }

    public function size($dstWidth, $dstHeight)
    {
        $this->dst_w = $dstWidth;
        $this->dst_h = $dstHeight;
        return $this->reset();
    }

    public function crop()
    {
        // Intializes destination image
        $this->reset();

        // Create a black image with a transparent ellipse, and merge with destination
        $mask = imagecreatetruecolor($this->dst_w, $this->dst_h);
        $maskTransparent = imagecolorallocate($mask, 255, 0, 255);
        imagecolortransparent($mask, $maskTransparent);
        imagefilledellipse($mask, $this->dst_w / 2, $this->dst_h / 2, $this->dst_w, $this->dst_h, $maskTransparent);
        imagecopymerge($this->dst_img, $mask, 0, 0, 0, 0, $this->dst_w, $this->dst_h, 100);

        // Fill each corners of destination image with transparency
        $dstTransparent = imagecolorallocate($this->dst_img, 255, 0, 255);
        imagefill($this->dst_img, 0, 0, $dstTransparent);
        imagefill($this->dst_img, $this->dst_w - 1, 0, $dstTransparent);
        imagefill($this->dst_img, 0, $this->dst_h - 1, $dstTransparent);
        imagefill($this->dst_img, $this->dst_w - 1, $this->dst_h - 1, $dstTransparent);
        imagecolortransparent($this->dst_img, $dstTransparent);

        return $this;
    }

}

Demo :

$img = imagecreatefromjpeg("test4.jpg");
$crop = new CircleCrop($img);
$crop->crop()->display();

Result :

enter image description here

Solution 2

I couln't get it done with Alain's code either. After some time understanding what each line of code does, here is my fix..

    //this creates a pink rectangle of the same size
    $mask = imagecreatetruecolor($imgwidth, $imgheight);
    $pink = imagecolorallocate($mask, 255, 0, 255);
    imagefill($mask, 0, 0, $pink);
    //this cuts a hole in the middle of the pink mask
    $black = imagecolorallocate($mask, 0, 0, 0);
    imagecolortransparent($mask, $black);
    imagefilledellipse($mask, $imgwidth/2, $imgheight/2, $imgwidth, $imgheight, $black);
    //this merges the mask over the pic and makes the pink corners transparent
    imagecopymerge($img, $mask, 0, 0, 0, 0, $imgheight, $imgheight);
    imagecolortransparent($img, $pink);
    imagepng($img, "my_circle.png");

Solution 3

You are cropping and removing black color ( or setting black as transparent ). Since your image has black color in it, it also gets removed.

Instead of removing the color, try to replace the outer layers color to, ie, pink and then set it to transparent.

Share:
13,437
Larry
Author by

Larry

Updated on June 16, 2022

Comments

  • Larry
    Larry almost 2 years

    I want to crop a circle image using PHP but it seems that my new image have some transparent pixels. Of course, I want ONLY the outside area of the ellipse to have background transparent

    My code is listed below:

            $image = imagecreatetruecolor($this->dst_w, $this->dst_h);
            imagealphablending($image,true);
            imagecopy ( $image , $image_s , 0, 0, $this->src_x, $this->src_y, $this->dst_w, $this->dst_h );
            $mask = imagecreatetruecolor($this->src_x, $this->src_y);
            $mask = imagecreatetruecolor($this->dst_w, $this->dst_h);
            $transparent = imagecolorallocate($mask, 255, 0, 0);
            imagecolortransparent($mask, $transparent);
            imagefilledellipse($mask, $this->dst_w/2, $this->dst_h/2, $this->dst_w, $this->dst_h, $transparent);
            $red = imagecolorallocate($mask, 0, 0, 0);
            imagecopymerge($image, $mask, 0, 0, 0, 0, $this->dst_w, $this->dst_h,100);
            imagecolortransparent($image, $red);
            imagefill($image,0,0, $red);
    
            if ($ext=="jpg" || $ext=="jpeg") {
                imagejpeg($image, $this->croppedImage);
            } else if ($ext=="png") {
                imagepng($image, $this->croppedImage);
            }           
            imagedestroy($image);
            imagedestroy($mask);
            // <------- END generate cropped Image ------->
    
            // <------- START generate transparent Image ------->               
            $this->generateTransparentImage('circle');
    

    ......

    An example of actual generated image is here: enter image description here

    EDIT: generateTransparentImage function has nothing to do with the code listed above; this function generate this image: http://s7.postimage.org/byybq9163/Koala7_500x375_c_transparent.png

    • hakre
      hakre over 11 years
      If you want others to play with your code, you should share the original image as well. Also the code of generateTransparentImage is hidden so others can not reproduce either.
    • hakre
      hakre over 11 years
      possible duplicate of PHP - Mask polygon over image - If you don't want to use a library, the source-code of Wideimage is free software so you can port it into your application. Otherwise, there are a lot of existing questions that show various other ways how to do that, fee free to have a look around: stackoverflow.com/search?q=%5Bphp%5D+%5Bgd%5D+mask
  • Ravi Jethva
    Ravi Jethva about 10 years
    its not transparent its give white colors in background
  • Alain Tiemblo
    Alain Tiemblo about 10 years
    hello, i just checked, and i confirm that transparency works here
  • Alain Tiemblo
    Alain Tiemblo about 10 years
    proof: home.fuz.org/tests/test-circle-crop.php (use any background color to check if the generated image is transparent)
  • Manoj Prajapat
    Manoj Prajapat about 4 years
    Good effort but not working as it should do please check generated image in Photoshop.
  • garrettlynchirl
    garrettlynchirl over 3 years
    imagecopymerge won't work in this example as the last parameter pct is missing.