
UPDATE: I’ve added support for PNG alpha transparency which makes this class significantly more useful. Changelog in the class code below.
I keep misplacing this class so I figured I’d post it here so that I know where it is.
I wrote/assembled this class because I wasn’t happy with any of the image manipulation libraries out there. They are either way too complex with a steep learning curve or didn’t work all that great. (I didn’t see any reason to load up a class with 3,000 lines of code just to resize or rotate an image.)
So here it is, a simple class that allows you to open an image, perform common operations, and save. There are no bells and whistles or anything exciting here. Just a collection of commonly used image manipulation functions: Rotate, Flip, Resize, Thumbnail (square and regular), and Crop.
A lot of the class code is original but some of it has been gathered over my life as a programmer from long lost sources. If you recognize your work, leave a comment and I’d be happy to cite you!
The concept is pretty simple. You open an image, and then perform your operations before saving the output, or writing it to a different file. The code is not optimized for batch processing of images, so be warned. It is mainly for when you are accepting uploads of profile photos or similar and need to resize it and generate thumbnails and all that shtuff. Enjoy and comment any questions/improvements/problems/etc.!
Creation – Getting Started
Before you can start working on an image, you must create an instance of the Image class.
All of these examples use Ephective’s logo as the test image:

$I = new Image('ephective.png');
You can also create an empty instance of Image by doing “$I = new Image();” and open, or set the image at a later time:
- $I->open( file ) – Open a file from disk
- $I->resource( image resource ) – Set an image resource
Saving
Before I’ll go on to explaining all the functions, you should first know how to save your work.
If you have opened a file from disk, you will probably want to save your changes back to that file.
// this will save your changes back to the file that you've opened // if you didn't open a file (passed a resource) this function will not work $I->save()
The second option is to write your changes to a different file.
$I->write( 'my_new_file.png' )
You could also get the image resource and do some additional custom processing on your own:
// now contains the image resource, you could write it to disk if you'd like $res = $I->resource();
Rotate: $I->rotate( $degrees [ , $bkg ] )
Now that we have the image loaded, we can begin using the simple functions to manipulate it.
Rotate can rotate the image by any number of degrees. If you rotate by anything except a multiple of 90, you will end up with the image appearing on an angle. This will automatically resize the dimensions of the image and set background color using the optional $bkg (defaults to black). The color must be allocated with imagecolorallocate or whatever it is… an example exists at the end of this page.
// rotates the image 38 degrees $I->rotate(38);
Before:
After: 
Notice how the image is larger and has a black background fill.
There are several shortcut functions to rotate an image and an additional special auto_rotate function.
- $I->rotate( $degrees [ , $bkg=0 ] )
- $I->rotate_left()
- $I->rotate_right()
- $I->rotate_180() – This turns the image upside down
- $I->auto_rotate() – There is also a special function called auto_rotate. auto_rotate will check to see if there is any orientation information in the exif data for the image. If there is, the image will automatically be rotated to the correct orientation. Note: This function only works if you have the PHP exif extension.
Here is an example of rotate_left:
$I->rotate_left();
rotate_left:
rotate_right:
rotate_180: 
Full Snippet:
require 'Image.php';
$I = new Image('ephective.png');
$I->rotate(38);
$I->save(); // will save the changes back to the file
$I->destroy(); // free the memory
Flip: $I->flip( $bFlipH [ , $bFlipV=false ] )
Flip will mirror an image in a certain direction—either horizontally or vertically.
There are a few shortcut functions to flip an image.
- $I->flip( $bFlipH [ , $bFlipV=false ] )
- $I->flip_h()
- $I->flip_v()
- $I->flip_both() – This is the same as rotate_180 only much more processor intense. Just use rotate_180
require 'Image.php';
$I = new Image('ephective.png');
$I->flip_v();
$I->save(); // will save the changes back to the file
Before:
flip_v:
flip_h:
flip_both: 
Resize: $I->resize( $newdim [ , $square=false [ , $bHeight=false [ , $resample=true ] ] ] )
You can only resize images and maintain proportions. I figured, in most day-to-day image operations you are resizing photos and such, so why would you want to squish the picture? So, to resize, you will only need to specify the largest dimension you want to allow. To make that a little more clear, think of Facebook profile photos. They always resize to a width of around 200 pixels. This results in some photos being much taller than others, but they are always 200 px wide.
The basic code to do something similar with Image is:
$I->resize ( 200 );
There are the following resize functions:
- $I->resize( $newdim [ , $square=false [ , $bHeight=false [ , $resample=true ] ] ] ) – By default, resize performs the same operation as width. Setting bHeight to true would make resize perform the same as height.
- $I->width( $newdim [ , $square=false [ , $resample=true ] ] )
- $I->height( $newdim [ , $square=false [ , $resample=true ] ] )
- $I->resize_1600()
- $I->resize_1200()
- $I->resize_1024()
- $I->resize_800()
- $I->resize_640()
When settings the $newdim, you can give an actual pixel count ($newdim=200;), a decimal percentage less than 1 ($newdim=0.5;), or a string percentage ($newdim=’50%’;).
Before:
width(60) pixels:
width(‘50%’) string percent:
width(.3) decimal: 
Thumbnail: $I->thumbnail( $dest, $newdim [ , $square=NULL [ , $bHeight=false [ , $out=false [ , $resample=true ] ] ] ] )
Thumbnail is identical to resize except thumbnail does not alter the classes image resource at all. This means that the original image is left untouched.
Crop: $I->crop( $top [ , $right=0 [ , $bottom=0 [ , $left=0 ] ] ] )
Instead of specifying a selection inside the current image, crop works by letting you specify how much to trim off the edges. So if you wanted to trim 50 pixels off the top and bottom sides of the image, you could do the following:
$I->crop(50, 0, 50);
There are the following resize functions:
- $I->crop( $top [ , $right=0 [ , $bottom=0 [ , $left=0 ] ] ] )
- $I->crop_top( $px )
- $I->crop_right( $px )
- $I->crop_bottom( $px )
- $I->crop_left( $px )
- $I->crop_h( $px ) – Top and bottom
- $I->crop_v( $px ) – Left and right
- $I->crop_all( $px ) – All sides (contract)
Before:
crop_all(10):
crop(18, 25, 28, 20):
crop_v(20):
crop_h(20):
crop_all(20): 
Combining Modifications
You can combine as many modifications as you’d like:
$I = new Image('ephective.png');
// loop through and incrementally change the color and rotate
for ( $i=0; $i < 255; $i+=10 )
{
$color = imagecolorallocatealpha( $I->resource(), 255, $i, $i, 0 );
$I->rotate(5, $color);
}
// each rotation increases the images dimensions, shrink it down to a reasonable size
$I->width(.3);
// write the output to a separate file
$I->write('ephective-combine.png');
// free the memory
$I->destroy();

