diff --git a/CImage.php b/CImage.php index e203bcb..4ffade1 100644 --- a/CImage.php +++ b/CImage.php @@ -19,7 +19,12 @@ class CImage { private $cropWidth; private $cropHeight; public $keepRatio; + public $cropToFit; public $crop; + public $crop_x; + public $crop_y; + private $quality; + public $filters; public $saveFolder; public $newName; private $newFileName; @@ -35,20 +40,12 @@ class CImage { * Constructor, can take arguments to init the object. * * @param $pathToImage string the filepath to the image. - * @param $newWidth integer the new width or null. - * @param $newHeight integer the new width or null. - * @param $keepRatio boolean true to keep aspect ratio else false. * @param $saveFolder string path to folder where to save the new file or null to skip saving. * @param $newName string new filename or leave to null to autogenerate filename. */ - public function __construct($pathToImage=null, $newWidth=null, $newHeight=null, - $keepRatio=true, $crop=false, $saveFolder=null, $newName=null) { + public function __construct($pathToImage=null, $saveFolder=null, $newName=null) { $this->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; } @@ -69,8 +66,20 @@ class CImage { */ 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']; + $crop = $this->cropToFit ? '_cf' : null; + $crop_x = $this->crop_x ? "_x{$this->crop_x}" : null; + $crop_y = $this->crop_y ? "_y{$this->crop_y}" : null; + $quality = $this->quality == 100 ? null : "_q{$this->quality}"; + $filters = null; + foreach($this->filters as $filter) { + if(is_array($filter)) { + $filters .= "_f{$filter['id']}"; + for($i=1;$i<=$filter['argc'];$i++) { + $filters .= ":".$filter["arg{$i}"]; + } + } + } + return $this->saveFolder . '/' . $parts['filename'] . '_' . round($this->newWidth) . '_' . round($this->newHeight) . $crop . $crop_x . $crop_y . $quality . $filters . '.' . $parts['extension']; } @@ -80,6 +89,9 @@ class CImage { 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_numeric($this->quality) and $this->quality >= 0 and $this->quality <= 100 or $this->RaiseError('Quality not in range.'); + //is_numeric($this->crop_x) && is_numeric($this->crop_y) or $this->RaiseError('Quality not in range.'); + //filter 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.'); @@ -126,6 +138,34 @@ class CImage { } + /** + * Map filter name to PHP filter and id. + * + * @param string $name the name of the filter. + */ + private function MapFilter($name) { + $map = array( + 'negate' => array('id'=>0, 'argc'=>0, 'type'=>IMG_FILTER_NEGATE), + 'grayscale' => array('id'=>1, 'argc'=>0, 'type'=>IMG_FILTER_GRAYSCALE), + 'brightness' => array('id'=>2, 'argc'=>1, 'type'=>IMG_FILTER_BRIGHTNESS), + 'contrast' => array('id'=>3, 'argc'=>1, 'type'=>IMG_FILTER_CONTRAST), + 'colorize' => array('id'=>4, 'argc'=>4, 'type'=>IMG_FILTER_COLORIZE), + 'edgedetect' => array('id'=>5, 'argc'=>0, 'type'=>IMG_FILTER_EDGEDETECT), + 'emboss' => array('id'=>6, 'argc'=>0, 'type'=>IMG_FILTER_EMBOSS), + 'gaussian_blur' => array('id'=>7, 'argc'=>0, 'type'=>IMG_FILTER_GAUSSIAN_BLUR), + 'selective_blur' => array('id'=>8, 'argc'=>0, 'type'=>IMG_FILTER_SELECTIVE_BLUR), + 'mean_removal' => array('id'=>9, 'argc'=>0, 'type'=>IMG_FILTER_MEAN_REMOVAL), + 'smooth' => array('id'=>10, 'argc'=>1, 'type'=>IMG_FILTER_SMOOTH), + 'pixelate' => array('id'=>11, 'argc'=>2, 'type'=>IMG_FILTER_PIXELATE), + ); + if(isset($map[$name])) + return $map[$name]; + else { + $this->RaiseError('No such filter.'); + } + } + + /** * Calculate new width and height of image. */ @@ -137,7 +177,7 @@ class CImage { if(isset($this->newWidth) && isset($this->newHeight)) { // Use newWidth and newHeigh as min width/height, image should fit the area. - if($this->crop) { + if($this->cropToFit) { $ratioWidth = $this->width/$this->newWidth; $ratioHeight = $this->height/$this->newHeight; $ratio = ($ratioWidth < $ratioHeight) ? $ratioWidth : $ratioHeight; @@ -179,8 +219,55 @@ class CImage { /** * Resize the image and optionally store/cache the new imagefile. Output the image. + * + * @param integer $newWidth the new width or null. Default is null. + * @param integer $newHeight the new width or null. Default is null. + * @param boolean $keepRatio true to keep aspect ratio else false. Default is true. + * @param boolean $cropToFit true to crop image to fit in box specified by $newWidth and $newHeight. Default is false. + * @param integer $quality the quality to use when saving the file, range 0-100, default is full quality which is 100. + * @param array $crop. + * @param array $filter. */ - public function ResizeAndOutput() { + public function ResizeAndOutput($args) { + $defaults = array( + 'newWidth'=>null, + 'newHeight'=>null, + 'keepRatio'=>true, + 'cropToFit'=>false, + 'quality'=>100, + 'crop'=>array('w'=>null, 'h'=>null, 'x'=>0, 'y'=>0), + 'filters'=>null, + ); + // Convert crop settins from string to array + if(isset($args['crop']) && !is_array($args['crop'])) { + $args['crop'] = array(); + } + + // Convert filter settins from array of string to array of array + if(isset($args['filters']) && is_array($args['filters'])) { + foreach($args['filters'] as $key => $filterStr) { + $parts = explode(',', $filterStr); + $filter = $this->MapFilter($parts[0]); + $filter['str'] = $filterStr; + for($i=1;$i<=$filter['argc'];$i++) { + if(isset($parts[$i])) { + $filter["arg{$i}"] = $parts[$i]; + } else { + $this->RaiseError('Missing arg to filter, review how many arguments are needed at http://php.net/manual/en/function.imagefilter.php'); + } + } + $args['filters'][$key] = $filter; + } + } + //echo "
" . print_r($args['filters'], true) . ""; + + // Merge default arguments with incoming and set properties. + $args = array_merge($defaults, $args); + foreach($defaults as $key=>$val) { + $this->{$key} = $args[$key]; + } + + // Init the object and do sanity checks on arguments $this->Init()->CalculateNewWidthAndHeight(); // Use original image? @@ -188,8 +275,6 @@ class CImage { $this->Output($this->pathToImage); } - //echo "{$this->newWidth}:{$this->newHeight}"; - // Check cache before resizing. $this->newFileName = $this->CreateFilename(); if(is_readable($this->newFileName)) { @@ -208,10 +293,9 @@ class CImage { /** * 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") { - if($this->crop) { + public function ResizeAndSave() { + if($this->cropToFit) { $cropX = ($this->cropWidth/2) - ($this->newWidth/2); $cropY = ($this->cropHeight/2) - ($this->newHeight/2); $imgPreCrop = imagecreatetruecolor($this->cropWidth, $this->cropHeight); @@ -222,14 +306,26 @@ class CImage { $imageResized = imagecreatetruecolor($this->newWidth, $this->newHeight); imagecopyresampled($imageResized, $this->image, 0, 0, 0, 0, $this->newWidth, $this->newHeight, $this->width, $this->height); } - + + if(isset($this->filters) && is_array($this->filters)) { + foreach($this->filters as $filter) { + switch($filter['argc']) { + case 0: imagefilter($imageResized, $filter['type']); break; + case 1: imagefilter($imageResized, $filter['type'], $filter['arg1']); break; + case 2: imagefilter($imageResized, $filter['type'], $filter['arg1'], $filter['arg2']); break; + case 3: imagefilter($imageResized, $filter['type'], $filter['arg1'], $filter['arg2'], $filter['arg3']); break; + case 4: imagefilter($imageResized, $filter['type'], $filter['arg1'], $filter['arg2'], $filter['arg3'], $filter['arg4']); break; + } + } + } + switch($this->fileExtension) { case 'jpg': case 'jpeg': if(imagetypes() & IMG_JPG) { if($this->saveFolder) { - imagejpeg($imageResized, $this->newFileName, $imageQuality); + imagejpeg($imageResized, $this->newFileName, $this->quality); } $imgFunction = 'imagejpeg'; } @@ -249,7 +345,7 @@ class CImage { $quality = 9 - round(($imageQuality/100) * 9); if (imagetypes() & IMG_PNG) { if($this->saveFolder) { - imagepng($imageResized, $this->newFileName, $quality); + imagepng($imageResized, $this->newFileName, $this->quality); } $imgFunction = 'imagepng'; } diff --git a/README.md b/README.md index bc67aad..0bcbad3 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,20 @@ Mikael Roos (mos@dbwebb.se) Revision history ---------------- +ToDo. + +* crop +* Pre-defined sizes. + + +v0.2 (2012-05-09) + +* Implemented filters as in http://php.net/manual/en/function.imagefilter.php +* Changed `crop` to `crop_to_fit`, woks the same way. +* Changed arguments to method and sends them in array. +* Added quality-setting. +* Added testcases for above. + v0.1.1 (2012-04-27) * Corrected calculation where both width and height were set. diff --git a/img.php b/img.php index 966e47e..d10c388 100644 --- a/img.php +++ b/img.php @@ -14,21 +14,30 @@ $pathToCache = __DIR__.'/cache/'; $srcImage = isset($_GET['src']) ? $pathToImages . basename($_GET['src']) : null; $newWidth = isset($_GET['width']) ? $_GET['width'] : (isset($_GET['w']) ? $_GET['w'] : null); $newHeight = isset($_GET['height']) ? $_GET['height'] : (isset($_GET['h']) ? $_GET['h'] : null); -$keepRatio = isset($_GET['no-ratio']) ? false : true; // Keep Aspect Ratio? -$crop = isset($_GET['crop']) ? true : false; // Crop image? +$keepRatio = isset($_GET['no-ratio']) ? false : true; +$cropToFit = isset($_GET['crop-to-fit']) ? true : false; +$crop = isset($_GET['crop']) ? $_GET['crop'] : (isset($_GET['c']) ? $_GET['c'] : null); +$quality = isset($_GET['quality']) ? $_GET['quality'] : (isset($_GET['q']) ? $_GET['q'] : 100); + +// Add all filters to an array +$filters = array(); +$filter = isset($_GET['filter']) ? $_GET['filter'] : (isset($_GET['f']) ? $_GET['f'] : null); +if($filter) { $filters[] = $filter; } +for($i=0; $i<10;$i++) { + $filter = isset($_GET["filter{$i}"]) ? $_GET["filter{$i}"] : (isset($_GET["f{$i}"]) ? $_GET["f{$i}"] : null); + if($filter) { $filters[] = $filter; } +} // Do some sanity checks !preg_match('/^[\w-\.]+$/', $srcImage) or die('Filename contains invalid characters.'); -if(isset($newWidth)) { - $newWidth < 1000 or die('To large width.'); - $newWidth > 10 or die('To small width.'); -} -if(isset($newHeight)) { - $newHeight < 1000 or die('To large height.'); - $newHeight > 10 or die('To small height.'); -} +is_null($newWidth) or ($newWidth > 10 && $newWidth < 1000) or die('Width out of range.'); +is_null($newHeight) or ($newHeight > 10 && $newHeight < 1000) or die('Hight out of range.'); +$quality >= 0 and $quality <= 100 or die('Quality out of range'); // Create the image object require(__DIR__.'/CImage.php'); -$img = new CImage($srcImage, $newWidth, $newHeight, $keepRatio, $crop, $pathToCache); -$img->ResizeAndOutput(); \ No newline at end of file +$img = new CImage($srcImage, $pathToCache); +$img->ResizeAndOutput(array('newWidth'=>$newWidth, 'newHeight'=>$newHeight, 'keepRatio'=>$keepRatio, + 'cropToFit'=>$cropToFit, 'quality'=>$quality, + 'crop'=>$crop, 'filters'=>$filters, + )); \ No newline at end of file diff --git a/test.php b/test.php index 709af05..cf1caca 100644 --- a/test.php +++ b/test.php @@ -7,36 +7,81 @@
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:
+imagefilter()
+Supports .jpg
, .png
and .gif
.
Sourcecode and issues on github: https://github.com/mosbth/cimage
Mikael Roos (mos@dbwebb.se)
+wider.jpg
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. | ![]() |
$key{$val['text']}".htmlentities($url)." |
higher.jpg
Testcase: | Result: |
---|---|
$key{$val['text']}".htmlentities($url)." |