PHP-GD: Resize Transparent Image PNG & GIF

Published by Miaz Akemapa

Book minded!!!

Reader Comments

  1. First, my english is a little… 🙂

    I have a problem. Im trying to open a png file with a transparent object inside (not all, just the border). Then i write some text and save the file as another .png file.

    The problem is that de quality of the object border is too bad (¿pixeled?). How can i fix that?

  2. The script gets hinkey if you have a file that is smaller in height than the minimum, but longer in width… you end up resizing huge in width to get the right height.

    It needs to resize down by which ever length is too long, or both if they are both too long.

    I HAVE AN IMPROVEMENT THAT FIXES THIS:

    //If image dimension is smaller, do not resize
    if ($imgInfo[0] <= $w && $imgInfo[1] = $w && $imgInfo[1] <= $h) {
    $nWidth = $w;
    $nHeight = $imgInfo[1]*($w/$imgInfo[0]);
    }else{
    //if h larger but not w, resize it, but keep it proportional
    if ($imgInfo[0] = $h) {
    $nWidth = $imgInfo[0]*($h/$imgInfo[1]);
    $nHeight = $h;
    }else{
    //if both w and h are larger, resize it, but keep it proportional
    if ($w/$imgInfo[0] > $h/$imgInfo[1]) {
    $nWidth = $w;
    $nHeight = $imgInfo[1]*($w/$imgInfo[0]);
    }else{
    $nWidth = $imgInfo[0]*($h/$imgInfo[1]);
    $nHeight = $h;
    }
    }
    }
    }
    $nWidth = round($nWidth);
    $nHeight = round($nHeight);

  3. Ahhh… just realised through lotsa testing that the line:

    if ($w/$imgInfo[0] > $h/$imgInfo[1]) {

    Should be

    if ($w/$imgInfo[0] < $h/$imgInfo[1]) {

  4. Gif -> Png transparent conversion failed.

    From gif files it is not working.. It is making a gif file, even if I selected PNG. And it is not transparent.

  5. Sorry, just had to removed the switches and directly let it to open as gif and save as png. It worked really fine. Just removed some if-s. 🙂

  6. Hi Oliver,
    I have the same problem as you. I copied the code, didn’t make changes and it’s still black in the background. No idea what to do. Does anyone have an idea?
    Stefan

  7. I had the same issue, so (since I really don’t need it to be transparent) I forced it not to be transparent and just preserve whatever color the trasparent area was started as. Like transparent green shows green without transparency.

    I believe the acctual issue is the fact that the transparent color is mandatorily white in this code, and none of my gifs ever end up with pure white as the transparent color after I optimize them. (Just my guess on the reason though.)

  8. It has to do with the imagecreatetruecolor call. This call does not work with gifs, as a gif only has 256 colors. I tried to convert the gif to png, then resize it, and then re-convert back to gif, but that didn’t work. Anyone have any thoughts?

  9. Hmmm, can you go into more detail though? My prob is I have a cms where users are uploading pics. Most of the time they’re non-transparent jpegs, gifs, or pngs, in which case it’s fine (even though the resizing/resampling of gifs loses alot of the quality).
    If you use imagecreate instead of imagecreatetruecolor, you’ll get the transparent background, but like I said, the quality is awful.

    If you’ve got image magick installed, you can use this:

    $image = new Imagick($source);
    $image->resizeImage($nWidth,$nHeight,imagick::FILTER_LANCZOS,1);
    $image->writeImage($destination);

    It makes it so the quality is slightly better, and still transparent. Only run that code if it’s a gif though; the qaulity goes down if it’s a png or jpeg.

    I’m still looking for a specific function which will improve gif quality when it gets resized. I believe the route I need to go is to convert it to a png, resize, resample, get rid of opacity for the images, and the resave as gif.

    Thoughts?

  10. Here is the code section that doesn’t do transparent on the gif… it splits the IF PNG and the IF GIF into two sections:

    /* Check if this image is PNG , then set if Transparent*/
    if($imgInfo[2] == 3){
    imagealphablending($newImg, false);
    imagesavealpha($newImg,true);
    $transparent = imagecolorallocatealpha($newImg, 255, 255, 255, 127);
    imagefilledrectangle($newImg, 0, 0, $nWidth, $nHeight, $transparent);
    }
    /* Check if this image is GIF , then DONT transparent */
    if($imgInfo[2] == 1){
    imagealphablending($newImg, true);
    imagesavealpha($newImg,false);
    $transparent = imagecolorallocatealpha($newImg, 255, 255, 255, 127);
    imagefilledrectangle($newImg, 0, 0, $nWidth, $nHeight, $transparent);
    }
    imagecopyresampled($newImg, $im, 0, 0, 0, 0, $nWidth, $nHeight, $imgInfo[0], $imgInfo[1]);

  11. Oh! You probably will need this alteration too:

    //Get Image size info
    $imgInfo = getimagesize($img);
    switch ($imgInfo[2]) {
    case 1: $im = imagecreatefromgif($img); break;
    case 2: $im = imagecreatefromjpeg($img); break;
    case 3: $im = imagecreatefrompng($img); break;
    default: $im = imagecreatefrombmp($img); break; // If it doesn’t fit a previous type, try it as a BMP
    }

    The BMP section is some custom function as below:
    <? // Accept BMP FILES
    function ConvertBMP2GD($src, $dest = false) {
    if(!($src_f = fopen($src, “rb”))) {
    trigger_error(“Can’t open $src”, E_WARNING);
    return false;
    }
    if(!($dest_f = fopen($dest, “wb”))) {
    trigger_error(“Can’t open $dest”, E_WARNING);
    return false;
    }
    $header = unpack(“vtype/Vsize/v2reserved/Voffset”, fread($src_f, 14));
    $info =
    unpack(“Vsize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vy­r
    es/Vncolor/Vimportant”, fread($src_f, 40));

    extract($info);
    extract($header);

    if($type != 0x4D42) { // signature “BM”
    return false;
    }

    $palette_size = $offset – 54;
    $ncolor = $palette_size / 4;
    $gd_header = “”;
    // true-color vs. palette
    $gd_header .= ($palette_size == 0) ? “\xFF\xFE” : “\xFF\xFF”;
    $gd_header .= pack(“n2”, $width, $height);
    $gd_header .= ($palette_size == 0) ? “\x01” : “\x00”;
    if($palette_size) {
    $gd_header .= pack(“n”, $ncolor);
    }
    // no transparency
    $gd_header .= “\xFF\xFF\xFF\xFF”;

    fwrite($dest_f, $gd_header);

    if($palette_size) {
    $palette = fread($src_f, $palette_size);
    $gd_palette = “”;
    $j = 0;
    while($j > 3;
    $scan_line_align = ($scan_line_size & 0x03) ? 4 – ($scan_line_size & 0x03)
    : 0;

    if($bits == 24) {
    $j = 0;
    $k = 1;
    $m = 2;
    $function = ‘return “‘;
    while($j < $scan_line_size) {
    $function .= “\\{\$s\{$m}}\{\$s\{$k}}\{\$s\{$j}}”;
    $j += 3;
    $k += 3;
    $m += 3;
    }
    $function .= ‘”;’;
    }
    else if($bits == 32) {
    $function = ‘return “‘;
    $j = 0;
    $k = 1;
    $m = 2;
    $n = 3;
    while($j < $scan_line_size) {
    $function .= “\\{\$s\{$m}}\{\$s\{$k}}\{\$s\{$j}}”;
    $j += 4;
    $k += 4;
    $m += 4;
    $n += 4;
    }
    $function .= ‘”;’;
    }
    else if($bits == 8) {
    $function = ‘return $s;’;
    }
    else if($bits == 4) {
    $j = 0;
    $function = ”;
    while($j >4);”;
    $function .= “\$a[]=chr(\$b&0x0F);”;
    $j++;
    }
    $function .= “return substr(implode(\$a), 0, \$width);”;
    }
    else if($bits == 1) {
    $j = 0;
    $function = ”;
    while($j < $scan_line_size) {
    $function .= “\$b=ord(\$s\{$j});”;
    $function .= “\$a[]=chr((int)((\$b&0x80)!=0));”;
    $function .= “\$a[]=chr((int)((\$b&0x40)!=0));”;
    $function .= “\$a[]=chr((int)((\$b&0x20)!=0));”;
    $function .= “\$a[]=chr((int)((\$b&0x10)!=0));”;
    $function .= “\$a[]=chr((int)((\$b&0x08)!=0));”;
    $function .= “\$a[]=chr((int)((\$b&0x04)!=0));”;
    $function .= “\$a[]=chr((int)((\$b&0x02)!=0));”;
    $function .= “\$a[]=chr((int)((\$b&0x01)!=0));\n”;
    $j++;
    }
    $function .= “return substr(implode(\$a), 0, \$width);”;
    }

    $f = create_function(‘$s, $width’, $function);

    for($i = 0, $l = $height – 1; $i varname = $url[“host”];
    $this->position = 0;
    $this->buffer = @$GLOBALS[$this->varname];
    return true;
    }

    function stream_close()
    {
    $GLOBALS[$this->varname] = $this->buffer;
    }

    function stream_read($count)
    {
    $ret = substr($this->buffer, $this->position, $count);
    $this->position += strlen($ret);
    return $ret;
    }

    function stream_write($data)
    {
    $this->buffer .= $data;
    $this->position += strlen($data);
    return strlen($data);
    }

    function stream_tell()
    {
    return $this->position;
    }

    function stream_eof()
    {
    return $this->position >= strlen($this->buffer);
    }

    function stream_stat() {
    return array( ‘size’ => strlen($this->buffer) );
    }

    }

    function imagecreatefrombmp($filename) {
    // use a memory stream instead of a temp file
    // where possible
    if(function_exists(‘stream_wrapper_register’)
    && stream_wrapper_register(“mem”, “MemoryStream”)) {
    $tmp_name = “mem://GD_TMP_FILE”;
    $del_tmp = false;
    }
    else {
    $tmp_name = tempnam(“/tmp”, “GD”);
    $del_tmp = true;
    }
    if(ConvertBMP2GD($filename, $tmp_name)) {
    $img = imagecreatefromgd($tmp_name);
    if($del_tmp) {
    unlink($tmp_name);
    }
    return $img;
    } return false;

    }
    ?>

    and the Save section to handle the BMP looks like this:

    $temp_array1 = explode(“.”, $img);
    if (($temp_array1[1] != “bmp”)&&($temp_array1[1] != “BMP”)){
    //Generate the file, and rename it to $newfilename
    switch ($imgInfo[2]) {
    case 1: imagegif($newImg,$newfilename); break;
    case 2: imagejpeg($newImg,$newfilename); break;
    case 3: imagepng($newImg,$newfilename); break;
    default: trigger_error(‘Failed resize image!’, E_USER_WARNING); break;
    }
    }else{
    imagepng($newImg,$newfilename); // Force it to save as a png type if it was a bmp but leave the .bmp extention
    }

  12. Here also is a sweet function that may be of use:

    <? // Rotate
    function rotateit($img, $angle) {
    //Set the new file name the same as the old file name
    $newfilename = $img;

    //Get Image size info
    $imgInfo = getimagesize($img);
    switch ($imgInfo[2]) {
    case 1: $im = imagecreatefromgif($img); break;
    case 2: $im = imagecreatefromjpeg($img); break;
    case 3: $im = imagecreatefrompng($img); break;
    default: trigger_error(‘Unsupported filetype!’, E_USER_WARNING); break;
    }

    // .. what colour will the uncovered bits be?
    $bgColour = 0xFFFFFF; // red
    // rotate the image by $angle degrees
    $newImg = imagerotate($im, $angle, $bgColour);

    //Generate the file, and rename it to $newfilename
    switch ($imgInfo[2]) {
    case 1: imagegif($newImg,$newfilename); break;
    case 2: imagejpeg($newImg,$newfilename); break;
    case 3: imagepng($newImg,$newfilename); break; // This will rotate any that are BMP but are loaded up into PNG Type
    default: trigger_error(‘Failed to rotate image!’, E_USER_WARNING); break;
    }

    echo “Image Rotated: ” . $newfilename . ” “;
    return $newfilename;
    }
    ?>

  13. That sucks.
    What I don’t get, is I can accomplish the task in photoshop. I take the 300×300 gif, turn it into a png, resize it to 72×72, then turn it back to a gif, and save it.

    Transparency is preserved, resize quality is perfect. There must be a way somehow in php. I’ve been looking around with image magick, but can’t seem to find it (nor is this documented anywhere online, oddly enough; gif resizing is, but not the kind of quality maintenance you get from photoshop)

  14. I’m still stuck on trying to get gifs to resize perfectly. Thats wsweet about the bmp’s though. I’ll test that out and see how they resize 🙂

  15. Finally, someone who has got this right. This code can resize a png with aphablending properly. I’ve been looking for this for so long, and have tried around 6 examples. Well done and thank you!

  16. The only thing I would like is to be able to select a max width (trying to add this myself). If you select 120,120 in the function you get an image which might be 120×147 for example, too big for my purposes.

  17. As Posted Above (with later correction), reposted for Dan.
    This section does what you need.

    //If image dimension is smaller, do not resize
    if ($imgInfo[0] <= $w && $imgInfo[1] = $w && $imgInfo[1] <= $h) {
    $nWidth = $w;
    $nHeight = $imgInfo[1]*($w/$imgInfo[0]);
    }else{
    //if h larger but not w, resize it, but keep it proportional
    if ($imgInfo[0] = $h) {
    $nWidth = $imgInfo[0]*($h/$imgInfo[1]);
    $nHeight = $h;
    }else{
    //if both w and h are larger, resize it, but keep it proportional
    if ($w/$imgInfo[0] < $h/$imgInfo[1]) {
    $nWidth = $w;
    $nHeight = $imgInfo[1]*($w/$imgInfo[0]);
    }else{
    $nWidth = $imgInfo[0]*($h/$imgInfo[1]);
    $nHeight = $h;
    }
    }
    }
    }
    $nWidth = round($nWidth);
    $nHeight = round($nHeight);

  18. One thing about the transparent gifs, I am pretty sure you cannot preserve transparency AND use imagecopyresampled. I am having the same problem with black background, however changing imagecopyresampled to imagecopyresized fixes transparency, but gives a crappy resize. Am investigating this further to see if there is a good solution.

  19. I currently have all my functions setup to resize whatever size I need – jpg mime type is cool – but I get jagged edges – this is wher IM HOPING this nifty lil script can save my ass – but for the life of me I can’t seem to figure how to implemant it – I copied and pasted and got tons of warnings.

    [php]
    Warning: Unsupported filetype! in C:\wamp\www\cool\functions\fns_photo.php on line 228

    Warning: imagecreatetruecolor() [function.imagecreatetruecolor]: Invalid image dimensions in C:\wamp\www\cool\functions\fns_photo.php on line 268

    Warning: imagecopyresampled(): supplied argument is not a valid Image resource in C:\wamp\www\cool\functions\fns_photo.php on line 286

    Warning: Failed resize image! in C:\wamp\www\cool\functions\fns_photo.php on line 300
    [/php]

    it seems It;s not getting the image I need to set – but how do I do that…

    Greatly appreciated if anyone can help

    thanks 🙂

  20. This blog entry is awesome. I was able to learn how to alter a different image resize component i’m using in cakephp to fix the transparency issue. Thanks a million!

Leave a Reply

Your email address will not be published. Required fields are marked *