Smart Image Resizing while Preserving Transparency With PHP and GD Library

Update: this function is now available at github.com/maxim/smart_resize_image.
Fork away!

Should be easy to find, right? All you want is a function that resizes an image to constraints (doesn’t care if it should scale up or down), with possibility to select if you want to keep it proportional, and possibility to use either width or height as the constraint. Also, you want it to preserve transparency damn it! Surprisingly, I was unable to find a good function that does all that, so I decided to attempt writing it. This should do it.

Features/Usage:

  • If you pass width as 0 (zero) — this function will disregard width, and use height as constraint. Same vice versa.
  • If you set “proportional” to false – the function will simply stretch (or shrink) the image to its full constraints.
  • If one of the dimensions is set to zero, and proportional set to “false” – then the image will be forced to stretch or shrink the other dimension, and disregard the zeroed dimension (leave it the same).
  • If proportional is set to true – the image will resize to constraints proportionally, once again, with possibility to have either width or height set to zero.
  • The function can use either linux “rm” command, or php @unlink. Most probably you don’t need to ever use that flag, but on some setups – @unlink won’t work due to user access restrictions.
  • The function will simply replace the file that you give it, with the resized file.
  • The function supports gif, png, and jpeg, and preserves the transparency of gif and png images.
  • Tested on GD version 2.0.28 only.

Essentially, everything happens just as logically expected. Please, if you see anything wrong, and you know ways to enhance, or optimize it, let me know.

Update: Thank you Joanne for finding a bug with proportional resize, and providing a great, more concise and clear solution to proportional resize. (see comments)

Update: The image transparency code was updated to a more thorough solution. See comments for info.

Update: There is a new parameter “output” which can be set to either

  • “file” – overwrite the given file (default)
  • “browser” – output image through http – with correct mime type
  • “return” – return GD Library Image object
  • or any filename of choice – save changed version to output path

Update: There is another new parameter “delete_original”. Speaks for itself.

Please, excuse a few code misalignments. Bug with coloring filter.

function smart_resize_image( $file, $width = 0, $height = 0, $proportional = false, $output = 'file', $delete_original = true, $use_linux_commands = false )
  {
    if ( $height <= 0 && $width <= 0 ) {
      return false;
    }

    $info = getimagesize($file);
    $image = '';

    $final_width = 0;
    $final_height = 0;
    list($width_old, $height_old) = $info;

    if ($proportional) {
      if ($width == 0) $factor = $height/$height_old;
      elseif ($height == 0) $factor = $width/$width_old;
      else $factor = min ( $width / $width_old, $height / $height_old);  

      $final_width = round ($width_old * $factor);
      $final_height = round ($height_old * $factor);

    }
    else {
      $final_width = ( $width <= 0 ) ? $width_old : $width;
      $final_height = ( $height <= 0 ) ? $height_old : $height;
    }

    switch ( $info[2] ) {
      case IMAGETYPE_GIF:
        $image = imagecreatefromgif($file);
      break;
      case IMAGETYPE_JPEG:
        $image = imagecreatefromjpeg($file);
      break;
      case IMAGETYPE_PNG:
        $image = imagecreatefrompng($file);
      break;
      default:
        return false;
    }
   
    $image_resized = imagecreatetruecolor( $final_width, $final_height );
       
    if ( ($info[2] == IMAGETYPE_GIF) || ($info[2] == IMAGETYPE_PNG) ) {
      $trnprt_indx = imagecolortransparent($image);
   
      // If we have a specific transparent color
      if ($trnprt_indx >= 0) {
   
        // Get the original image's transparent color's RGB values
        $trnprt_color    = imagecolorsforindex($image, $trnprt_indx);
   
        // Allocate the same color in the new image resource
        $trnprt_indx    = imagecolorallocate($image_resized, $trnprt_color['red'], $trnprt_color['green'], $trnprt_color['blue']);
   
        // Completely fill the background of the new image with allocated color.
        imagefill($image_resized, 0, 0, $trnprt_indx);
   
        // Set the background color for new image to transparent
        imagecolortransparent($image_resized, $trnprt_indx);
   
     
      }
      // Always make a transparent background color for PNGs that don't have one allocated already
      elseif ($info[2] == IMAGETYPE_PNG) {
   
        // Turn off transparency blending (temporarily)
        imagealphablending($image_resized, false);
   
        // Create a new transparent color for image
        $color = imagecolorallocatealpha($image_resized, 0, 0, 0, 127);
   
        // Completely fill the background of the new image with allocated color.
        imagefill($image_resized, 0, 0, $color);
   
        // Restore transparency blending
        imagesavealpha($image_resized, true);
      }
    }

    imagecopyresampled($image_resized, $image, 0, 0, 0, 0, $final_width, $final_height, $width_old, $height_old);
 
    if ( $delete_original ) {
      if ( $use_linux_commands )
        exec('rm '.$file);
      else
        @unlink($file);
    }
   
    switch ( strtolower($output) ) {
      case 'browser':
        $mime = image_type_to_mime_type($info[2]);
        header("Content-type: $mime");
        $output = NULL;
      break;
      case 'file':
        $output = $file;
      break;
      case 'return':
        return $image_resized;
      break;
      default:
      break;
    }

    switch ( $info[2] ) {
      case IMAGETYPE_GIF:
        imagegif($image_resized, $output);
      break;
      case IMAGETYPE_JPEG:
        imagejpeg($image_resized, $output);
      break;
      case IMAGETYPE_PNG:
        imagepng($image_resized, $output);
      break;
      default:
        return false;
    }

    return true;
  }

