I recently had an interesting request on a client project: how can we resize animated gifs without losing the animation in the thumbnails? WordPress lets you upload animated gifs, but as soon as it resizes them the thumbnails are decidedly less animated.
Generating a thumbnail could take your gif from this:
…to this:
As a purveyor of fine, animated gifs, I can tell you that an animated gif with no animation is no gif worth having!
Fortunately, there’s a free, open-source library called Gifsicle designed to manipulate animated gifs. The fun part is using Gifsicle to resize your gifs for you, automatically.
Determining image sizes
The hardest part of resizing any image, animated or not, is determining its target dimensions and cropping. In WordPress, we can make the platform do the heavy lifting, using image_resize_dimensions()
:
1 2 3 4 5 6 7 8 9 10 |
$source = getimagesize( $file ); // $width and $height represent the target dimensions. $dimensions = image_resize_dimensions( $source[0], $source[1], $width, $height, true ); |
Luckily, the return value of image_resize_dimensions()
is an array that closely matches the arguments of PHP’s imagecopyresampled()
function; if you’re not using WordPress pretty much any resizing tutorial that has you preparing values for imagecopyresampled()
should give you the values you need:
$dst_x
x-coordinate of destination point.$dst_y
y-coordinate of destination point.$src_x
x-coordinate of source point.$src_y
y-coordinate of source point.$dst_w
Destination width.$dst_h
Destination height.$src_w
Source width.$src_h
Source height.
Resizing the images with Gifsicle
There are libraries available for interacting with Gifsicle from PHP, but those who have seen my Building for the PHP Command Line Interface talk know that we can actually execute system commands from within PHP, using functions like passthru()
. Once Gifsicle is installed on your server, using it from within PHP will look something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
try { if ( ! exec( 'which gifsicle' ) ) { throw new Exception( 'Unable to find Gifsicle!' ); } $command = 'gifsicle {some args}'; // Execute the command. passthru( $command ); } catch ( Exception $e ) { error_log( 'Unable to resize gif: ' . $e->getMessage() ); } |
This lets us execute Gifsicle the same as we would via the command line (because – technically – we are)!
Building the arguments
Now comes the fun part: assembling the right arguments. Since the majority of you reading this page are more likely just looking for the answer, I’ll lead with that, then break it down:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$command = sprintf( 'gifsicle --crop %s --resize %s %s > %s', escapeshellarg( sprintf( '%d,%d-%d,%d', $src_x, $src_y, $src_w - $src_x, $src_h - $src_y ) ), escapeshellarg( sprintf( '%dx%d', $dst_w, $dst_h ) ), escapeshellarg( $file ), escapeshellarg( $destination ) ); |
Well, that was easy, wasn’t it? In case you’re not well-versed in juggling arguments and using PHP to assemble CLI commands, we’ll apply some real numbers: let’s say we have this gif of America’s Sweetheart, Nick Offerman:
The original dimensions of this image are 500×281, approximately a 16:9 aspect ratio. Now, let’s say we want a square version of that same image, at 150×150. In order to achieve this, we need to perform two operations: resizing the image to fit as much of it as possible into a 150×150 box, then crop off anything outside that area.
Fortunately, resizing the image is easy: Gifsicle has a --resize
option, and the math that led us to our getimagesize()
-compatible calculations up above would tell us that to fit a 500×281 image in a 150×150 box, our target dimensions would be 267×150.
With these numbers at hand, we can ask Gifsicle to resize our image to 267×150:
1 |
$ gifsicle --resize 267x150 original.gif > resized.gif |
Of course, that’s just going to give us a Gif that’s 150px tall, but not a 150×150 square. To do this, we’ll need to add the --crop
option:
1 2 |
gifsicle --crop 0,0-281x281 --resize 150x150 \ original.gif > resized-cropped.gif |
Did you catch that “0,0” at the beginning of the --crop
option? By default, Gifsicle will want to draw the crop from the top-left corner, giving us something like this (I’ve masked the cropped area rather than directly cropping the image):
That’s not bad, but Jimmy Fallon’s reaction to Offerman’s breakdancing helps sell the gif. If we can calculate the starting point along the horizontal axis to get the center, we can get a much more appealing image.
It’s worth mentioning that, according to the Gifsicle documentation, “[cropping] takes place before any rotation, flipping, resizing, or positioning.” This means that our calculations around the center of the image will need to be based on the source image’s coordinates, not the target size. In the case of our Offerman gif, that means drawing a 281×281 square smack-dab in the center:
Calculating the starting point is just a matter of doing some simple math: ($src_width - $dest_width) / 2
, or (500 - 281) / 2
, which gives us 109. With that value, we can tell Gifsicle to start the cropping at coordinates (109, 0):
1 2 |
gifsicle --crop 109,0+281x281 --resize 150x150 original.gif \ > resized-cropped-centered.gif |
Now that we have the hard-coded version, we’ll break this up into placeholders (using proper escaping for shell arguments, of course) and we’ll find ourselves with the full, variable-filled version we had before!
Gotchas
You may have noticed there’s a bit of math involved with resizing images; it’s nothing that requires an advanced degree, but it’s still not how I’d like to spend my afternoon. Even if you’re not using WordPress, I’d encourage you to look at the image_resize_dimensions()
function and how it’s implemented; this is a geometry problem that’s been solved before, so there’s no need to reinvent the wheel.
You may also notice that there was some rounding going on, even in my examples. In my testing, I’ve found that gifs with even dimensions (e.g. 500×280 instead of 500×281) tend to resize much cleaner and with less distortion. The smaller you go, the harder Gifsicle has to work to round everything out; in fact, you may find that gifs with smaller dimensions sometimes carry a larger filesize.
Finally, I’ll remind you that while gifs can be a lot of fun, they’re not always the best way to convey a message. A page full of gifs can make even a beefy computer or device strain itself, so just because you can bombard your users with animations doesn’t mean that you should (at least, if you care about user experience). Also, as with any media meant to convey meaning on the web, please make sure you’re including relevant alternative text.
Now, go forth and gif!
chzumbrunnen
Thanks for this great article. Hopefully the plugin get’s released or another one that someone creates with the help of this article. Would like to do it myself, but sadly have other priorities and am not a developer anyway…