Introduction to Image Manipulation in php + GD
Written By: Nathan Baker
- 24 Jul 2006 -
Description: We all know that php is very useful for generating dynamic webpages. However, fewer people know that it is also useful for generating dynamic images! In this tutorial, I will guide you through generating image thumbnails on the fly.
OK, I'm excited! Let's write the getImage function!
Great! Let's go.
function getImage($mult, $name, $ext) { $oldSize = getimagesize("$name"); $oldx = $oldSize[0]; $oldy = $oldSize[1]; $x = $oldx * $mult; $y = $oldy * $mult; //Make sure we're not too insanely huge while($x * $y < 2097152){ $mult -= .25; $x = $oldx * $mult; $y = $oldy * $mult; } }
"WHAT?", you scream, flecks of spittle flying from your mouth like debris from an explosion, "THIS is what I was supposed to be excited about?" Just calm down and take a few deep breaths. And maybe see a psychiatrist about that anger problem. This isn't the whole function, but it's a good start.
Let's look at that first code block to start off with, shall we? The only thing particularly fancy there is the call to getimagesize. That call gets a two-element array containing the width as element 0 and the height as element 1. It takes a string as a parameter, so I use a double-quoted string and embed the $name variable right inside, which is a handy thing that php lets you do. Then I multiply the original values by the multiplier to get the new values.
Next, as the comment indicates, I make sure that the image is not too large. If you're wondering where that number came from, it's two megapixels (well, it's 1024 * 1024 * 2, which is close enough). This is because the parameters are passed through HTTP get. Thus, someone could just pass in a gigantic value for scale, and all of a sudden all your CPU power and RAM are eaten up generating two-gigabyte images. This is not a substitute for good access control, but it does help close an avenue of attack, which is something all web developers should keep in mind at all times.
Anyway, that was boring. On to the next part of the function; this should go after the above code but before the closing brace for the getImage function:
switch($ext){ case 'jpg': case 'jpeg': $src = imageCreateFromJPEG("$name"); header('Content-type: image/jpeg'); break; case 'png': $src = imageCreateFromPNG("$name"); header('Content-type: image/png'); break; case 'gif': $src = imageCreateFromGIF("$name"); header('Content-type: image/gif'); break; default: //We don't know what it is, so die die("Unrecognized file type: $ext"); } if($src===false){ return null; }
First, the header function. As you hopefully know, HTTP messages contain header information that tells the browser information about what it's getting. Those of you on Windows are no doubt so used to the system using the file extension to determine file type that this might be a little shocking, but it's what allows us to have a file with a .php extension that generates an image. The header command sends header information to the browser, and in this case it is sending metadata about the file which will subsequently arrive. Note that we still depend on the file type, so make sure you get it right.
The second item is the imageCreateFrom* functions. It takes an image on disk, reads it in, and stores a pointer to it in memory. That's what the $src variable holds. If this were a statically-typed language then we would have to screw around with polymorphism and other big words, or maybe byte arrays, but php is content to let us assign any old thing to a variable. Yay for php!
Finally, I use not one, not two, but three equals signs in the trailing if statement. A single equal sign (a = b) means "set a equal to b". A double equal sign (a == b) means "the contents of a are equivalent to the contents of b". This is tricky. Due to the aforementioned dynamic typing of php, you never really know what type a variable has. Thus, php is pretty liberal about what is equal to what. For example, '4' (a string) is equal to 4 (a number). Try it out! Also, false is equal to 0. This means that if a function returns 0 and you compare it to false, you will get a, no pun intended, false positive. The triple-equals can be read as "a is identical to b". One handy thing that php has over many other languages is that false and zero are different things. You just have to use the right comparison operator.
OK, assuming I haven't bored you to death talking about operators and degrees of equality, let's finish this baby up!
$dest = imagecreatetruecolor($x, $y); imagecopyresampled($dest, $src, 0, 0, 0, 0, $x, $y, $oldx, $oldy); switch($ext){ case 'png': imagepng($dest); break; case 'gif': imagegif($dest); break; case 'jpg': case 'jpeg': imagejpeg($dest); break; }
All of this still belongs in front of the closing function brace (if you've lost track of your braces then see your dentist, or check out the full source, which is available as a download). There are, again, three things going on here, so we shall examine them:
First is imagecreatetruecolor. Besides being in all lower case and somewhat difficult to read, it also creates a new image of the given dimensions. We are starting with a blank canvas the exact length and width of our resized image.
Next, we say imagecopyresampled. This particular function, which takes a zillion parameters, copies an image to another image, resampling/resizing as necessary. I guess you could have figured that out from the name. Anyway, the parameters let you copy just parts of the image, but of course we want them all.
Finally, we use the image* commands. imagepng, for example, sends a png image to the browser. We don't want any extra stuff being sent, so the function (and thus the script) ends right after this.