The Spotless Developer Blog

Older Posts »« Newer Posts

Using PHP to Resize an Image (Crop-to-fit and Letterbox)

By Dustin Hendricks - December 12th, 2010

Resizing an image using PHP only becomes complicated when the destination image's aspect ratio is different from the source image's aspect ratio. When faced with this task, you have two choices. You either need to crop, or letterbox your image in order to make it fit into the destination aspect ratio. In this blog post, I will show you how to accomplish both methods using PHP.

First let's create a function that we will use to accomplish the image resizing.

<?php
function resize_image($source_image$destination_width$destination_height$type 1) {
    
// $type (1=crop to fit, 2=letterbox)
    
    
if ($type == 1) {
        
// crop to fit
            
    
} else {
        
// letterbox to fit
            
    
}
    
    return 
$destination_image;
}
?>

The $source_image should be an image resource created by a GD library function such as imagecreatefromjpg(). Notice we use a $type argument and a conditional to allow us to do either method within the same function.

The first thing we need to do in our function is figure out the dimensions of the source image, and the aspect ratios of both the source and destination images.

<?php
$source_width 
imagesx($source_image);
$source_height imagesy($source_image);
$source_ratio $source_width $source_height;
$destination_ratio $destination_width $destination_height;
?>

The aspect ratios tell us the relationship between width and height, which means if we know what the width should be, we can also figure out what the height should be, and vice versa. We do this by multiplying or dividing the known dimension by the aspect ratio.

Since one of the dimensions of the source image will end up being an exact resize to its correlating dimension on the destination image, and the other dimension will be figured out using the known dimension, we now only need to figure out which dimension, width or height, is the one that should be resized to match the destination dimension exactly. We do this by comparing the aspect ratios of both images.

<?php
if ($source_ratio $destination_ratio) {
    
// source has a wider ratio

} else {
    
// destination has a wider (or equal) ratio

}
?>

We will work on the crop method first. If the source has a wider aspect ratio, then we know we need to make the source height match the destination height, and crop the width in order to make it match the destination aspect ratio. Once we know the width and height of the peice of image we will be copying from the source, we can also figure out what x and y coordinates to use for copying from the source as well, by figuring out the difference, and dividing by two. We do this using the following code.

<?php
$temp_width 
= (int)($source_height $destination_ratio);
$temp_height $source_height;
$source_x = (int)(($source_width $temp_width) / 2);
$source_y 0;
?>

And vice verca if the destination has a wider aspect ratio.

<?php
$temp_width 
$source_width;
$temp_height = (int)($source_width $destination_ratio);
$source_x 0;
$source_y = (int)(($source_height $temp_height) / 2);
?>

Now we have to get the rest of the arguments used for the PHP imagecopyresample() function in order to complete the resize. Notice the destination copy coordinates and dimensions do not change for this method since we will be filling the entire destination image with our copy.

<?php
$destination_x 
0;
$destination_y 0;
$source_width $temp_width;
$source_height $temp_height;
$new_destination_width $destination_width;
$new_destination_height $destination_height;
?>

The letterbox method is similar except we need to figure out the destination copy dimensions and coordinates, since they will be the ones that differ when using this method.

<?php
if ($source_ratio $destination_ratio) {
    
// destination has a wider ratio
    
$temp_width = (int)($destination_height $source_ratio);
    
$temp_height $destination_height;
    
$destination_x = (int)(($destination_width $temp_width) / 2);
    
$destination_y 0;
} else {
    
// source has a wider (or equal) ratio
    
$temp_width $destination_width;
    
$temp_height = (int)($destination_width $source_ratio);
    
$destination_x 0;
    
$destination_y = (int)(($destination_height $temp_height) / 2);
}
$source_x 0;
$source_y 0;
$new_destination_width $temp_width;
$new_destination_height $temp_height;
?>

Now that we know all of needed values to pass as arguments to the imagecopyresampled() function, we can finally resize the image. Notice we fill the image with black (or whatever color you like) for letterboxing.

<?php
$destination_image 
imagecreatetruecolor($destination_width$destination_height);
if (
$type 1) {
    
imagefill($destination_image00imagecolorallocate($destination_image000));
}
imagecopyresampled($destination_image$source_image$destination_x$destination_y$source_x$source_y$new_destination_width$new_destination_height$source_width$source_height);
?>

When we put it all together, we get the complete function that we can now incorporate into whatever class we use for image manipulation. You should probably break this up into smaller functions for clarity.

<?php
function resize_image($source_image$destination_width$destination_height$type 0) {
    
// $type (1=crop to fit, 2=letterbox)
    
$source_width imagesx($source_image);
    
$source_height imagesy($source_image);
    
$source_ratio $source_width $source_height;
    
$destination_ratio $destination_width $destination_height;
    if (
$type == 1) {
        
// crop to fit
        
if ($source_ratio $destination_ratio) {
            
// source has a wider ratio
            
$temp_width = (int)($source_height $destination_ratio);
            
$temp_height $source_height;
            
$source_x = (int)(($source_width $temp_width) / 2);
            
$source_y 0;
        } else {
            
// source has a taller ratio
            
$temp_width $source_width;
            
$temp_height = (int)($source_width $destination_ratio);
            
$source_x 0;
            
$source_y = (int)(($source_height $temp_height) / 2);
        }
        
$destination_x 0;
        
$destination_y 0;
        
$source_width $temp_width;
        
$source_height $temp_height;
        
$new_destination_width $destination_width;
        
$new_destination_height $destination_height;
    } else {
        
// letterbox
        
if ($source_ratio $destination_ratio) {
            
// source has a taller ratio
            
$temp_width = (int)($destination_height $source_ratio);
            
$temp_height $destination_height;
            
$destination_x = (int)(($destination_width $temp_width) / 2);
            
$destination_y 0;
        } else {
            
// source has a wider ratio
            
$temp_width $destination_width;
            
$temp_height = (int)($destination_width $source_ratio);
            
$destination_x 0;
            
$destination_y = (int)(($destination_height $temp_height) / 2);
        }
        
$source_x 0;
        
$source_y 0;
        
$new_destination_width $temp_width;
        
$new_destination_height $temp_height;
    }
    
$destination_image imagecreatetruecolor($destination_width$destination_height);
    if (
$type 1) {
        
imagefill($destination_image00imagecolorallocate ($destination_image000));
    }
    
imagecopyresampled($destination_image$source_image$destination_x$destination_y$source_x$source_y$new_destination_width$new_destination_height$source_width$source_height);
    return 
$destination_image;
}
?>

Tags: #php #resize-image #resizing-images #gd #crop-to-fit #letterbox

Older Posts »« Newer Posts