Create Thumbnail in PHP
I wanted to get some thumbnail creation code in PHP to use for a little project I was working on, and so I traipsed over to Google and asked for "php thumbnail". I took a peek at the first search result, snagged the code, inserted it in my test app, and went merrily on my way. And that will be the last time I trust Google to find me code.
There are many problems with the code that I got. It's not malicious, but it does many easy things wrong. And the one thing that it's supposed to do well - the thing for which Google found it - it doesn't do correctly.
I can't entirely blame the code author, because this particular bit of code is very commonly written incorrectly. But what's a guy to do when you just want a simple PHP function (as opposed to a whole class) that generates thumbnails? Well, it's about time someone did something about that. Here we go.
Here's what most people do wrong. You can't simply compare whether the width of the original image is greater than the height of the original image. You need to compare the aspect ratio (the ratio of width to height) of the source size to the destination (thumbnail) size. Why?
Assume the destination size is a square, 100 pixels by 100 pixels. The aspect ratio of the destination size is 1 (100/100). If the source image is wider than it is tall, then you need to maximize the width of the output. If the source image is taller than it is wide, then you need to maximize the height of the output. In either of these cases, you're fitting the larger of the two sides into the 100×100 pixel space. That the comparison of original width to original height works out is pure coincidence. To see where that evaluation fails, consider a different set of dimensions.
Assume the destination size is a rectangle, 100 pixels wide by 50 pixels high. If you have an image that is 50×50, it should obviously not be resized. According to the algorithm I was using, you would end up resizing the image to 100×50, which is obviously not right.
Worse yet, if your source image was 75×50, it should still not be resized because it is already at the maximum output height. You end up with a very oddly resized image using the old, incorrect algorithm.
If instead of comparing the size of the source image dimensions to itself, you compare the ratio of those dimensions to the ratio of the dimensions of the output image, then you know which way the source image will better fit into the destination size. You can then use that information to anchor your dimensions for the thumbnail output.
When you're done playing mental math with how to use those dimensions, there are a few other things that we should clean up in the PHP, too.
First, you don't explode() a file extension off the end of the file. If you already have the image file, use the built-in getimagesize() image function to determine the dimensions of the file and its file type all in one pass. When you have to determine filetype based on extension, use PHP's pathinfo() function. It returns other information that might be useful later.
For the love of all that is good, will people please start using switch/case? There is a point when you gain enough experience in coding to know that you're going to be making comparisons to a bunch of things and that you'll have to handle each differently. Even if you only have to deal with one thing to start off (like a jpeg, for example) you still get that feeling that maybe this shouldn't be an if(), but a switch(). Given the option, use the switch(). There's not much worse than a bunch of ifelse(){} statements stacked thoughtlessly onto one another.
Let's also try returning some values on success or failure instead of die()ing. Sure, we might not use the return value. There might be nothing we can do if the thumbnail creation fails, but at least we can try to let someone know if they care. I often care.
PHP GD now supports GIF input and output, so there is no need to omit it. There are also a few tricky functions in PHP that let you apply matrix transformations to your images, so you can do things like sharpen the thumbnail before it's saved. I stole borrowed the code for sharpening directly from the online PHP manual.
Granted, this function to create thumbnails isn't perfect, but in my opinion, it's a big improvement over what came before, and it's still small enough to include as a clip of commonly used code.
Here is the function, with a decent amount of inline and phpdoc comments:
/**
* Creates a resized image from a source image, saves resized image to file
* @param string $src_filename Filename of the image to resize
* @param string $dst_filename Filename of the resized image to output
* @param integer $max_width Maximum width of resized image
* @param integer $max_height Maximum height of resized image
* @return boolean true on success, false on failure
*/
function createthumb( $src_filename, $dst_filename, $max_width, $max_height )
{
// Get information about the image
list($src_width, $src_height, $type, $attr) = getimagesize( $src_filename );
// Load the image based on filetype
switch( $type ) {
case IMAGETYPE_JPEG:
$src_img = imagecreatefromjpeg( $src_filename );
break;
case IMAGETYPE_PNG:
$src_img = imagecreatefrompng( $src_filename );
break;
case IMAGETYPE_GIF:
$src_img = imagecreatefromgif( $src_filename );
break;
default:
return false;
}
// Did the image fail to load?
if ( !$src_img ) {
return false;
}
// Calculate the output size based on the original's aspect ratio
if ( $src_width / $src_height > $max_width / $max_height ) {
$thumb_w = $max_width;
$thumb_h = $src_height * $max_width / $src_width;
}
else {
$thumb_w = $src_width * $max_height / $src_height;
$thumb_h = $max_height;
}
// Create the output image and copy to source to it
$dst_img = ImageCreateTrueColor( $thumb_w, $thumb_h );
imagecopyresampled( $dst_img, $src_img, 0, 0, 0, 0, $thumb_w, $thumb_h, $src_width, $src_height );
/* Sharpen before save?
$sharpenMatrix = array( array(-1, -1, -1), array(-1, 16, -1), array(-1, -1, -1) );
$divisor = 8;
$offset = 0;
imageconvolution( $dst_img, $sharpenMatrix, $divisor, $offset );
//*/
// Get information about the output image
$path_info = pathinfo($dst_filename);
// Setup default return info
$return = true;
// Load the image based on filetype
switch ( strtolower( $path_info['extension'] ) ) {
case 'jpg':
case 'jpeg':
imagejpeg( $dst_img, $dst_filename );
break;
case 'png':
imagepng( $dst_img, $dst_filename );
break;
case 'gif':
imagegif( $dst_img, $dst_filename );
break;
default:
$return = $false;
}
// Clean up memory
imagedestroy( $dst_img );
imagedestroy( $src_img );
return $return;
}
Comments
Comment by Sam on .
The formula I've been looking for. Thanks
Comment by flashjatekok on .
Great script!
I found a lot, but only this is the only 1, what keeps the aspect ratio. I think this line is the key:
if ( $src_width / $src_height > $max_width / $max_height ) {
Keep the good work!
Comment by Justin on .
Hi,
It's good It's what I want, But can you tell me how to preview it before save it. I want to show but not save it.
Thanks,
Justin
Comment by Owen on .
To output the image instead of saving it (and if you do this, I suggest sending a bunch of caching headers, since your server will otherwise need to generate that same image on every hit) omit the $filename parameter from the calls to imagejpeg(), imagepng(), and imagegif(), then send correct content type headers based on the requested file type. So for jpeg, you'd send:
header('content-type: image/jpeg');
In all, it would be better to save the thumbnail if it didn't already exist, then redirect the visitor to the saved file regardless of whether you just wrote the thumbnail or not. You could probably use a 301 redirection, which could reduce server traffic even more. In any case, if you do it that way, then Apache will handle the caching for you, instead of messing with that complicated business in PHP.
Comment by ricky on .
forgive my ignorance (i'm new to PHP), but how would you refer to this in the document? if i have a two dozen images that i want dynamically generated on a gallery page, how would i call the individual images using the img tag?
and then, how would Owen's suggestion be added to the code (meaning, what would the code look like if implementing his suggestion)?
thanks for the help!
Comment by Josef on .
Thanks or the snippet! Just what I needed to replace my intricate network of if statements with.
Comment by Sudar on .
Thanks Owen for the code. You helped me to save a couple of hours in my life.
- Sudar
Comment by Daniel Sanchez on .
You are right! I completely missed it. I have been trying to come up with my own version, and got caught up on testing yours, making sure it worked. Thank you!
Comment by owen on .
I don't think you're doing your math wrong, but you're missing an evaluation somewhere.
Your source ratio is $src_width / $src_height (400/362=1.104) and your maximum size ratio is $max_width / $max_height (300/225=1.333).
If you follow the code, that leads you to the else branch:
$thumb_w = $src_width * $max_height / $src_height
(400*225/362=248)
and
$thumb_h = $max_height
(225)
So your end dimensions are 248x225.
Comment by Daniel Sanchez on .
Hello, just a quick question. I have an image that is 400 wide x 362 Height. I want a maximum size of 300 wide x 225 height. Applying these parameters, your function gives an output image of: 300 wide x 248 height? Am I doing my math wrong?
Comment by dre on .
i have been working on this solution for 6 hours today and this is the first script that actually worked correctly the first time. you are AWESOME!!! thank you so much!!!!
Comment by owen on .
Neat! Glad it was helpful. :)
Comment by alas on .
Hello Owen, at first I thought your code was bad, and did a series of test to find the correct size calculations, when I went to look at your code it was the almost the same.
I changed this:
---------------
// Calculate the output size based on the original's aspect ratio
if ( $src_width / $src_height > $max_width / $max_height ) {
$thumb_w = $max_width;
$thumb_h = $src_height * $max_width / $src_width;
}
else {
$thumb_w = $src_width * $max_height / $src_height;
$thumb_h = $max_height;
}
to this:
--------
$thumb_w = $max_w;
$thumb_h = $max_h;
$thumb_r = $max_w / $max_h;
$src_ar = $src_width / $src_height;
if ($src_ar > $thumb_r) {
$thumb_h /= $src_ar;
} else if ($src_ar $thumb_r) {
$thumb_w *= $src_ar;
}
and posted in the php online manual citing your page;
couse every one of the previos thumbnailers sucks, and yours is perfect, good work ;) keep it up!
Comment by raede on .
hi there...nice code,its fine except im having error in 1 line,its " { case IMAGETYPE_JPEG: $src_img = imagecreatefromjpeg( $src_filename ); " its show error - Parse error: syntax error, unexpected T_CASE in C:\xampp\htdocs\Mysystem\6th.php on line 13. cn u plz help me,im so confused...