commit 580b3a46f94f4ad41910dc0c3276c4201ea2d07f Author: Mikael Roos Date: Wed Apr 25 15:49:09 2012 +0200 initial release diff --git a/CImage.php b/CImage.php new file mode 100644 index 0000000..0a6f237 --- /dev/null +++ b/CImage.php @@ -0,0 +1,273 @@ +pathToImage = $pathToImage; + $this->fileExtension = pathinfo($this->pathToImage, PATHINFO_EXTENSION); + $this->newWidth = $newWidth; + $this->newHeight = $newHeight; + $this->keepRatio = $keepRatio; + $this->crop = $crop; + $this->saveFolder = $saveFolder; + $this->newName = $newName; + } + + + /** + * Raise error, enables to implement a selection of error methods. + * + * @param $message string the error message to display. + */ + public function RaiseError($message) { + throw new Exception($message); + } + + + /** + * Create filename to save file in cache. + */ + public function CreateFilename() { + $parts = pathinfo($this->pathToImage); + $crop = $this->crop ? '_c_' : null; + return $this->saveFolder . '/' . $parts['filename'] . '_' . round($this->newWidth) . '_' . round($this->newHeight) . $crop . '.' . $parts['extension']; + } + + + /** + * Init and do some sanity checks before any processing is done. Throws exception if not valid. + */ + public function Init() { + is_null($this->newWidth) or is_numeric($this->newWidth) or $this->RaiseError('Width not numeric'); + is_null($this->newHeight) or is_numeric($this->newHeight) or $this->RaiseError('Height not numeric'); + is_readable($this->pathToImage) or $this->RaiseError('File does not exist.'); + in_array($this->fileExtension, $this->validExtensions) or $this->RaiseError('Not a valid file extension.'); + is_null($this->saveFolder) or is_writable($this->saveFolder) or $this->RaiseError('Save directory does not exist or is not writable.'); + + // Get details on image + $info = list($this->width, $this->height, $this->type, $this->attr) = getimagesize($this->pathToImage); + !empty($info) or $this->RaiseError("The file doesn't seem to be an image."); + $this->mime = $info['mime']; + + return $this; + } + + + /** + * Output image using caching. + * + */ + protected function Output($file) { + $time = filemtime($file); + if(isset($_SERVER['If-Modified-Since']) && strtotime($_SERVER['If-Modified-Since']) >= $time){ + header("HTTP/1.0 304 Not Modified"); + } else { + header('Content-type: ' . $this->mime); + header('Last-Modified: ' . gmdate("D, d M Y H:i:s",$time) . " GMT"); + readfile($file); + } + exit; + } + + + /** + * Open image. + * + */ + protected function Open() { + switch($this->fileExtension) { + case 'jpg': + case 'jpeg': $this->image = @imagecreatefromjpeg($this->pathToImage); break; + case 'gif': $this->image = @imagecreatefromgif($this->pathToImage); break; + case 'png': $this->image = @imagecreatefrompng($this->pathToImage); break; + default: $this->image = false; $this->RaiseError('No support for this file extension.'); + } + return $this; + } + + + /** + * Calculate new width and height of image. + */ + protected function CalculateNewWidthAndHeight() { + // Only calculate new width and height if keeping aspect-ratio. + if($this->keepRatio) { + + // Both new width and height are set. + if(isset($this->newWidth) && isset($this->newHeight)) { + + // Use newWidth and newHeigh as min width/height, image should fit the area. + if($this->crop) { + $ratioWidth = $this->width/$this->newWidth; + $ratioHeight = $this->height/$this->newHeight; + $ratio = ($ratioWidth < $ratioHeight) ? $ratioWidth : $ratioHeight; + $this->cropWidth = $this->width / $ratio; + $this->cropHeight = $this->height / $ratio; + } + + // Use newWidth and newHeigh as max width/height, image should not be larger. + else { + if($this->width > $this->height) { + $factor = (float)$this->newWidth / (float)$this->width; + $this->newHeight = $factor * $this->height; + } else { + $factor = (float)$this->newHeight / (float)$this->height; + $this->newWidth = $factor * $this->width; + } + } + } + + // Use new width as max-width + elseif(isset($this->newWidth)) { + $factor = (float)$this->newWidth / (float)$this->width; + $this->newHeight = $factor * $this->height; + } + + // Use new height as max-hight + elseif(isset($this->newHeight)) { + $factor = (float)$this->newHeight / (float)$this->height; + $this->newWidth = $factor * $this->width; + } + } + + // Do not keep aspect ratio, but both newWidth and newHeight must be set + else { + $this->newWidth = isset($this->newWidth) ? $this->newWidth : $this->width; + $this->newHeight = isset($this->newHeight) ? $this->newHeight : $this->height; + } + return $this; + } + + + /** + * Resize the image and optionally store/cache the new imagefile. Output the image. + */ + public function ResizeAndOutput() { + $this->Init()->CalculateNewWidthAndHeight(); + + // Use original image? + if(is_null($this->newWidth) && is_null($this->newHeight)) { + $this->Output($this->pathToImage); + } + + // Check cache before resizing. + $this->newFileName = $this->CreateFilename(); + if(is_readable($this->newFileName)) { + $fileTime = filemtime($this->pathToImage); + $cacheTime = filemtime($this->newFileName); + if($fileTime <= $cacheTime) { + $this->Output($this->newFileName); + } + } + + // Resize and output + $this->Open()->ResizeAndSave(); + } + + + /** + * Resize, crop and output the image. + * + * @param $imageQuality number the quality to use when saving the file, default is full quality. + */ + public function ResizeAndSave($imageQuality="100") { + + //echo "{$this->width}:{$this->height}:{$this->mime}
"; + //echo "{$this->cropWidth}:{$this->cropHeight}
"; + //echo "{$this->newWidth}:{$this->newHeight}
"; + + if($this->crop) { + $cropX = ($this->cropWidth/2) - ($this->newWidth/2); + $cropY = ($this->cropHeight/2) - ($this->newHeight/2); + $imgPreCrop = imagecreatetruecolor($this->cropWidth, $this->cropHeight); + $imageResized = imagecreatetruecolor($this->newWidth, $this->newHeight); + imagecopyresampled($imgPreCrop, $this->image, 0, 0, 0, 0, $this->cropWidth, $this->cropHeight, $this->width, $this->height); + imagecopyresampled($imageResized, $imgPreCrop, 0, 0, $cropX, $cropY, $this->newWidth, $this->newHeight, $this->newWidth, $this->newHeight); + } else { + $imageResized = imagecreatetruecolor($this->newWidth, $this->newHeight); + imagecopyresampled($imageResized, $this->image, 0, 0, 0, 0, $this->newWidth, $this->newHeight, $this->width, $this->height); + } + + switch($this->fileExtension) + { + case 'jpg': + case 'jpeg': + if(imagetypes() & IMG_JPG) { + if($this->saveFolder) { + imagejpeg($imageResized, $this->newFileName, $imageQuality); + } + $imgFunction = 'imagejpeg'; + } + break; + + case 'gif': + if (imagetypes() & IMG_GIF) { + if($this->saveFolder) { + imagegif($imageResized, $this->newFileName); + } + $imgFunction = 'imagegif'; + } + break; + + case 'png': + // Scale quality from 0-100 to 0-9 and invert setting as 0 is best, not 9 + $quality = 9 - round(($imageQuality/100) * 9); + if (imagetypes() & IMG_PNG) { + if($this->saveFolder) { + imagepng($imageResized, $this->newFileName, $quality); + } + $imgFunction = 'imagepng'; + } + break; + default: + $this->RaiseError('No support for this file extension.'); + break; + } + header('Content-type: ' . $this->mime); + header('Last-Modified: ' . gmdate("D, d M Y H:i:s",time()) . " GMT"); + $imgFunction($imageResized); + exit; + } + + +} diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..da4a118 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,2 @@ +This is a software solution to a general known problem. +Free software. Use at own risk. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..8e99c23 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +Image conversion on the fly using PHP +===================================== + +The `CImage.php` contains a class that can resize and crop images and output them to +a webpage. The class has cache of generated images. + +The file `img.php` uses `CImage.php` to resize images. It is a usecase on how to use +the class. + +The file `test.php` has some testcases that show the results of `img.php` with different +settings. + +Start by reviewing the `test.php`, then have a look at `img.php` and finally go through +`CImage.php`. + +Enjoy. + +Mikael Roos (mos@dbwebb.se) + + +Revision history +---------------- + +v0.1 (2012-04-25) + +* Initial release after rewriting some older code I had lying around. diff --git a/img.php b/img.php new file mode 100644 index 0000000..259b5b7 --- /dev/null +++ b/img.php @@ -0,0 +1,34 @@ + 10 or die('To small width.'); +} +if(isset($newHeight)) { + $newHeight < 1000 or die('To large height.'); + $newHeight > 10 or die('To small height.'); +} + +// Create the image object +require(__DIR__.'/CImage.php'); +$img = new CImage($srcImage, $newWidth, $newHeight, $keepRatio, $crop, null/*$pathToCache*/); +$img->ResizeAndOutput(); \ No newline at end of file diff --git a/img/higher.jpg b/img/higher.jpg new file mode 100644 index 0000000..81e3440 Binary files /dev/null and b/img/higher.jpg differ diff --git a/img/wider.jpg b/img/wider.jpg new file mode 100644 index 0000000..702d3c3 Binary files /dev/null and b/img/wider.jpg differ diff --git a/test.php b/test.php new file mode 100644 index 0000000..3568f0e --- /dev/null +++ b/test.php @@ -0,0 +1,39 @@ + + + + Testing img resizing using CImage.php + + +

Testing CImage.php through img.php

+

You can test any variation of resizing the images through img.php?src=wider.jpg&w=200&h=200 or img.php?src=higher.jpg&w=200&h=200

+

Supported arguments throught the querystring are:

+ + + + + + + + + + + + + + + + + + + + + +
Test cases
Testcase:Result:
Original image of wider.jpg.
wider.jpg max width 200.
wider.jpg max height 200.
wider.jpg max width 200 and max height 200.
wider.jpg max width 200 and max height 200 and no-ratio.
wider.jpg max width 200 and max height 200 and cropped.
wider.jpg max width 200 and max height 100 and cropped.
Original image of higher.jpg.
higher.jpg max width 200.
higher.jpg max height 200.
higher.jpg max width 200 and max height 200.
higher.jpg max width 200 and max height 200 and no-ratio.
higher.jpg max width 200 and max height 200 and cropped.
higher.jpg max width 200 and max height 100 and cropped.
+ + + \ No newline at end of file