112

The following PHP code snippet uses GD to resize a browser-uploaded PNG to 128x128. It works great, except that the transparent areas in the original image are being replaced with a solid color- black in my case.

Even though imagesavealpha is set, something isn't quite right.

What's the best way to preserve the transparency in the resampled image?

$uploadTempFile = $myField[ 'tmp_name' ]
list( $uploadWidth, $uploadHeight, $uploadType ) 
  = getimagesize( $uploadTempFile );

$srcImage = imagecreatefrompng( $uploadTempFile );    
imagesavealpha( $targetImage, true );

$targetImage = imagecreatetruecolor( 128, 128 );
imagecopyresampled( $targetImage, $srcImage, 
                    0, 0, 
                    0, 0, 
                    128, 128, 
                    $uploadWidth, $uploadHeight );

imagepng(  $targetImage, 'out.png', 9 );
George
  • 36,413
  • 9
  • 66
  • 103
Cheekysoft
  • 35,194
  • 20
  • 73
  • 86

11 Answers11

214
imagealphablending( $targetImage, false );
imagesavealpha( $targetImage, true );

did it for me. Thanks ceejayoz.

note, the target image needs the alpha settings, not the source image.

Edit: full replacement code. See also answers below and their comments. This is not guaranteed to be be perfect in any way, but did achieve my needs at the time.

$uploadTempFile = $myField[ 'tmp_name' ]
list( $uploadWidth, $uploadHeight, $uploadType ) 
  = getimagesize( $uploadTempFile );

$srcImage = imagecreatefrompng( $uploadTempFile ); 

$targetImage = imagecreatetruecolor( 128, 128 );   
imagealphablending( $targetImage, false );
imagesavealpha( $targetImage, true );

imagecopyresampled( $targetImage, $srcImage, 
                    0, 0, 
                    0, 0, 
                    128, 128, 
                    $uploadWidth, $uploadHeight );

imagepng(  $targetImage, 'out.png', 9 );
Brooks
  • 2,082
  • 2
  • 18
  • 26