// v.0.2.1
/*
CHANGELOG
0.2.1 -Added output() screen, in addition to save/write
-Added support for PNG transparency
-Made contenttype the PNG by default
*/
class Image
{
var $res = NULL;
var $source = NULL;
var $contenttype = IMAGETYPE_PNG;
/***
* Constructor
* $src_or_resource: src is the path to an image. If it exists, the image will be automatically opened
* can also be an already created image resource
*/
function Image ($src_or_resource=NULL)
{
if( ! is_null($src_or_resource) )
if ( is_resource($src_or_resource) )
$this->resource($src_or_resource);
else
$this->open($src_or_resource);
}
/***
* Rotate the active image
* $degrees: number of degrees to spin the image, if this is something like
* 45, it will leave gaps in the frame which will be filled in by $bkg
* $bkg: If image is rotated in a way that does not fill the frame, this is the color that will be used
*/
function rotate ($degrees, $bkg='0')
{
$im = imagerotate( $this->res, $degrees, $bkg );
imagedestroy($this->res);
$this->res = $im;
}
/***
* Rotate image right
* Shortcut function that will rotate the image to the right
*/
function rotate_right() { $this->rotate(270); }
/***
* Rotate image 180 degrees
* Shortcut function that will rotate the image 180 degrees
*/
function rotate_180() { $this->rotate(180); }
/***
* Rotate image left
* Shortcut function that will rotate the image to the left
*/
function rotate_left() { $this->rotate(90); }
/***
* Attempt to automatically rotate the image based on exif data
* a lot of digital cameras store orientation data of the camera
* this will use that to automatically fix an images orientation
*
* Note: This will only work if you have the function exif_read_data
* which is part of PHPs exif data extension
*/
function auto_rotate() {
if( ! function_exists('exif_read_data') ) return false;
$exif = exif_read_data($this->source);
$ort = $exif['IFD0']['Orientation'];
switch($ort) // http://www.impulseadventure.com/photo/exif-orientation.html
{
case 1: // regular, do nothing
break;
case 2:
return $this->flip_h();
case 3:
return $this->rotate_180();
case 4:
return $this->flip_v();
case 5:
return ($this->flip_h() && $this->rotate_right());
case 6:
return $this->rotate_right();
case 7:
return ($this->flip_h() && $this->rotate_left());
case 8:
return $this->rotate_left();
}
}
/***
* Will flip an image (mirror it)
*
* $bFlipH: flip horizontal (true/false)
* $bFlipV: flip vertical (true/false)
*
*/
function flip($bFlipH, $bFlipV=false)
{
$imgsrc = $this->res;
$width = imagesx($imgsrc);
$height = imagesy($imgsrc);
$imgdest = imagecreatetruecolor($width, $height);
$this->prepare($imgdest);
for ($x=0 ; $x<$width ; $x++)
{
for ($y=0 ; $y<$height ; $y++)
{
if ($bFlipH && $bFlipV) imagecopy($imgdest, $imgsrc, $width-$x-1, $height-$y-1, $x, $y, 1, 1);
else if ($bFlipH) imagecopy($imgdest, $imgsrc, $width-$x-1, $y, $x, $y, 1, 1);
else if ($bFlipV) imagecopy($imgdest, $imgsrc, $x, $height-$y-1, $x, $y, 1, 1);
}
}
$this->res = $imgdest;
imagedestroy($imgsrc);
}
/***
* Shortcut functions to flip
*/
function flip_h() { $this->flip(true, false); }
function flip_v() { $this->flip(false, true); }
function flip_both() { $this->flip(true, true); }
/***
* Resizes an image to fit in a certain size
*
* $newdim: This is the largest dimension the image can contain in
* pixels. If either of the measurements are larger than this size,
* the image will be scaled
*
* $square: (true/false) make the new image square instead of keeping the dimensions
*
* $resample: high quality resize? it is good to turn this off if you are doing
* lots of conversions and quality isn't a huge issue
* resample on/off is a noticeable difference in time
*/
function resize($newdim, $square=false, $bHeight=false, $resample=true) {
$src_width = imagesx( $this->res );
$src_height = imagesy( $this->res );
$src_w = $src_width;
$src_h = $src_height;
$src_x = 0;
$src_y = 0;
$percent = false;
if ( $newdim < 1 )
$percent = $newdim;
elseif ( substr($newdim, -1) == '%' )
$percent = substr($newdim, 0, -1)/100;
if ( false !== $percent )
$newdim = round( ($bHeight ? ($src_height*$percent) : ($src_width*$percent) ) );
if ($square)
{
$dst_w = $newdim;
$dst_h = $newdim;
if ( ! $bHeight )
{
$src_x = ceil( ( $src_width - $src_height ) / 2 );
$src_w = $src_height;
$src_h = $src_height;
}
else
{
$src_y = ceil( ( $src_height - $src_width ) / 2 );
$src_w = $src_width;
$src_h = $src_width;
}
}
else
{
if ( ! $bHeight )
{
$dst_w = $newdim;
$dst_h = floor( $src_height * ($dst_w / $src_width) );
}
else
{
$dst_h = $newdim;
$dst_w = floor( $src_width * ($dst_h / $src_height) );
}
}
$dst_im = imagecreatetruecolor($dst_w,$dst_h);
$this->prepare($dst_im);
if ($resample)
imagecopyresampled($dst_im, $this->res, 0, 0, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h);
else
imagecopyresized($dst_im, $this->res, 0, 0, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h);
imagedestroy($this->res);
$this->res = $dst_im;
}
/***
* Shortcut functions to resize an image to certain, standard sizes
*/
function width($newdim, $square=false, $resample=true) { $this->resize($newdim, $square, false, $resample); }
function height($newdim, $square=false, $resample=true) { $this->resize($newdim, $square, true, $resample); }
function resize_1600($square=false) { $this->resize(1600, $square); }
function resize_1200($square=false) { $this->resize(1200, $square); }
function resize_1024($square=false) { $this->resize(1024, $square); }
function resize_800($square=false) { $this->resize(800, $square); }
function resize_640($square=false) { $this->resize(640, $square); }
/***
* Generate a thumbnail from the loaded image
*
* $dest: The destination file on disk to save new thumbnail
*
* $out: The type of image to create (uses PHPs standard image constants for PNG, JPG, GIF)
*
* $newdim: This is the largest dimension the image can contain in
* pixels. If either of the measurements are larger than this size,
* the image will be scaled
*
* $square: create a square thumbnail
*
* $resample: high quality resize? it is good to turn this off if you are doing
* lots of conversions and quality isn't a huge issue
*/
function thumbnail($dest, $newdim, $square=false, $bHeight=false, $out=NULL, $resample=true) {
$src_width = imagesx($this->res);
$src_height = imagesy($this->res);
$src_w = $src_width;
$src_h = $src_height;
$src_x = 0;
$src_y = 0;
$percent = false;
if ( $newdim < 1 )
$percent = $newdim;
elseif ( substr($newdim, -1) == '%' )
$percent = substr($newdim, 0, -1)/100;
if ( false !== $percent )
$newdim = round( ($bHeight ? ($src_height*$percent) : ($src_width*$percent) ) );
if ($square)
{
$dst_w = $largest_dim;
$dst_h = $largest_dim;
if ( ! $bHeight )
{
$src_x = ceil( ($src_width - $src_height) / 2 );
$src_w = $src_height;
$src_h = $src_height;
}
else
{
$src_y = ceil( ($src_height - $src_width) / 2 );
$src_w = $src_width;
$src_h = $src_width;
}
}
else
{
if ( ! $bHeight )
{
$dst_w = $largest_dim;
$dst_h = floor( $src_height * ($dst_w / $src_width) );
}
else
{
$dst_h = $largest_dim;
$dst_w = floor( $src_width * ($dst_h / $src_height) );
}
}
$dst_im = imagecreatetruecolor($dst_w,$dst_h);
$this->prepare($dst_im);
if ($resample)
imagecopyresampled($dst_im, $this->res, 0, 0, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h);
else
imagecopyresized($dst_im, $this->res, 0, 0, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h);
if ( is_null($out) )
$out = $this->contenttype;
switch($out)
{
case IMAGETYPE_PNG:
imagepng($dst_im, $dest);
break;
case IMAGETYPE_GIF:
imagegif($dst_im, $dest);
break;
case IMAGETYPE_JPEG:
imagejpeg($dst_im, $dest);
break;
}
imagedestroy($dst_im);
}
/***
* Shortcut functions to some standard thumbnail sizes
*/
function thumbnail_xsmall($dest, $out, $square=false) { $this->thumbnail($dest, $out, 60, $square); }
function thumbnail_small($dest, $out, $square=false) { $this->thumbnail($dest, $out, 80, $square); }
function thumbnail_medium($dest, $out, $square=false) { $this->thumbnail($dest, $out, 160, $square); }
function thumbnail_large($dest, $out, $square=false) { $this->thumbnail($dest, $out, 300, $square); }
function thumbnail_xlarge($dest, $out, $square=false) { $this->thumbnail($dest, $out, 512, $square); }
/***
* Crop an image by N pixels
*
* Same order as CSS property
*
* This will crop an image by the number of pixels you specify.
* For example, to crop 10 pixels off the bottom and left sides
* you would do crop(10, 0, 10)
*/
function crop ($top, $right=0, $bottom=0, $left=0)
{
$w = imagesx($this->res);
$h = imagesy($this->res);
$nw = $w - ($left+$right);
$nh = $h - ($top+$bottom);
$im = imagecreatetruecolor( $nw, $nh );
$this->prepare($im);
imagecopy($im, $this->res, 0, 0, $left, $top, $nw, $nh );
imagedestroy($this->res);
$this->res = $im;
}
/***
* Shortcut functions to crop
*/
function crop_top($px) { $this->crop($px); }
function crop_right($px) { $this->crop(0, $px, 0, 0); }
function crop_bottom($px) { $this->crop(0, 0, $px, 0); }
function crop_left($px) { $this->crop(0, 0, 0, $px); }
function crop_h($px) { $this->crop($px, 0, $px, 0); }
function crop_v($px) { $this->crop(0, $px, 0, $px); }
function crop_all($px) { $this->crop($px, $px, $px, $px); }
/***
* Open an image from a file on disk
*/
function open($src)
{
$this->source = $src;
switch( ( $this->contenttype = exif_imagetype($src) ) )
{
case IMAGETYPE_PNG:
$this->res = imagecreatefrompng($src);
break;
case IMAGETYPE_GIF:
$this->res = imagecreatefromgif($src);
break;
case IMAGETYPE_JPEG:
$this->res = imagecreatefromjpeg($src);
break;
}
$this->prepare($this->res);
}
// takes an image resource and a content type and prepares the resource
function prepare($res, $contenttype=NULL)
{
if ( is_null($contenttype) ) $contenttype = $this->contenttype;
if ( $contenttype == IMAGETYPE_PNG )
{
imagesavealpha($res, true);
imagealphablending($res, false);
}
}
/***
* Get/set image resource
*/
function resource ($res=NULL)
{
if ( is_null($res) )
return $this->res;
else
$this->res = $res;
}
/***
* Save the image back to the original file
*/
function save($out=NULL)
{
if ( ! is_null($this->source) )
$this->write($this->source, $out);
}
/***
* Save the image to a different location
*/
function write($dest, $out=NULL)
{
if(is_null($out))
$out = $this->contenttype;
switch($out)
{
case IMAGETYPE_PNG:
imagepng($this->res, $dest);
break;
case IMAGETYPE_GIF:
imagegif($this->res, $dest);
break;
case IMAGETYPE_JPEG:
imagejpeg($this->res, $dest);
break;
}
}
/***
* Output the image to the stream (browser)
*/
function output($out=NULL)
{
switch($out)
{
default:
case IMAGETYPE_PNG:
$contenttype = 'png';
break;
case IMAGETYPE_GIF:
$contenttype = 'gif';
break;
case IMAGETYPE_JPEG:
$contenttype = 'jpeg';
break;
}
header('Content-type: image/'.$contenttype);
$this->write(NULL, $out);
}
/***
* Kill self
*/
function destroy()
{
imagedestroy($this->res);
$this->source=NULL;
$this->contenttype = NULL;
}
}
if( ! function_exists('exif_imagetype') )
{
function exif_imagetype ( $f )
{
if ( false !== ( list(,,$type,) = getimagesize( $f ) ) )
return $type;
return IMAGETYPE_PNG; // meh
}
}
Wow! I was just about to sit down to write this (and my PHP coding is about as good as you’d expect from an iPhone programmer). Thanks. You now have at least 2 subscribers. Cheers.
Reply
Cory reply on January 12th, 2010 02.02.16:
Ut oh. iPhone coder… we don’t like yer kind ’round here. jk ;] Thanks for reading and you can claim your #2 subscriber tshirt once I reach 10,000 lol
Reply
can this be used with transparent backgrounds?
Reply
Cory reply on January 15th, 2010 07.28.39:
You should be able to. Just load the image as you normally would, and then you would be able to manipulate the image resource however you’d like.
So something like the following should create a slightly rotated image with a transparent background… (note: I didn’t test this code.)
$I = new Image( ... path ... ); imagesavealpha($I->res, true); imagealphablending($I->res, false); // this should be a color that doesn't exist in your image // it will be made the transparent color $transcolor = imagecolorallocatealpha($I->res, 200, 200, 200, 127); $I->rotate(15, $transcolor); $I->write('new.png', IMAGETYPE_PNG);Reply
Cory reply on January 28th, 2010 03.23.28:
There were some problems with PNG and transparency (with cropping, for example).
The script should now completely support PNG alpha transparency. (Tested with resize and crop)
Reply
Cory, great collection of utilities, I need a little hand holding however.
I am using the code so far like so.
$img = new Image();
$img->open($_FILES["uploadedFile"]["tmp_name"]);
$img->auto_rotate();
$img->width(320);
$img->write(“images/fullImg/” . $usersName .”–”.date(Ymdhis) . “.png”);
but when I try to make a $img->thumbnail(“images/tNails/” . $usersName.”–”.date(Ymdhis) . “.png”,115); the file is not created. What am I missing?
Thanks
e
Reply
Cory reply on February 19th, 2010 01.38.14:
First, you can pass the file name with the Image constructor, so: new Image( $filename )
Second, it looks like you’re handling a file upload. PHP can’t directly access file uploads in their temporary location. You will have to use move_uploaded_file to move the file someplace you can work on it. See PHP file uploads for more info in the PHP manual.
Reply
absolutely fantastic! I had been searching for ages to do some of this (especially flipping the image). works a treat!
Reply