October 28, 2008

Resize Images Using PHP and GD Library

PHP provides a rich set of functions to generate and manipulate images. Most of these functions require the GD library, which is bundled with PHP since version 4.3.

The following code sample demonstrates the use of GD library to resize an image to generate its thumbnail. The script first saves the uploaded image in a specific location, it then generates a thumbnail version of the image and finally saves the generated thumbnail image in JPEG format.

The generate_image_thumbnail() Function

The following function accepts the following parameters:

  1. source image file path
  2. thumbnail image file path

Upon execution, the function analyzes the source image, calculates destination image dimensions, resizes the image using GD library functions and saves the image to the specified destination in JPEG format.

<?php
/*
 * PHP function to resize an image maintaining aspect ratio
 * http://salman-w.blogspot.com/2008/10/resize-images-using-phpgd-library.html
 *
 * Creates a resized (e.g. thumbnail, small, medium, large)
 * version of an image file and saves it as another file
 */

define('THUMBNAIL_IMAGE_MAX_WIDTH', 150);
define('THUMBNAIL_IMAGE_MAX_HEIGHT', 150);

function generate_image_thumbnail($source_image_path, $thumbnail_image_path)
{
    list($source_image_width, $source_image_height, $source_image_type) = getimagesize($source_image_path);
    switch ($source_image_type) {
        case IMAGETYPE_GIF:
            $source_gd_image = imagecreatefromgif($source_image_path);
            break;
        case IMAGETYPE_JPEG:
            $source_gd_image = imagecreatefromjpeg($source_image_path);
            break;
        case IMAGETYPE_PNG:
            $source_gd_image = imagecreatefrompng($source_image_path);
            break;
    }
    if ($source_gd_image === false) {
        return false;
    }
    $source_aspect_ratio = $source_image_width / $source_image_height;
    $thumbnail_aspect_ratio = THUMBNAIL_IMAGE_MAX_WIDTH / THUMBNAIL_IMAGE_MAX_HEIGHT;
    if ($source_image_width <= THUMBNAIL_IMAGE_MAX_WIDTH && $source_image_height <= THUMBNAIL_IMAGE_MAX_HEIGHT) {
        $thumbnail_image_width = $source_image_width;
        $thumbnail_image_height = $source_image_height;
    } elseif ($thumbnail_aspect_ratio > $source_aspect_ratio) {
        $thumbnail_image_width = (int) (THUMBNAIL_IMAGE_MAX_HEIGHT * $source_aspect_ratio);
        $thumbnail_image_height = THUMBNAIL_IMAGE_MAX_HEIGHT;
    } else {
        $thumbnail_image_width = THUMBNAIL_IMAGE_MAX_WIDTH;
        $thumbnail_image_height = (int) (THUMBNAIL_IMAGE_MAX_WIDTH / $source_aspect_ratio);
    }
    $thumbnail_gd_image = imagecreatetruecolor($thumbnail_image_width, $thumbnail_image_height);
    imagecopyresampled($thumbnail_gd_image, $source_gd_image, 0, 0, 0, 0, $thumbnail_image_width, $thumbnail_image_height, $source_image_width, $source_image_height);
    imagejpeg($thumbnail_gd_image, $thumbnail_image_path, 90);
    imagedestroy($source_gd_image);
    imagedestroy($thumbnail_gd_image);
    return true;
}
?>
Thumbnail Dimensions Calculation

The thumbnail image generated by this function maintains the same aspect ratio as the original image. The function calculates best-fit dimensions for the thumbnail.

To calculate the dimensions for the thumbnail image, the aspect ratios of the original image and "ideal" thumbnail image are calculated. (Note: The aspect ratio of an image is its width divided by its height). The two aspect ratios are then compared. This leads us to the following three possible cases:

Aspect ratios are same

Resizing an image with same aspect ratio

The function uses the ideal width and ideal height for the thumbnail.

Source image is comparatively "taller"

Resizing a taller image

The function uses constrained width and ideal height for the thumbnail.

Source image is comparatively "wider"

Resizing a wider image

The function uses the ideal width and constrained height for the thumbnail.

Resizing the Image

The function uses the imagecopyresampled function available in the GD library. PHP manual provides the following description:

imagecopyresampled() copies a rectangular portion of one image to another image, smoothly interpolating pixel values so that, in particular, reducing the size of an image still retains a great deal of clarity.

Visit the following URLs to see documentation of the functions used in this example:

The process_image_upload() Function

The following function performs file upload handling. It accepts only one parameter -- the name of the field that contains the file. This function performs necessary error checking and moves the uploaded file around accordingly. It then calls the above mentioned function to generate the thumbnail.

<?php
/*
 * Uploaded file processing function
 */

define('UPLOADED_IMAGE_DESTINATION', './images/');
define('THUMBNAIL_IMAGE_DESTINATION', './thumbnails/');

function process_image_upload($field)
{
    $temp_image_path = $_FILES[$field]['tmp_name'];
    $temp_image_name = $_FILES[$field]['name'];
    list(, , $temp_image_type) = getimagesize($temp_image_path);
    if ($temp_image_type === NULL) {
        return false;
    }
    switch ($temp_image_type) {
        case IMAGETYPE_GIF:
            break;
        case IMAGETYPE_JPEG:
            break;
        case IMAGETYPE_PNG:
            break;
        default:
            return false;
    }
    $uploaded_image_path = UPLOADED_IMAGE_DESTINATION . $temp_image_name;
    move_uploaded_file($temp_image_path, $uploaded_image_path);
    $thumbnail_image_path = THUMBNAIL_IMAGE_DESTINATION . preg_replace('{\\.[^\\.]+$}', '.jpg', $temp_image_name);
    $result = generate_image_thumbnail($uploaded_image_path, $thumbnail_image_path);
    return $result ? array($uploaded_image_path, $thumbnail_image_path) : false;
}
?>