123 responses to “Smart Image Resizing while Preserving Transparency With PHP and GD Library”

  1. Aaron

    Thank you for providing some great code… However, after trying to resize some transparent gifs for the past few days I finally noticed from php.net that imagecreatetruecolor() does not work with gifs.

    so I added

    if ( $info[2] == IMAGETYPE_GIF )
        $image_resized = imagecreate( $final_width, $final_height );
    else
        $image_resized = imagecreatetruecolor( $final_width, $final_height );

    If someone is able to get imagecreatetruecolor() to work on gifs let me know, but I keep getting a filled in background.

    1. Aaron

      a second option for dealing with GIFs is to add

      imagetruecolortopalette($image_resized, true, imagecolorstotal($image) );

      after

      imagecolortransparent( $image_resized, $trnprt_indx );

      this will convert the truecolor image to a palette with the same number of colors as the original image.

      FYI: GIFs are images based on the 256 color palette, imagecreatetruecolor() creates an image based on 1.6 million colors (or so).

  2. Tom

    My own re-size function was not properly handling transparency. I was able to integrate your transparency handling section into my function in just a couple minutes and have tested both gif and png.

    Works absolutely perfect!

  3. Marvin S.

    Hey you, sorry, but i’ve one question. If I want to save the resized image into an thumb folder like /thumbs/, where do I have to define the output path of the saved image?

    Marvin

  4. Jeff

    I was using the thumbnail resize solution from Sitepoint’s PHP Anthology (2nd edition), and I was able to integrate your transparency handling code into it easily. Thanks!

  5. pat

    Struggled with inconsistent transparency-results on a resized PNG for two hours… Solved my problem in 5min after i found your routine/example… THANKS!

  6. Jay

    Excellent work. I am using your function (along with Aaron’s fix) and it works great.

  7. Amien

    Awesome stuff! Thanks!

    Now to add a watermark/branding parameter ;)

  8. Matt

    This function is good and all, but I have been looking for 3 days now on how to fill the transparent portion with a specific color. As you can probably guess, I am not all that familiar with GD.

  9. Yoff

    Thanks! It works like a champ!

  10. Amy

    Ok, so I’m uploading PNG files and they are resizing correctly but coming out with a black background instead of transparency. Is that due to the way the original PNG was created or is there something I need to tweek with your code. Unfortunately I don’t know a lot about working with images like this. Any help is greatly appreciated.

    Thanks!

  11. James

    For those who get a black background, this works perfectly with images saved as PNG 24 rather than 8. If you still get a black background and you are merging the image returned from this function on to another image then heres the problem. It took me days to figure this out and I hope it saves someone else lots of time. imagecopymerge() does NOT preserve the transparency, that is what makes it a black background, not this function, instead use imagecopy() and it will work perfectly.

    There are also 2 typos in this function if you get the version from GitHub, $trnprt_indx should be $transparency and $trnprt_color should be $transparent_color

  12. Rien

    If i’d like to resize my image with 50% ? How I manage that? $width = $width/2 –> doesn’t work..

    Grts

  13. PHP ve PNG Sorunu

    [...] [...]

  14. Wrbhhh

    An excellent function! Thanks a lot!

    The only thing missing is the ability to preserve animation of animated gifs.

  15. Arunkumar

    Very good application. I am benefited so much

  16. Alejo Marello

    I added the transparency code into my own resize function and it worked like a charm.

    Thanks a lot!

  17. Việt Dũng

    it’s a really great function, but i got some problem when trying to resize big image. Here’s my code:

    $id = $_GET['id']; if (is_uploaded_file($_FILES['Filedata']['tmp_name'])){

    
    
    $uploadDirectory="file/";
    $uploadFile=$uploadDirectory.$id.basename($_FILES['Filedata']['name']);
    $them="(1)";<br />
    $uploadFile2=$uploadDirectory.$id.$them.basename($_FILES['Filedata']['name']);
    copy($_FILES['Filedata']['tmp_name'], $uploadFile2);
       include('SimpleImage.php');
      smart_resize_image( "$uploadFile2", $width = 240, $height = 0, $proportional = true, $output = "$uploadFile", $delete_original = false, $use_linux_commands = false );

    }

    the idea is to duplicate the image and resize it. It’s work great with small image, but when i try to upload the image such as 3508×4961 px, it’s doesn’t work. the image size only 138kb. I’m not sure what i doing incorrectly. Please help me! Thanks a lot.

    1. Mauro Zadunaisky

      Try this before calling the resize function

      ini_set(‘memory_limit’, ’128M’);

  18. Matt

    I added a couple of options it could really use like image quality and final destination path. By default your script works great but the resulting quality was pretty pixilated after the resize so I had to add the native php image quality percentage and it’s perfect now.

    function smart_resize_image( $file, $width = 0, $height = 0, $proportional = true, $output = ‘file’, $delete_original = true, $use_linux_commands = false, $resizedfilename = ‘c:\photos\large’, $quality = 95)

    Add $quality to:

    switch ( $info[2] ) { case IMAGETYPE_GIF: imagegif($image_resized, $output, $quality); break; case IMAGETYPE_JPEG: imagejpeg($image_resized, $output, $quality); break; case IMAGETYPE_PNG: imagepng($image_resized, $output, $quality); break; default: return false; }

    Thanks for the code assist, the transparency issue was one thing I was struggling with.

  19. Edward Goodnow

    Declare function => function smart_resize_image( $file, $width = 0, $height = 0, $proportional = false, $output = ‘file’, $delete_original = true, $use_linux_commands = false, $degrees = 0, $rotate = false )

    existing code => switch ( strtolower($output) ) { case ‘browser’: $mime = image_type_to_mime_type($info[2]); header(“Content-type: $mime”); $output = NULL; break; case ‘file’: $output = $file; break; case ‘return’: return $image_resized; break; default: break; }

    New code =>

    if($rotate == ‘true’ & is_numeric($degrees)){

    
    
     $image_resized = imagerotate($image_resized, $degrees, 0);

    }

    useage with rotation =>

    smart_resize_image( $image, $width = 200, $height = 150, $proportional = true, $output = ‘browser’, $delete_original = false, $use_linux_commands = false, $degrees = ’90′, $rotate = true );

    quite simple to add really thank you for the original code though

  20. Nick D.

    I love you.

    About to test this code out. Thanks so much, bud!

  21. arguon

    if I set the $output to file and it’s a PNG image, the save image result is a blank image. can you help me out here?

  22. Sandeep

    Great works cheers

  23. 0cool.f

    take a look: http://www.codicefacile.it/smartimage/SmartImage096.zip

    i’ve corrected this class with your function:

    $new = imagecreatetruecolor($newwidth, $newheight);

    $new = $this->saveTransparence($newwidth, $newheight);

    $new = imagecreatetruecolor($w, $h);

    $new = $this->saveTransparence($w, $h);

    // your function: private function saveTransparence($width, $height) { $image_resized = imagecreatetruecolor($width, $height); $image = $this->gdID; if ($this->info[2] == 1 || $this->info[2] == 3) { $trnprt_indx = imagecolortransparent($image);

    
    
        // If we have a specific transparent color
        if ($trnprt_indx &gt;= 0) {
            // Get the original image's transparent color's RGB values
            $trnprt_color = imagecolorsforindex($image, $trnprt_indx);</p>

    <pre><code>    // Allocate the same color in the new image resource
        $trnprt_indx = imagecolorallocate($image_resized, $trnprt_color['red'], $trnprt_color['green'], $trnprt_color['blue']);

        // Completely fill the background of the new image with allocated color.
        imagefill($image_resized, 0, 0, $trnprt_indx);

        // Set the background color for new image to transparent
        imagecolortransparent($image_resized, $trnprt_indx);
    }
    // Always make a transparent background color for PNGs that don't have one allocated already
    elseif ($this-&amp;gt;info[2] == 3) {
        // Turn off transparency blending (temporarily)
        imagealphablending($image_resized, false);

        // Create a new transparent color for image
        $color = imagecolorallocatealpha($image_resized, 0, 0, 0, 127);

        // Completely fill the background of the new image with allocated color.
        imagefill($image_resized, 0, 0, $color);

        // Restore transparency blending
        imagesavealpha($image_resized, true);
    }

    } return $image_resized;

    }

    What do you think about? P.S. the above class it's not mine... (sorry for my english i'm italian...)

  24. 0cool.f

    i’ve commented this lines but the text-editor have removed <#>

    /* $new = imagecreatetruecolor($newwidth, $newheight); */

    /* $new = imagecreatetruecolor($w, $h); */

  25. f1dz

    Great…!!! Its work for CodeIgniter Library.

    Thanks a lot.

  26. 大眼夹的鸟巢 » 自动获取带有防盗链保护的图片并在浏览器中显示

    [...] 实际应用时,我们发现有时候小百合BBS中的图片都是几百万像素的照片原图,在校园网内访问这些图片自然是毫无压力的,而且Web版BBS中有JavaScript来自动将过大的图片强制缩小显示,以免撑破版面。但是到了外站,如此大的图片就显得有些夸张了,利用PHP中的GD图形库,我们可以方便地进行图片的二次处理,首要的需求自然是将过大的图片缩小。GD库并没有直接按比例缩小图片的功能(如果有也太高级了),好在网上早已有许多现成的代码片段,我们便无需再次发明轮子了。参考了Maxim Chernyak的代码片段,我们可以很轻松地实现这一功能。需要说明的是,原始的代码片段中对于小图片也会进行放大处理,而且它对GIF动画的处理会让它变成静止图片,因此需要对其进行小小的修改来满足我们的需要。 [...]

  27. not able to resize the image-file with high resolution. | DeveloperQuestion.com
  28. gtagamer

    thanks for the code dude, really helps.

  29. buy marijuana in canada

    best bzp free ecstasy where to buy marijuana in colorado where can you buy k2 in kansas city legal high spice reviews k2 herbal incense chicago smoke legal buds cannabis shops in uk kronic legal high buy cocaine colombia best party pill no hang over tai high gold review where to buy k2 smoke blend in indiana how many ecstasy pills should you take legal bud high times blue playstation extacy salvia divinorum extract for sale where can i buy k2 herbal smoke legal bud that gets you high exotic party pills ingredients legal extasy uk herbal exstacy legal weed black mamba serenity now herbal incense high legal smoking highs smoke shops nyc buy marijuana las vegas buy k2 spice online buy marijuana washington dc buy k2 massachusetts where can i get extacy

  30. Richy

    Class doesn’t work on my server. I get the following error message:

    Parse error: syntax error, unexpected T_STRING, expecting T_OLD_FUNCTION or T_FUNCTION or T_VAR or ‘}’ in /home/vhosts/mysite.nl/httpdocs/096/SmartImage.class.php on line 16

  31. Mark

    You saved the day sir! thank you so much for posting this online! You’re the only person who has posted a script that actually works on PNGs with full opacity.

  32. logan

    hearty thanks for you…

  33. GDライブラリで透過形式の画像をリサイズするのに便利な関数 « redcocker's notebook

    [...] Smart Image Resizing while Preserving Transparency With PHP and GD Library [...]

  34. Toxicroadkill

    GREAT!!

  35. Mehul Batliwala

    Very good work. Thank you. :)

  36. Michael L Watson

    Nice work, thanks a lot!

  37. Pies

    Very cool, thank you so much!

    There’s a typo in the code on github, instead of:

    
    
        $transparent_color  = imagecolorsforindex($image, $trnprt_indx);
        $transparency       = imagecolorallocate($image_resized, $trnprt_color['red'], $trnprt_color['green'], $trnprt_color['blue']);

    it should be:

    
    
        $transparent_color  = imagecolorsforindex($image, $transparency);
        $transparency       = imagecolorallocate($image_resized, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);

    Thanks anyway :)

  38. Maniya

    Great work.. Thanks for your code.. it saved my time… :)

  39. David

    Are you getting black background? I was getting it for like 3 hours before I found solution. I had this problem just with some images. Some were good, some had black background. I tried to dump $trnprt_indx variable with these bad images and I was getting values less than zero! Condition if ($trnprt_indx >= 0) is meaningless. Note: I was dealing just with PNGs. Don’t know if it affects GIFs.

  40. shukaku

    Works like a charm, thanks a lot.. it helped me. (y)

  41. lvt

    Thank you guys for the great posts.

    You saved a lot of my hairs, I love you all :P

  42. Imagesbox

    Thanks, your function is saved my hours. Can you added to works with gif animated please?

Leave a Reply