
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:

1 |
$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.
The second option is to write your changes to a different file.
1 |
$I->write( 'my_new_file.png' ) |
You could also get the image resource and do some additional custom processing on your own:
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.
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:
rotate_left:
rotate_right:
rotate_180: 
Full Snippet:
3 |
$I = new Image('ephective.png'); |
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
3 |
$I = new Image('ephective.png'); |
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:
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:
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:
01 |
$I = new Image('ephective.png'); |
04 |
for ( $i=0; $i < 255; $i+=10 ) |
06 |
$color = imagecolorallocatealpha( $I->resource(), 255, $i, $i, 0 ); |
07 |
$I->rotate(5, $color); |
14 |
$I->write('ephective-combine.png'); |

014 |
var $contenttype = IMAGETYPE_PNG; |
021 |
function Image ($src_or_resource=NULL) |
023 |
if( ! is_null($src_or_resource) ) |
024 |
if ( is_resource($src_or_resource) ) |
025 |
$this->resource($src_or_resource); |
027 |
$this->open($src_or_resource); |
036 |
function rotate ($degrees, $bkg='0') |
038 |
$im = imagerotate( $this->res, $degrees, $bkg ); |
039 |
imagedestroy($this->res); |
047 |
function rotate_right() { $this->rotate(270); } |
053 |
function rotate_180() { $this->rotate(180); } |
059 |
function rotate_left() { $this->rotate(90); } |
069 |
function auto_rotate() { |
070 |
if( ! function_exists('exif_read_data') ) return false; |
071 |
$exif = exif_read_data($this->source); |
073 |
$ort = $exif['IFD0']['Orientation']; |
079 |
return $this->flip_h(); |
081 |
return $this->rotate_180(); |
083 |
return $this->flip_v(); |
085 |
return ($this->flip_h() && $this->rotate_right()); |
087 |
return $this->rotate_right(); |
089 |
return ($this->flip_h() && $this->rotate_left()); |
091 |
return $this->rotate_left(); |
102 |
function flip($bFlipH, $bFlipV=false) |
104 |
$imgsrc = $this->res; |
105 |
$width = imagesx($imgsrc); |
106 |
$height = imagesy($imgsrc); |
107 |
$imgdest = imagecreatetruecolor($width, $height); |
108 |
$this->prepare($imgdest); |
110 |
for ($x=0 ; $x<$width ; $x++) |
112 |
for ($y=0 ; $y<$height ; $y++) |
114 |
if ($bFlipH && $bFlipV) imagecopy($imgdest, $imgsrc, $width-$x-1, $height-$y-1, $x, $y, 1, 1); |
115 |
else if ($bFlipH) imagecopy($imgdest, $imgsrc, $width-$x-1, $y, $x, $y, 1, 1); |
116 |
else if ($bFlipV) imagecopy($imgdest, $imgsrc, $x, $height-$y-1, $x, $y, 1, 1); |
120 |
$this->res = $imgdest; |
121 |
imagedestroy($imgsrc); |
127 |
function flip_h() { $this->flip(true, false); } |
128 |
function flip_v() { $this->flip(false, true); } |
129 |
function flip_both() { $this->flip(true, true); } |
144 |
function resize($newdim, $square=false, $bHeight=false, $resample=true) { |
145 |
$src_width = imagesx( $this->res ); |
146 |
$src_height = imagesy( $this->res ); |
148 |
$src_h = $src_height; |
155 |
elseif ( substr($newdim, -1) == '%' ) |
156 |
$percent = substr($newdim, 0, -1)/100; |
158 |
if ( false !== $percent ) |
159 |
$newdim = round( ($bHeight ? ($src_height*$percent) : ($src_width*$percent) ) ); |
167 |
$src_x = ceil( ( $src_width - $src_height ) / 2 ); |
168 |
$src_w = $src_height; |
169 |
$src_h = $src_height; |
173 |
$src_y = ceil( ( $src_height - $src_width ) / 2 ); |
183 |
$dst_h = floor( $src_height * ($dst_w / $src_width) ); |
188 |
$dst_w = floor( $src_width * ($dst_h / $src_height) ); |
191 |
$dst_im = imagecreatetruecolor($dst_w,$dst_h); |
192 |
$this->prepare($dst_im); |
194 |
imagecopyresampled($dst_im, $this->res, 0, 0, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h); |
196 |
imagecopyresized($dst_im, $this->res, 0, 0, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h); |
198 |
imagedestroy($this->res); |
199 |
$this->res = $dst_im; |
205 |
function width($newdim, $square=false, $resample=true) { $this->resize($newdim, $square, false, $resample); } |
206 |
function height($newdim, $square=false, $resample=true) { $this->resize($newdim, $square, true, $resample); } |
207 |
function resize_1600($square=false) { $this->resize(1600, $square); } |
208 |
function resize_1200($square=false) { $this->resize(1200, $square); } |
209 |
function resize_1024($square=false) { $this->resize(1024, $square); } |
210 |
function resize_800($square=false) { $this->resize(800, $square); } |
211 |
function resize_640($square=false) { $this->resize(640, $square); } |
229 |
function thumbnail($dest, $newdim, $square=false, $bHeight=false, $out=NULL, $resample=true) { |
230 |
$src_width = imagesx($this->res); |
231 |
$src_height = imagesy($this->res); |
233 |
$src_h = $src_height; |
240 |
elseif ( substr($newdim, -1) == '%' ) |
241 |
$percent = substr($newdim, 0, -1)/100; |
243 |
if ( false !== $percent ) |
244 |
$newdim = round( ($bHeight ? ($src_height*$percent) : ($src_width*$percent) ) ); |
248 |
$dst_w = $largest_dim; |
249 |
$dst_h = $largest_dim; |
252 |
$src_x = ceil( ($src_width - $src_height) / 2 ); |
253 |
$src_w = $src_height; |
254 |
$src_h = $src_height; |
258 |
$src_y = ceil( ($src_height - $src_width) / 2 ); |
267 |
$dst_w = $largest_dim; |
268 |
$dst_h = floor( $src_height * ($dst_w / $src_width) ); |
272 |
$dst_h = $largest_dim; |
273 |
$dst_w = floor( $src_width * ($dst_h / $src_height) ); |
276 |
$dst_im = imagecreatetruecolor($dst_w,$dst_h); |
277 |
$this->prepare($dst_im); |
279 |
imagecopyresampled($dst_im, $this->res, 0, 0, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h); |
281 |
imagecopyresized($dst_im, $this->res, 0, 0, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h); |
284 |
$out = $this->contenttype; |
288 |
imagepng($dst_im, $dest); |
291 |
imagegif($dst_im, $dest); |
295 |
imagejpeg($dst_im, $dest); |
298 |
imagedestroy($dst_im); |
304 |
function thumbnail_xsmall($dest, $out, $square=false) { $this->thumbnail($dest, $out, 60, $square); } |
305 |
function thumbnail_small($dest, $out, $square=false) { $this->thumbnail($dest, $out, 80, $square); } |
306 |
function thumbnail_medium($dest, $out, $square=false) { $this->thumbnail($dest, $out, 160, $square); } |
307 |
function thumbnail_large($dest, $out, $square=false) { $this->thumbnail($dest, $out, 300, $square); } |
308 |
function thumbnail_xlarge($dest, $out, $square=false) { $this->thumbnail($dest, $out, 512, $square); } |
319 |
function crop ($top, $right=0, $bottom=0, $left=0) |
321 |
$w = imagesx($this->res); |
322 |
$h = imagesy($this->res); |
323 |
$nw = $w - ($left+$right); |
324 |
$nh = $h - ($top+$bottom); |
325 |
$im = imagecreatetruecolor( $nw, $nh ); |
328 |
imagecopy($im, $this->res, 0, 0, $left, $top, $nw, $nh ); |
330 |
imagedestroy($this->res); |
337 |
function crop_top($px) { $this->crop($px); } |
338 |
function crop_right($px) { $this->crop(0, $px, 0, 0); } |
339 |
function crop_bottom($px) { $this->crop(0, 0, $px, 0); } |
340 |
function crop_left($px) { $this->crop(0, 0, 0, $px); } |
341 |
function crop_h($px) { $this->crop($px, 0, $px, 0); } |
342 |
function crop_v($px) { $this->crop(0, $px, 0, $px); } |
343 |
function crop_all($px) { $this->crop($px, $px, $px, $px); } |
350 |
$this->source = $src; |
351 |
switch( ( $this->contenttype = exif_imagetype($src) ) ) |
354 |
$this->res = imagecreatefrompng($src); |
357 |
$this->res = imagecreatefromgif($src); |
360 |
$this->res = imagecreatefromjpeg($src); |
364 |
$this->prepare($this->res); |
368 |
function prepare($res, $contenttype=NULL) |
370 |
if ( is_null($contenttype) ) $contenttype = $this->contenttype; |
372 |
if ( $contenttype == IMAGETYPE_PNG ) |
374 |
imagesavealpha($res, true); |
375 |
imagealphablending($res, false); |
382 |
function resource ($res=NULL) |
393 |
function save($out=NULL) |
395 |
if ( ! is_null($this->source) ) |
396 |
$this->write($this->source, $out); |
402 |
function write($dest, $out=NULL) |
405 |
$out = $this->contenttype; |
410 |
imagepng($this->res, $dest); |
413 |
imagegif($this->res, $dest); |
416 |
imagejpeg($this->res, $dest); |
424 |
function output($out=NULL) |
430 |
$contenttype = 'png'; |
433 |
$contenttype = 'gif'; |
436 |
$contenttype = 'jpeg'; |
440 |
header('Content-type: image/'.$contenttype); |
441 |
$this->write(NULL, $out); |
449 |
imagedestroy($this->res); |
451 |
$this->contenttype = NULL; |
455 |
if( ! function_exists('exif_imagetype') ) |
457 |
function exif_imagetype ( $f ) |
459 |
if ( false !== ( list(,,$type,) = getimagesize( $f ) ) ) |
461 |
return IMAGETYPE_PNG; |