Cheekysoft
  • 35,194
  • 20
  • 73
  • 86
  • 6
    FIY, this needs to be after the target image has been created. In this case, it would be after imagecreatetruecolor. – RisingSun Feb 25 '13 at 07:03
  • Wondering why is `alphablending = false` important here? [The doc states it](http://www.php.net/manual/en/function.imagesavealpha.php)... "You have to unset alphablending (`imagealphablending($im, false)`), to use it." – eightyfive Jun 27 '14 at 09:50
  • 4
    Not only is this answer correct and useful, it is particularly helpful because the first comment (at the time of writing) on the PHP docs for `imagecreatefrompng()` suggests that `imagealphablending` should be set `true`, which is clearly wrong. Thanks muchly. – spikyjt Jul 23 '14 at 09:09
  • imagesavealpha() should already be enough. no need to use imagealphablending on most of the time, especially if you will use imagefttext() later on – fedmich Jul 26 '14 at 05:52
  • 3
    This solution, in my case GD works fine ONLY if PNG has a "regular" transparency area, like a surrounding transparent area, if it has a complex area, like with inner parts of the image with transparency, it always fails and puts black background, for example this image fails: http://www.seomofo.com/downloads/new-google-logo-knockoff.png. Can anybody try this and confirm? – aesede Sep 29 '14 at 15:01
  • @aesede The image you gave works 100% for me using PHP 5.4 - haven't tested with other versions. – Floris Mar 30 '15 at 19:10
  • Wow. imagealphablending( $targetImage, false ); imagesavealpha( $targetImage, true ); Just these 2 statements solved my issue. Thanks Cheekysoft... – Raj Jul 13 '15 at 12:16
  • 3
    Does not seem to work with some transparent png files. I tried to create a image from a jpg and copy a transparent png inside. As aesede points out the result is a black square. – RubbelDeCatc Oct 11 '15 at 11:58
  • Best/shortest solution I've found for PNG images. Anti-aliasing keeps preserved. The best/easiest solution for GIF is the one just below with: imagecolortransparent($new_im, imagecolorallocate($new_im, 0, 0, 0)); – Jonny Apr 18 '17 at 12:12
  • 1
    I had a pretty complicated script with cropping, rotating, and resizing using several times imagecopyresampled. Once I added before each of those steps the simples lines imagealphablending( $targetImage, false ); imagesavealpha( $targetImage, true ); the PNGs remain all perfect transparent they way they come. Works prefect and is easy. – Oliver Dec 17 '19 at 17:19
  • @Oliver perhaps you meant after – Lepy May 23 '22 at 08:17
21

Why do you make things so complicated? the following is what I use and so far it has done the job for me.

$im = ImageCreateFromPNG($source);
$new_im = imagecreatetruecolor($new_size[0],$new_size[1]);
imagecolortransparent($new_im, imagecolorallocate($new_im, 0, 0, 0));
imagecopyresampled($new_im,$im,0,0,0,0,$new_size[0],$new_size[1],$size[0],$size[1]);
  • Didn't work, I still get black background with this image: http://www.seomofo.com/downloads/new-google-logo-knockoff.png – aesede Sep 29 '14 at 15:15
  • Was trying out both solutions: yours and the one above with over 150 votes. Your solutions works great for GIFs. The above one works best with PNG files while your solution is loosing anti-aliasing what you can see best on creating thumbnails (looks blocky, pixelated). – Jonny Apr 18 '17 at 12:04
11

I believe this should do the trick:

$srcImage = imagecreatefrompng($uploadTempFile);
imagealphablending($srcImage, false);
imagesavealpha($srcImage, true);

edit: Someone in the PHP docs claims imagealphablending should be true, not false. YMMV.

ceejayoz
  • 176,543
  • 40
  • 303
  • 368
  • 5
    Using `imagealphablending` either with true or false I always get a black background. – aesede Sep 29 '14 at 15:17
  • PHP7 - Working for me – Yehonatan Jan 25 '17 at 10:59
  • 1
    Played around with it (PHP 7.x): PNG: imagealphablending($targetImage, false); // if true on PNGs: black background GIF: imagealphablending($targetImage, true); // if false on GIFs: black background – Jonny Apr 18 '17 at 12:07
8

An addition that might help some people:

It is possible to toggle imagealphablending while building the image. I the specific case that I needed this, I wanted to combine some semi-transparent PNG's on a transparent background.

First you set imagealphablending to false and fill the newly created true color image with a transparent color. If imagealphablending were true, nothing would happen because the transparent fill would merge with the black default background and result in black.

Then you toggle imagealphablending to true and add some PNG images to the canvas, leaving some of the background visible (ie. not filling up the entire image).

The result is an image with a transparent background and several combined PNG images.

Jorrit Schippers
  • 1,538
  • 12
  • 17
6

I have made a function for resizing image like JPEG/GIF/PNG with copyimageresample and PNG images still keep there transparency:

$myfile=$_FILES["youimage"];

function ismyimage($myfile) {
    if((($myfile["type"] == "image/gif") || ($myfile["type"] == "image/jpg") || ($myfile["type"] == "image/jpeg") || ($myfile["type"] == "image/png")) && ($myfile["size"] <= 2097152 /*2mb*/) ) return true; 
    else return false;
}

function upload_file($myfile) {         
    if(ismyimage($myfile)) {
        $information=getimagesize($myfile["tmp_name"]);
        $mywidth=$information[0];
        $myheight=$information[1];

        $newwidth=$mywidth;
        $newheight=$myheight;
        while(($newwidth > 600) || ($newheight > 400 )) {
            $newwidth = $newwidth-ceil($newwidth/100);
            $newheight = $newheight-ceil($newheight/100);
        } 

        $files=$myfile["name"];

        if($myfile["type"] == "image/gif") {
            $tmp=imagecreatetruecolor($newwidth,$newheight);
            $src=imagecreatefromgif($myfile["tmp_name"]);
            imagecopyresampled($tmp, $src, 0, 0, 0, 0, $newwidth, $newheight, $mywidth, $myheight);
            $con=imagegif($tmp, $files);
            imagedestroy($tmp);
            imagedestroy($src);
            if($con){
                return true;
            } else {
                return false;
            }
        } else if(($myfile["type"] == "image/jpg") || ($myfile["type"] == "image/jpeg") ) {
            $tmp=imagecreatetruecolor($newwidth,$newheight);
            $src=imagecreatefromjpeg($myfile["tmp_name"]); 
            imagecopyresampled($tmp, $src, 0, 0, 0, 0, $newwidth, $newheight, $mywidth, $myheight);
            $con=imagejpeg($tmp, $files);
            imagedestroy($tmp);
            imagedestroy($src);
            if($con) {  
                return true;
            } else {
                return false;
            }
        } else if($myfile["type"] == "image/png") {
            $tmp=imagecreatetruecolor($newwidth,$newheight);
            $src=imagecreatefrompng($myfile["tmp_name"]);
            imagealphablending($tmp, false);
            imagesavealpha($tmp,true);
            $transparent = imagecolorallocatealpha($tmp, 255, 255, 255, 127);
            imagefilledrectangle($tmp, 0, 0, $newwidth, $newheight, $transparent); 
            imagecopyresampled($tmp, $src, 0, 0, 0, 0, $newwidth, $newheight, $mywidth, $myheight);
            $con=imagepng($tmp, $files);
            imagedestroy($tmp);
            imagedestroy($src);
            if($con) {
                return true;
            } else {
                return false;
            }
        }   
    } else
          return false;
}
Manoj Sharma
  • 1,467
  • 2
  • 13
  • 20
pricopz
  • 71
  • 1
  • 1
  • 3
    It's rather onerous to read through all the code to figure out why transparency is preserved in this code over the code in the question. – eh9 Nov 09 '12 at 18:29
  • I skipped these two lines and it still worked: `$transparent = imagecolorallocatealpha($tmp, 255, 255, 255, 127);` `imagefilledrectangle($tmp, 0, 0, $newwidth, $newheight, $transparent);` – santiago arizti Apr 13 '17 at 20:06
  • This answer looks a lot like https://stackoverflow.com/a/279310/470749 . – Ryan Feb 22 '20 at 17:52
4

I suppose that this might do the trick:

$uploadTempFile = $myField[ 'tmp_name' ]
list( $uploadWidth, $uploadHeight, $uploadType ) 
  = getimagesize( $uploadTempFile );

$srcImage = imagecreatefrompng( $uploadTempFile );

$targetImage = imagecreatetruecolor( 128, 128 );

$transparent = imagecolorallocate($targetImage,0,255,0);
imagecolortransparent($targetImage,$transparent);
imagefilledrectangle($targetImage,0,0,127,127,$transparent);

imagecopyresampled( $targetImage, $srcImage, 
                    0, 0, 
                    0, 0, 
                    128, 128, 
                    $uploadWidth, $uploadHeight );

imagepng(  $targetImage, 'out.png', 9 );

The downside is that the image will be stripped of every 100% green pixels. Anyhow, hope it helps :)

