How to create scaled thumbnails in PHP
En Español  

It's always a good idea to generate as many different sizes of thumbnails as are required in a web based system, right after an image is uploaded. Some of the advantages of generating and storing the thumbnails are that you can reduce the processing time that your server needs to use if you generate the thumbnails on demand, and of course you can reduce the consumed bandwidth by not sending the full size image when this is not necessary.

Topics
Objective, requirements and usage
Creating the function
Footnotes
Download the function

Objective, requirements and usage

Our objective here is that the image is scaled down to fit in a rectangular area, while keeping the aspect ratio of the image. We are going to define the maximum width and the maximum height of the thumbnail, and the image is going to be fit in there. Additionally, we may only provide the function with either a maximum width or a maximum height, so our image would be scaled based only in the provided value, adjusting the missing one to keep the aspect ratio of the image. Our function will ask for the following values:

Usually, when we dynamically upload an image to the server, we save the name of said image in a database, or we assign it a name that we can link to an entry; the following example assumes that we obtain the information from an entry in a variable called $row["image"] and that we use this to call the image:

<img src="/path/<?=$row["image"];?>" width="100" height="100" alt="" />

The idea is that instead of call the image as in the previous example, we can call it as in the following one, assuming "mini" is the prefix that we wish to use; we can also use something like "mini100x100" if we want, this may be useful if we generate more than one size, giving each size it's own prefix:

<img src="/path/mini_<?=$row["image"];?>" alt="" />

Our function will request the following values:

  • The path of the source image: This image must be already stored in a folder inside the server where this PHP script is hosted, therefore, this function is to be called once the image upload request has been processed.
  • The desired maximum width of the thumbnail, this may be a wildcard character (*), in which case we would determine the width based on the provided height.
  • The desired maximum height of the thumbnail, this may be a wildcard character (*), in which case we would determine the height based on the provided width.
  • A prefix (or you may use a suffix if you wish, with a simple change to the code) to be added to the name of the source image, so we use this name to save the new image.

And it will be called as follows, once the image is in the server, adjusting the relative path to the image, the size and the prefix that we desire:

create_scaled_thumbnail("../images/".$image_name, 100, 100, "mini");

Creating the function

What we do here is to create an image buffer with the original image, we calculate the size of the thumbnail (as we are providing the function with a maximum height and/or a maximum width and not the final size, or else we may end with the wrong aspect ratio), we create a secondary image buffer with the size that we just calculated where we are going to store the scaled image, then we are going to perform the scaling operation, we save this image, and finally we destroy the image buffers.

function create_scaled_thumbnail($image_path, $thumb_width, $thumb_height, $prefix) {

We start by checking that the parameters are either positive integer numbers or the wildcard character (*), and that both parameters are not the wildcard character.

if ($thumb_width === "*" && $thumb_height === "*") {
    echo "Both values should not be a wildcard";
    exit(1);
}

if (!(is_integer($thumb_width) && $thumb_width > 0) && !($thumb_width === "*")) {
    echo "The width is invalid";
    exit(1);
}

if (!(is_integer($thumb_height) && $thumb_height > 0) && !($thumb_height === "*")) {
    echo "The height is invalid";
    exit(1);
}

After this we find out what type of image is it by using pathinfo(), and we create the first image buffer.

$extension = pathinfo($image_path, PATHINFO_EXTENSION);
switch ($extension) {
    case "jpg":
    case "jpeg":
        $source_image = imagecreatefromjpeg($image_path);
        break;
    case "gif":
        $source_image = imagecreatefromgif($image_path);
        break;
    case "png":
        $source_image = imagecreatefrompng($image_path);
        break;
    default:
        echo "Invalid file type";
        exit(1);
        break;
}

We obtain the width and the height of the image:

$source_width = imageSX($source_image);
$source_height = imageSY($source_image);

When a parameter is the wildcard character, we just calculate the missing parameter using a cross-multiplication. But when both parameters are numeric values, we need to compare the aspect ratio of the source image and that of the thumbnail, in this case we may face tree situations:

The aspect ratio of the images is the same:

A graphical representation of the scaling operation when the source image and the thumbnail have the same aspect ratio.

In this case we don't recalculate any of the values, we create the thumbnail with the exact same measures that we received as parameters.

The source image is wider than the thumbnail.

A graphical representation of the scaling operation when the source image is wider than the thumbnail.

In this case we take the width given as a parameter, and calculate the height by using a cross-multiplication, so that the source image fits inside the thumbnail without losing it's aspect ratio.

The source image is narrower than the thumbnail.

A graphical representation of the scaling operation when the source image is narrower than the thumbnail.

In this case, we take the height that we received as a parameter, and calculate the width with a cross-multiplication, so that the source image fits inside the thumbnail area without losing it's aspect ratio.

if ($thumb_width === "*") {
    $thumb_width = ceil($thumb_height * $source_width / $source_height);
} else {
    if ($thumb_height === "*") {
        $thumb_height = ceil($thumb_width * $source_height / $source_width);
   } else {
        if (($source_width / $source_height) < ($thumb_width / $thumb_height)) {
            $thumb_width = ceil($thumb_height * $source_width / $source_height);
        } else {
            if (($source_width / $source_height) > ($thumb_width / $thumb_height)) {
                $thumb_height = ceil($thumb_width * $source_height / $source_width);
            }
        }
    }
}

After this we create the second image buffer, as an empty area, with ImageCreateTrueColor():

$target_image = ImageCreateTrueColor($thumb_width, $thumb_height);

And we generate the thumbnail of the image with imagecopyresampled():

imagecopyresampled($target_image, $source_image, 0, 0, 0, 0, $thumb_width, $thumb_height, $source_width, $source_height);

After this we save the thumbnail, preserving the original image type.

$target_file = pathinfo($image_path, PATHINFO_DIRNAME) . "/";
$target_file .= $prefix . "_" . pathinfo($image_path, PATHINFO_FILENAME);
switch ($extension) {
    case "jpg":
    case "jpeg":
        imagejpeg($target_image, $target_file);
        break;
    case "gif":
        imagegif($target_image, $target_file);
        break;
    case "png":
        imagepng($target_image, $target_file);
        break;
}

And finally we destroy the image buffers with imagedestroy() and close the function.

    imagedestroy($target_image);
    imagedestroy($source_image);
}

Footnotes

If we want to use a suffix instead of a prefix we just need to replace $prefix with $suffix in the function declaration, and we change the line:

$target_file .= $prefix . "_" . pathinfo($image_path, PATHINFO_FILENAME);

for the lines:

$target_file .= pathinfo($image_path, PATHINFO_BASENAME);
$target_file .= "_" . $suffix . "." . $extension;

Download the function

Just remember to call it once the image was uploaded to the server, and add the prefix (or suffix) to the name of the image to show the thumbnail.

thumb_scaled.tar.gz
md5sum: 83c54820cf82c6dc0703277e390b7d03