Image Manipulation with PHP – The GD Libraries

The GD libraries are the principle PHP module used for image manipulation, and are available from Boutel.Com, Inc.

If you are lucky enough to be hosted on (or indeed own) a server running GD2.0 or above, you’ll have the ability to use truecolour images in jpeg format (and in png if version 2.0.4+) and therefore, you won’t really benefit by reading this tutorial. Those using GD2.0+ should be opting to use ImageCreateTrueColor in place of ImageCreate, and ImageCopyResampled in place of ImageCopyResized to assure use of maximum colour levels. If you’re running anything under 2.0, read on!

Creating the Image

The function ImageCreate(x_dimension,y_dimension) in GD is restricted to a palette of 256 colours, and as such, it rarely outputs an image at a quality that’s acceptable to most Web designers. Fortunately there is a way of getting around this restriction, and maximizing the palette to 16.7 million colours. To enable this, you’ll need a graphics editor that’s capable of saving jpegs with zero compression, a GD-capable server, and a few spare minutes to read this tutorial.

A quick explanation of the concept…

If you use ImageCreateFromJPEG($image_pointer); your usable palette will exactly mimic that of the image to which you point. So, if we throw away the ImageCreate(width,height) function and focus on trying to use ImageCreateFromJPEG, we should be heading toward a better palette. Those of you who are conversant with GD (or the swifter thinkers among you) might already have noted that creating from a hosted image does not allow us to specify a width and height for the image we’ve created. If our resource image is 400px by 200px, and we want to create a thumbnail wiht a maximum dimension of 100px, then we’ll want a base image of 100px by 50px.

This limits us to two options:

  1. Either we upload base images at all the width-height variations needed by our script, and dictate which one to use through a little basic mathematics, or
  2. We use one image that’s sized to accommodate any dimension we will need, and which will only draw to a designated area of that size, leaving any edges blank/page colour.

If all your displayed images are the same size, you won’t have any major problems to solve — you can simply base your images on a large blank palette that is presized to the dimensions you need. And if you feel like exploring the first option and uploading multiple base images, you should be able to manage with the hints you’ve already picked up here.

The rest of this tutorial will deal with the second option. We’ll look at how to deduce which area of the created image to draw to, and the tips and hints provided here should get you up and running quickly!

Basic Thumbnail Rules

1. Be Kind To Your Server

It’s easy to display a group of thumbnails just by iterating through the images in a directory, and pushing them all through a thumbnailing script. Because it is so easy, some people do exactly that at every page load, which quite obviously is a little server- resource hungry. A much better alternative is to save an actual thumbnail image onto the server and simply display that.

2. Protect your Thumbnailing Scripts

For versatility, you’ll want a few variables that control output of the thumbnail. You’ll need an image pointer at the very least, maybe a switch that allows you to save a copy to the server if need be, and perhaps a compression setting. With those in place, you’ll want to assure that no one can enter a URL that will override your usual settings. The typical options of sessions or .htaccess will do fine.

The Scripts

Currently we’re trying to create a new image that represents another image whose dimensions are not known prior to running the script.

As we discussed, we’ll want to have available a few variables that allow us to specify our resource image, control whether a copy is saved on the server, and control the output compression ratio.

But we can also use a session check to control whether the script is run or not. Perhaps our administration panel could register a session called ‘allow_thumbs‘ to enable the script. We’d call this script using the src attribute of an <img> tag, in a manner similar to this:

<img src="thumb.php?basepath=foldername/ 
&img_ref=nicebig.jpg&create=yes&compress=65">

Because we will be using exactly the same base image each time, we can also hard code the dimensions into the tag, along with an alt=. You will, no doubt, find your own system for building the calling tag, based on your current administration set up.

Another thing to note about calling through the src of an <img> tag is that any parse errors that occur in the script will not be echoed to the page. All that will happen is that you’ll see an image-not-found space on your page. If you need to see the error returns for debugging purposes, you’ll need to temporarily remove the session protection, and access the script directly through a URL call.

The following script will produce a thumbnail by using a blank base image, rather than the ImageCreate() function. An explanation of the specific syntaxes follows.