Putting it all together

To create a complete working example using the functions above, we need to create a page that posts a file to the processing script. Following is the complete code example, along with the file upload form and the actual function call.

The HTML Form

Please note the enctype="multipart/form-data" in the following form. This is very important to assure that your browser actually sends the file.

<form action="image-resize-demo.php" method="post" enctype="multipart/form-data">
    Upload an image for processing<br>
    <input type="file" name="Image1"><br>
    <input type="submit" value="Upload">
</form>
The PHP Code

Below is the PHP code that combines the two above-mentioned functions, calls them and displays the success or failure message:

<?php
/*
 * PHP function to resize an image maintaining aspect ratio
 * http://salman-w.blogspot.com/2008/10/resize-images-using-phpgd-library.html
 *
 * Creates a resized (e.g. thumbnail, small, medium, large)
 * version of an image file and saves it as another file
 */

define('THUMBNAIL_IMAGE_MAX_WIDTH', 150);
define('THUMBNAIL_IMAGE_MAX_HEIGHT', 150);

function generate_image_thumbnail($source_image_path, $thumbnail_image_path)
{
    list($source_image_width, $source_image_height, $source_image_type) = getimagesize($source_image_path);
    switch ($source_image_type) {
        case IMAGETYPE_GIF:
            $source_gd_image = imagecreatefromgif($source_image_path);
            break;
        case IMAGETYPE_JPEG:
            $source_gd_image = imagecreatefromjpeg($source_image_path);
            break;
        case IMAGETYPE_PNG:
            $source_gd_image = imagecreatefrompng($source_image_path);
            break;
    }
    if ($source_gd_image === false) {
        return false;
    }
    $source_aspect_ratio = $source_image_width / $source_image_height;
    $thumbnail_aspect_ratio = THUMBNAIL_IMAGE_MAX_WIDTH / THUMBNAIL_IMAGE_MAX_HEIGHT;
    if ($source_image_width <= THUMBNAIL_IMAGE_MAX_WIDTH && $source_image_height <= THUMBNAIL_IMAGE_MAX_HEIGHT) {
        $thumbnail_image_width = $source_image_width;
        $thumbnail_image_height = $source_image_height;
    } elseif ($thumbnail_aspect_ratio > $source_aspect_ratio) {
        $thumbnail_image_width = (int) (THUMBNAIL_IMAGE_MAX_HEIGHT * $source_aspect_ratio);
        $thumbnail_image_height = THUMBNAIL_IMAGE_MAX_HEIGHT;
    } else {
        $thumbnail_image_width = THUMBNAIL_IMAGE_MAX_WIDTH;
        $thumbnail_image_height = (int) (THUMBNAIL_IMAGE_MAX_WIDTH / $source_aspect_ratio);
    }
    $thumbnail_gd_image = imagecreatetruecolor($thumbnail_image_width, $thumbnail_image_height);
    imagecopyresampled($thumbnail_gd_image, $source_gd_image, 0, 0, 0, 0, $thumbnail_image_width, $thumbnail_image_height, $source_image_width, $source_image_height);
    imagejpeg($thumbnail_gd_image, $thumbnail_image_path, 90);
    imagedestroy($source_gd_image);
    imagedestroy($thumbnail_gd_image);
    return true;
}

/*
 * Uploaded file processing function
 */

define('UPLOADED_IMAGE_DESTINATION', './images/');
define('THUMBNAIL_IMAGE_DESTINATION', './thumbnails/');

function process_image_upload($field)
{
    $temp_image_path = $_FILES[$field]['tmp_name'];
    $temp_image_name = $_FILES[$field]['name'];
    list(, , $temp_image_type) = getimagesize($temp_image_path);
    if ($temp_image_type === NULL) {
        return false;
    }
    switch ($temp_image_type) {
        case IMAGETYPE_GIF:
            break;
        case IMAGETYPE_JPEG:
            break;
        case IMAGETYPE_PNG:
            break;
        default:
            return false;
    }
    $uploaded_image_path = UPLOADED_IMAGE_DESTINATION . $temp_image_name;
    move_uploaded_file($temp_image_path, $uploaded_image_path);
    $thumbnail_image_path = THUMBNAIL_IMAGE_DESTINATION . preg_replace('{\\.[^\\.]+$}', '.jpg', $temp_image_name);
    $result = generate_image_thumbnail($uploaded_image_path, $thumbnail_image_path);
    return $result ? array($uploaded_image_path, $thumbnail_image_path) : false;
}

/*
 * Here is how to call the function(s)
 */

$result = process_image_upload('Image1');
if ($result === false) {
    echo '<br>An error occurred while processing upload';
} else {
    echo '<br>Uploaded image saved as ' . $result[0];
    echo '<br>Thumbnail image saved as ' . $result[1];
}
?>

Limitations of the Script

  • The example currently supports GIF, JPEG and PNG file handling. Additional image type support can be added rather easily.
  • The example does not generate unique file names and therefore overwrites existing uploaded or thumbnail images.
  • The example does not show any error messages. All "return false" need to be replaced by appropriate error handling.