Manipulate existing images

So far, we’ve covered the various techniques PHP gives you to create new images from scratch, but unless you’re a particularly creative person the best pictures you’ll be able to make will look like the came straight from MS Paint. Fortunately for all of us PHP is also more than capable of doing day-to-day manipulation of existing images, and that makes it perfect for use as a tool for creating a web image gallery. Relax, this isn’t a tutorial on how to create a web gallery. For one thing, I only have three pages here and it would take up at least that to get even a minimal gallery working, but more importantly there are dozens of web gallery scripts out already. Web galleries, it seems, are exceeded in number only by web browsers and Notepad replacements! What we’ll be concentrating on is enhancing our images and serving them up through PHP scripts – so let’s get started!
Functions for your utility belt
When working with existing images, there are three functions you simply have to get friendly with: imagesx() and its friend imagesy(), and getimagesize(). The first two take an image as their only parameter and return the width and height of the image respectively. In code, they’re used like this:
$image = imagecreatefrompng(“duck.png”);The third function actually does both of those combined and more, but the added complexity makes it a little harder to use – I use imagesx() and imagesy() all the time, just because they are so self-explanatory.
$image_width = imagesx($image);
$image_height = imagesy($image);
The function in question is getimagesize(), which takes the filename of an image to work with, and returns an array of information about it. While that might sound quite easy, the problem is that the array you get back has its keys numbered 0 to 3 rather than in any other way that you might find easier to remember.
These keys are, in order: the width of the picture (equivalent to imagesx()), the height of the picture (equivalent to imagesy()), the type of image (we’ll come to that in a moment), and also a fragment of HTML that looks like this:
height=”yyy” width=”xxx”The Xs and Ys will naturally get replaced with the width and height of the image, but you can use that string immediately inside a HTML IMG tag. The third element in the array is the type of the image – whether it’s a GIF, a JPEG and so on. This is reported back as a number that corresponds to a constant in PHP
The difference between 7 and 8 is the ordering of the bytes – 7 is Intel (II) and 8 is Apple Mac (MM is Motorola). Remember, WBMP is not ‘Windows Bitmap’, but ‘Wireless Bitmap’, a picture type designed for mobile devices. With these utility functions out of the way, we can crack on with the real work…
Resizing an image
The most basic tweak in any image toolkit is the ability to take an image down in size so that it’s a smaller file. Of course, this also works in the other direction – you can increase image size, though it will affect the final quality. On the web, dynamic resizing is most often used to generate thumbnails to save bandwidth – a concept that worked well until the ‘Internet accelerator’ programs came out that automatically followed links on pages to preload them, thereby downloading both thumbnails and full images! These have died off with the increase in broadband use, which means thumbnails are safe again.
Resizing in PHP is done with the imagecopyresized() function, which is one of those wonderfully descriptive function names that led most people to love PHP programming. However, it takes an enormous number of parameters that you need to at least be aware of – don’t bother trying to remember them, though. They are: destination image, source image, destination X and Y position (from the top-left of the image), source X and Y position, destination width and height, and source width and height. Got that? Easy, huh?
Well, the key to this function is that you need to create your destination image before passing it into the function. This matters a lot, because if you create a paletted destination image, PHP will use its palette to handle the resizing as opposed to resizing the original in its colour space then converting the palette across. There is a subtle difference there, but the result is that resizing a true colour image to a paletted destination will look a great deal worse than resizing true colour to true colour and converting to a palette by hand.
We have two sets of X and Y coordinates, plus two sets of width and height values in pixels. These define copy rectangles in the destination and source images, enabling you to take only part of an image and resize it into its destination. Alternatively, it also allows you to take all of your source image and resize it into only part of the destination image. The latter might sound completely useless, but you might use it to take your source image and place it onto a destination image so that the source exactly fits within the boundaries of a TV set in the destination image. Now you have an idea how it works, let’s give this function a run in some real code:
$src_img = imagecreatefrompng(“future.png”);That’s quite a chunk of code, so let me break it down line by line. First up we use imagecreatefrompng() to load up a picture of the Future Publishing website, and store it in $src_img. We then grab the size of the picture and divide it by three so we know how big the resized version will be – one-third the size of the original. The destination image is then created as a true colour image with the X and Y sizes we just calculated.
$src_size = getimagesize(“future.png”);
$dest_x = $src_size[0] / 3; $dest_y = $src_size[1] / 3;
$dst_img = imagecreatetruecolor ($dest_x, $dest_y);
imagecopyresized($dst_img, $src_img, 0, 0, 0, 0, $dest_x, $dest_y, $src_size[0], $src_size[1]);
imagepng($dst_img, “future_small.png”);
imagedestroy($src_img);
imagedestroy($dst_img);
Next, the big function call. As you can see, I’ve specified ‘’ for the sizes of the source and destination, which instructs PHP to use the entire picture. I’ve also put in the calculated width and height, plus the original width and height, to finish off the function call. Finally the picture is saved in the file called future_small.png, with a call to imagepng(), and – importantly – both the image resources get freed.
Resizing mark 2: resampling
As you’ll soon notice, the problem with plain, old resizing is that it literally throws pixel data away when resizing a picture downwards. To get an effective and natural-looking image resize, what you really need to do is resample it. This involves taking the image down in size by averaging the pixel colours and blending them smoothly so that you get a high-quality finished product. Resampling is done through the imagecopyresampled() function, which also has a large number of parameters. Thankfully they happen to be the same large number of parameters that you use with imagecopyresized(), so you can just simply edit your script to replace the word ‘resized’ with ‘resampled’ and you’II be ready to go!
The difference is substantial: if you look at the two pictures above – the top picture is resized, and the right-hand is resampled. Even though both of them have been reduced by 33 per cent of their original size, you can actually read a bit of the text in the bottom picture.
However, all is not completely well on ‘Planet Resampled’: did you notice that the resampled script took substantially longer to execute than the resized script? A resize operation is very quick to perform, because, as I said, it just throws pixels away. On the other hand, resampling is a complex operation that needs to perform hundreds (or even thousands) of pixel colour average operations in order to create the final result. So, while resampling generates a much higher quality result, it also takes up much more processor time – a sore point for web servers teeming with users. Most web developers I’ve talked to look at the end results and see that resizing just doesn’t hold a candle to resampling in terms of image quality, and opt for the slower but much better resample operation.
Finishing off
There are two final functions I want to touch upon before I finish this PHP pictures mini-series, and they’re Image_Type_To_Mime_Type() and gd_info(). The former is perhaps the hardest to type function in all of PHP, and – to be blunt – whenever I use it I tend to use variable functions, like this:
$ittmt = “image_type_to_mime_type”;If you’ve never used variable functions before, in the code above I’m setting the variable $ittmt to the name of a function, then using the variable as the function name later –a great feature for long function names like this one, but ultimately quite lazy!
$mime = $ittmt(IMAGETYPE_PNG);
As you can also see by the code, this function takes an image type constant as its only parameter – we covered these earlier in the big table. What it returns is the MIME type of the image, for example image/png or image/gif, which is important for sending out the HTTP Content-Type header with the header() function.
The gd_info() function returns an array of data describing the properties of your GD library – the part of PHP that creates and works with images. I recommend you give this a try – you get information, such as whether PNG is available (and also whether GIF is available, which is new), and whether you have fonts available too. [See below for rotating your images without losing quality].
Paul Hudson
Rotate your images without losing quality.
Moving swiftly onwards, the very last technique I want to show you is how to rotate images. Compared to resizing and resampling, this is a walk in the park, and my excuse for leaving it last after the other two is because that’s where it falls alphabetically! Rotating an image is accomplished through the imagerotate() function, which takes an image source, a rotation angle, and a colour as its three parameters.
Rotation is measured in degrees anti-clockwise from the centre of the image, which means 0 does nothing to the image, 90 rotates it so the top becomes the left-hand side, 180 rotates it so that that top becomes the bottom, and 359 effectively rotates the image one degree to the right. The last parameter is important: it’s the colour you want PHP to use wherever the ‘background’ should be. If you rotate an image by 45 degrees, you get gaps around it – this is the background.
As you know, rotating an image by a value other than 90, 180, 270, or 360 will result in an image that has larger dimensions than the original, but PHP handles all this for us by automatically matching the size of the output and the destination image resource. This scaling is also done using a smooth algorithm similar to that seen in imagecopyresampled(), so the end result will look good – and be equally slow in generation!
Here’s an example in code:
<?php $src_img = imagecreatefrompng( “future.png”);Now, I don’t want to sound like I’m patronising you, but here’s an important tip regarding rotation: whenever you rotate an image, some quality is lost. PHP doesn’t keep an ‘original’ around automatically that allows you to rotate time and time again without any quality lost – if you plan to rotate an image more than once, be smart and keep an original to work with. If you don’t do this, not only will the quality of your picture get gradually worse with each rotation, but you’ll also be rotating the background colour and adding more background colour – making your picture file size very large indeed!
$src_size = getimagesize(“future.png”);
$dst_img = imagecreatetruecolo r($src_size[0], $src_size[1]);
$background = imagecolorallocate($src_img, 255, 0, 255);
$dst_img = imagerotate($src_ img, 45, $background);
imagepng($dst_img, “future_ rotated.png”);
imagedestroy($src_img);
imagedestroy($dst_img);
?>