session_start(); 
if($HTTP_SESSION_VARS["allow_thumbs"] == "yes")
{
header ("Content-type: image/jpeg");

// define the small, square image that will be  
// used as the thumbnail base

$palette_image = 'foldername/large_palette_base_image.jpg';

/****** You shouldn't need to edit below here ******/

// Set some defaults values for variables that have not  
// been passed to the script through the url

if(!isset($HTTP_GET_VARS['create']))  
{$HTTP_GET_VARS['create'] = 'no';}
if(!isset($HTTP_GET_VARS['basepath']))  
{$HTTP_GET_VARS['basepath'] = '';}
if(!isset($HTTP_GET_VARS['compress']))  
{$HTTP_GET_VARS['compress'] = 100;}

// establish where on the thumbnail we can draw to

$thumbsize = getImageSize($palette_image);
$maxdim = $thumbsize[0];
$draw_from = $HTTP_GET_VARS['basepath'].$HTTP_GET_VARS['img_ref'];
$dim = GetImageSize($draw_from);
if($dim[0]>$dim[1])
{
$to_w = $maxdim;
$to_h = round($dim[1]*($maxdim/$dim[0]));
$to_x = 0;
$to_y = round($maxdim-$to_h)/2;
}
else
{
$to_h = $maxdim;
$to_w = round($dim[0]*($maxdim/$dim[1]));
$to_y = 0;
$to_x = round($maxdim-$to_w)/2;
}

// create some base images to start designing from  
// and make initial basic thumbnail

if($dim[2]==1) {$from = ImageCreateFromGIF($draw_from);}
elseif($dim[2]==2) {$from = ImageCreateFromJPEG($draw_from);}
elseif($dim[2]==3) {$from = ImageCreateFromPNG($draw_from);}
$thumb = ImageCreateFromJPEG($palette_image);
// $set_bg_colour = ImageColorAllocate($thumb,255,0,0);
// $fill_bg_colour = ImageFill($thumb,0,0,$set_bg_colour);
ImageCopyResized($thumb, $from, $to_x, $to_y, 0,  
0, $to_w, $to_h, $dim[0], $dim[1]);

/******* Image Manipulation Scripting *******/

// extra image manipulation can go here

/***** End Image Manipulation Scripting *****/

// output the created thumnbnail onto the calling page  
// and, if $create has been set to 'yes', also create  
// a copy of the thumbnail on the server

ImageJPEG($thumb,'',$HTTP_GET_VARS['compress']);
if($HTTP_GET_VARS['create'] == "yes")
{
ImageJPEG($thumb,$HTTP_GET_VARS['basepath'].substr
($HTTP_GET_VARS['img_ref'],0,
strpos($HTTP_GET_VARS['img_ref'],'.')).'_thumb.jpg',  
$HTTP_GET_VARS['compress']);
}

// destroy all the temporary images used by the  
//server while executing this  
scriptlet (tidying up)

ImageDestroy($from);
ImageDestroy($thumb);
}

The script above will take a resource image (referenced by img_ref= in the calling url) and reduce it to a thumbnail that fits centrally within the palette image (the blank image used to extend the palette). The palette indexes on the resultant thumbnail will be increased to a maximum size that mirrors that of the palette image. You might like to copy the above script and refer to it during the following explanations.

Syntax Explanations