Linus Unnebäck
  • 23,234
  • 15
  • 74
  • 89
  • If you set to an extremely ugly color that almost no image would use it can be very helpful. – Cory Nov 15 '10 at 17:18
  • 2
    The accepted answer didn't work for me. Using this answer with `imagecreate(...)` worked though. You create an image, which get's filled with the first color you allocate. Then you set that color to transparent. If alphablending is set to true for the target image, both images will be merged and the transparency works correctly. – Sumurai8 Oct 22 '13 at 11:10
1

Regrading the preserve transparency, then yes like stated in other posts imagesavealpha() have to be set to true, to use the alpha flag imagealphablending() must be set to false else it doesn't work.

Also I spotted two minor things in your code:

  1. You don't need to call getimagesize() to get the width/height for imagecopyresmapled()
  2. The $uploadWidth and $uploadHeight should be -1 the value, since the cordinates starts at 0 and not 1, so it would copy them into an empty pixel. Replacing it with: imagesx($targetImage) - 1 and imagesy($targetImage) - 1, relativily should do :)
gmadd
  • 1,146
  • 9
  • 18
Kalle
  • 11
  • 1
0

Here is my total test code. It works for me

$imageFileType = pathinfo($_FILES["image"]["name"], PATHINFO_EXTENSION);
$filename = 'test.' . $imageFileType;
move_uploaded_file($_FILES["image"]["tmp_name"], $filename);

$source_image = imagecreatefromjpeg($filename);

$source_imagex = imagesx($source_image);
$source_imagey = imagesy($source_image);

$dest_imagex = 400;
$dest_imagey = 600;
$dest_image = imagecreatetruecolor($dest_imagex, $dest_imagey);

imagecopyresampled($dest_image, $source_image, 0, 0, 0, 0, $dest_imagex, $dest_imagey, $source_imagex, $source_imagey);

imagesavealpha($dest_image, true);
$trans_colour = imagecolorallocatealpha($dest_image, 0, 0, 0, 127);
imagefill($dest_image, 0, 0, $trans_colour);

imagepng($dest_image,"test1.png",1);
0

Pay attention to the source image's width and height values which are passed to imagecopyresampled function. If they are bigger than actual source image size, the rest of image area will be filled with black color.

nsinvocation
  • 7,559
  • 3
  • 41
  • 46
0

I combined the answers from ceejayoz and Cheekysoft, which gave the best result for me. Without imagealphablending() and imagesavealpha() the image is not clear:

$img3 = imagecreatetruecolor(128, 128);
imagecolortransparent($img3, imagecolorallocate($img3, 0, 0, 0));
imagealphablending( $img3, false );
imagesavealpha( $img3, true );
imagecopyresampled($img3, $srcImage, 0, 0, 0, 0, 128, 128, $uploadWidth, $uploadHeight);
imagepng($img3, 'filename.png', 9);
Marco
  • 3,470
  • 4
  • 23
  • 35
0

For anyone having problems with imagecopyresampled or imagerotate with black bars on background, I have found a code example here:

https://qna.habr.com/q/646622#answer_1417035

        // get image sizes (X,Y)
        $wx = imagesx($imageW);
        $wy = imagesy($imageW);

        // create a new image from the sizes on transparent canvas
        $new = imagecreatetruecolor($wx, $wy);

        $transparent = imagecolorallocatealpha($new, 0, 0, 0, 127);
        $rotate = imagerotate($imageW, 280, $transparent);
        imagealphablending($rotate, true);
        imagesavealpha($rotate, true);

        // get the newest image X and Y
        $ix = imagesx($rotate);
        $iy = imagesy($rotate);

        //copy the image to the canvas
        imagecopyresampled($destImg, $rotate, 940, 2050, 0, 0, $ix, $iy, $ix, $iy);
Lepy
  • 152
  • 1
  • 12