4

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

Larry
  • 441
  • 4
  • 7
  • 20
  • 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 Oct 18 '12 at 11:18
  • possible duplicate of [PHP - Mask polygon over image](http://stackoverflow.com/questions/1544621/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: http://stackoverflow.com/search?q=%5Bphp%5D+%5Bgd%5D+mask – hakre Oct 18 '12 at 11:26

3 Answers3

8

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

Community
  • 1
  • 1
Alain Tiemblo
  • 36,099
  • 17
  • 121
  • 153
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");
Dhex
  • 21
  • 1
1

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.

Peon
  • 7,902
  • 7
  • 59
  • 100