Let’s now step through the script, and explore what happens at each point.

	
  • Session - is simply there for security. You could do session_start(); $allow_thumbs = "yes"; session_register("allow_thumbs"); in your admin page to activate the thumbnailing.

    Alternatively, you could remove the session parts and put the thumbnailing script in a folder with a safeguarding .htaccess file. Either way, just make sure people cannot access the script by typing in the URL.

    	
  • header() - to output to a page or file, a header type must be sent. We chose jpeg due to its compression ability.

    	
  • $palette_image - the path (absolute from root, or relative to this script) and name of the square blank image that is to be used as a thumbnail base. I emphasized "square" because you must use a square image with the above script. To create a nice truecolour image, simply:

  • open your graphics program,

  • make a new square (have I mentioned that it should be square?) image,
  • increase colour depth to maximum (if it isn't already), and

  • save it as a .jpg.

    You might also like to fill it with the same colour as the background of your page before you save. Lastly, upload it and change the value of $palette_image to reference this new file.

  • default values - to avoid always having to pass every variable=value pair through the URL call, we just set some default values for use if the variable isn't set. For instance, if we called an image with img src="thumb.php?img_ref=blat.png", the script would automatically set to create=no, basepath='' and compress=100.

  • establish draw area - using GetImageSize(), we find the height and width of both our resource image, and thumbnail base. We check which is bigger on the resource image (whether it is portrait or landscape) and reduce that to mimic the space available on the thumbnail base. Some reasonably easy mathematics can then be used to deduce the other dimension and the top left pixel of our draw area. We have...

    	
  • $to_h - height of the area to draw to

    	
  • $to_w - width of the area to draw to

    	
  • $to_x - horizontal position of first pixel, counted from the left

    	
  • $to_y - vertical position of first pixel, counted from the top

  • create some images - for GD to do any image manipulations it needs to create a copy of the designated image and work from that. We need it to create two images, the thumbnail base (which we called $thumb), and a copy of the resource image (which we called $from). Our earlier use of GetImageSize on the resource image also yielded an index that holds the filetype. A quick bit of value testing will reveal that we can create our copy of that image -- be it a .gif, .png or .jpg -- which means our script is a bit more versatile now.

  • // $set_bg_colour - if you didn't fill your thumbnail base image with the same colour as your page background, you should un-comment this line and the $fill_bg_colour line. You should insert rgb values into the ImageColorAllocate($thumb, red, green, blue) call to mimic your page colour. You could even pass these in as variables in the calling URL.

  • ImageCopyResized - using all the mathematically deduced values, we use this function to take the entire area of our resource image, shrink it and copy it to the rigidly defined area of our thumbnail base.

  • /* image manipulation scripting */ - if we wanted to perform any further manipulations on our thumbnail, we would do so in this area. Further along in this tutorial are a couple of code snippets that you could plug in here.

  • ImageJPEG - this is the call that outputs an image to the browser.

  • if (create = "yes") - to save a copy of the thumbnail we've created onto the server, we need to use the middle parameter of the ImageJPEG function. We test if our variable create has explicitly been set to "yes" and, if so, we rip apart the resource image name to remove the extension and create a file based upon [that image name]_thumb.jpg.

    Note that you will need the directory that you save your thumbnails into to be chmodded to pretty high permissions. You might also want to put the thumbnails in their own seperate directory, in which case you'll want to amend the path - eg. to $HTTP_GET_VARS['basepath']. 'thumbnail_folder/' .substr($....

  • ImageDestroy - because GD had to create images to work from, we end all manipulation scripts by destroying those temporary images from the server.

    I've tested this script using GD1.6.2 and GD1.8.4, which both performed admirably -- certainly an improvement on the 256 colours allowed in the use of ImageCreate().

    Now we've got the basics, let's take a look at how we can use further image manipulation functions...

    Further Manipulations

    In the prior script, there was an area commented out as /* image manipulation scripting */, which was empty. If we wanted to try a few weird and wonderful effects on our thumbnails, we could add code here, integrating all the variables we defined or deduced earlier.

    An example idea might be to add a shadowed bevel to the thumbnail, perhaps with light colour shading on the top and left, and dark colour shading on the right and base. To give you an idea of the techniques involved, I'll take the shadowed bevel idea and step through it slowly.

    Shadowed Bevel

    A useful function in GD is ImageCopyMerge, as it allows us to merge a part of one image onto our thumbnail. It is especially useful because we can also define an opacity for the merged portion (which to you and me means shading). If we use a short for loop to count how far we are from each edge of the thumbnail, we can also use that incremental number to work out the opacity with which we'll draw our dark and light lines.

    // create a dark image and a light image   
     
    $dark_shadey = ImageCreate($maxdim,$maxdim);  
    $nadir = ImageColorAllocate($dark_shadey,0,0,0);  
    $light_shadey = ImageCreate($maxdim,$maxdim);  
    $nadir = ImageColorAllocate($light_shadey,255,255,255);  
     
    // decide how wide we want our edge shading  
     
    $edge_width = 10;  
    for($edge_pixel = 0; $edge_pixel < $edge_width; $edge_pixel++)  
    {  
     
    // work out the opacity relative to how far from the edge we are  
     
    $opacity =  100 - (($edge_pixel+1) * (100 / $edge_width));  
     
    // merge a bit of the light image along the top and left side  
    // merge a bit of the dark image along the base and right side  
     
    ImageCopyMerge($thumb,$light_shadey,$to_x+($edge_pixel-1),  
    $to_y+($edge_pixel-1),0,0,1,$to_h-(2*$edge_pixel),$opacity);  
    ImageCopyMerge($thumb,$light_shadey,$to_x+($edge_pixel-1),  
    $to_y+($edge_pixel-1),0,0,$to_w-(2*$edge_pixel),1,$opacity);  
    ImageCopyMerge($thumb,$dark_shadey,$to_x+($to_w-($edge_pixel+1)),  
    $to_y+$edge_pixel,0,0,1,$to_h-(2*$edge_pixel),$opacity-20);  
    ImageCopyMerge($thumb,$dark_shadey,$to_x+$edge_pixel,$to_y+  
    ($to_h-($edge_pixel+1)),0,0,$to_w-(2*$edge_pixel),1,$opacity-20);  
    }  
     
    // destroy the two new images that we used  
     
    ImageDestroy($dark_shadey);  
    ImageDestroy($light_shadey);

    You might notice I've used a few weird syntaxes here, such as reducing the dark image opacity by 20 in the ImageCopyMerge calls. This is simply because the resultant image looks better if the dark isn't quite as dark as the light is light. If you decide to code your own manipulations, you will (more than likely) have to add a few workaround syntaxes like this to your own scripts.

    Just to keep you going, here are a couple more manipulations that can be cut and pasted into the /* image manipulation scripting */ area of the main tutorial script.

    Spider's Web

    This small plugin scriptlets produces a spider's web effect, which is drawn over the thumbnail in the colour defined as $zenith. This script simply draws a few lines from the lower left corner at various angles, and then draws elliptical arcs centred at the same corner.

    $zenith = ImageColorAllocate($thumb,255,255,255);   
    for($draw = 0;$draw<$to_h;$draw+=12)  
    {  
    ImageLine($thumb,$to_x,($to_h+$to_y),($to_w+$to_x),  
    (($to_h-$draw)+$to_y),$zenith);  
    }  
    for($draw = 0;$draw<$to_w;$draw+=12)  
    {  
    ImageLine($thumb,$to_x,($to_h+$to_y),($draw+$to_x),  
    $to_y,$zenith);  
    }  
    for($draw = 1;$draw<14;$draw++)  
    {  
    ImageArc($thumb,$to_x,($to_h+$to_y),$draw*($to_w/4),$draw*  
    ($to_h/4),270,0,$zenith);  
    }

    Doughnut

    This larger plugin scriptlet produces an elliptical shaded doughnut thumbnail. The background and shading colour is defined by $zenith. This manipualtion works by iterating through the thumbnail one pixel at a time, and deciding exactly how much opacity to apply when merging a small dot at that place.

    $dot = ImageCreate(1,1);   
    $zenith = ImageColorAllocate($dot,255,255,255);  
     
    for($ypos=0;$ypos<$to_h;$ypos++)  
    {  
    for($xpos=0;$xpos<$to_w;$xpos++)  
    {  
    $xdist = abs(($to_w/2)-$xpos);  
    if($xdist==0) {$xdist = 0.01;}  
    $ydist = abs(($to_h/2)-$ypos);  
    $dist = sqrt(pow($xdist,2)+pow($ydist,2));  
    $angl = atan($ydist/$xdist);  
    $el_dist =    
    sqrt(pow(abs(cos($angl)*$to_w/2),2)+pow(abs(sin($angl)*$to_h/2),2));  
    if($dist>$el_dist || $dist<$el_dist/6)  
    {  
    ImageCopyMerge($thumb,$dot,$xpos+$to_x,$ypos+$to_y,0,0,1,1,100);  
    }  
    else {  
    $dnut_dist = ($el_dist/12)*5;  
    $offset_dist = abs((($el_dist/12)*7)-$dist);  
    $uppy = sin(acos($offset_dist/$dnut_dist))*$dnut_dist;  
    $opac = 100-((100/$dnut_dist)*$uppy);  
    ImageCopyMerge($thumb,$dot,$xpos+$to_x,$ypos+$to_y,0,0,1,1,$opac);  
    }  
    }  
    }  
    ImageDestroy($dot);

    I hope that tutorial and the extra little scriptlets have taught you how versatile the GD libraries can be. Perhaps you have even caught the image-manipualtion bug and are eager to start coding your own transformations! Good luck.

  • Free book: Jump Start HTML5 Basics

    Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

    No Reader comments

    Comments on this post are closed.