Created
December 4, 2017 10:25
-
-
Save pentagonal/15d8f2e5f6148314acc0d295a00ebd12 to your computer and use it in GitHub Desktop.
Php SImple Image Resizer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Pentagonal; | |
/** | |
* Image Resizer | |
* | |
* @uses | |
* $im = new ImageResizer(); | |
* // array if success | |
* $result = $im->create('/path/to/file/source.png') | |
* ->resize(100, 200, 'landscape') | |
* ->saveTo('/path/of/target/target_save.png', 100, true); | |
* | |
* @purpose Resize and saves image | |
* @require PHP5, GD library. | |
* @suggest ext: Imagick | |
* | |
* @author awan <[email protected]> | |
* @version 1.0 | |
* @license MIT | |
*/ | |
final class ImageResizer | |
{ | |
/** | |
* List image type extension | |
* | |
* @var array | |
*/ | |
protected $image_type_list = array( | |
1 => array( | |
'IMAGETYPE_GIF', | |
'gif' | |
), | |
2 => array( | |
'IMAGETYPE_JPEG', | |
'jpg' | |
), | |
3 => array( | |
'IMAGETYPE_PNG', | |
'png' | |
), | |
4 => array( | |
'IMAGETYPE_SWF', | |
'swf' | |
), | |
5 => array( | |
'IMAGETYPE_PSD', | |
'psd' | |
), | |
6 => array( | |
'IMAGETYPE_BMP', | |
'bmp' | |
), | |
7 => array( | |
'IMAGETYPE_TIFF_II', | |
'tiff' | |
), | |
8 => array( | |
'IMAGETYPE_TIFF_MM', | |
'tiff' | |
), | |
9 => array( | |
'IMAGETYPE_JPC', | |
'jpc' | |
), | |
10 => array( | |
'IMAGETYPE_JP2', | |
'jp2' | |
), | |
11 => array( | |
'IMAGETYPE_JPX', | |
'jpx' | |
), | |
12 => array( | |
'IMAGETYPE_JB2', | |
'jb2', | |
), | |
13 => array( | |
'IMAGETYPE_SWC', | |
'swc' | |
), | |
14 => array( | |
'IMAGETYPE_IFF', | |
'iff' | |
), | |
15 => array( | |
'IMAGETYPE_WBMP', | |
'bmp' | |
), | |
16 => array( | |
'IMAGETYPE_XBM', | |
'xbm' | |
), | |
17 => array( | |
'IMAGETYPE_ICO', | |
'ico' | |
), | |
); | |
/** | |
* list functions uses | |
* | |
* @var array | |
*/ | |
protected $image_type_function = array( | |
'IMAGETYPE_GIF' => 'imagecreatefromgif', | |
'IMAGETYPE_JPEG' => 'imagecreatefromjpeg', | |
'IMAGETYPE_PNG' => 'imagecreatefrompng', | |
'IMAGETYPE_SWF' => 'class:imagick', | |
'IMAGETYPE_PSD' => 'class:imagick', | |
'IMAGETYPE_BMP' => 'imagecreatefromwbmp', | |
'IMAGETYPE_TIFF_II' => 'class:imagick', | |
'IMAGETYPE_TIFF_MM' => 'class:imagick', | |
'IMAGETYPE_JPC' => 'class:imagick', | |
'IMAGETYPE_JP2' => 'class:imagick', | |
'IMAGETYPE_JPX' => 'class:imagick', | |
'IMAGETYPE_JB2' => 'class:imagick', | |
'IMAGETYPE_SWC' => 'class:imagick', | |
'IMAGETYPE_IFF' => 'class:imagick', | |
'IMAGETYPE_WBMP' => 'imagecreatefromwbmp', | |
'IMAGETYPE_XBM' => 'imagecreatefromxbm', | |
'IMAGETYPE_ICO' => 'class:imagick' | |
); | |
/** | |
* Source file | |
* | |
* @var string | |
*/ | |
protected $source_file; | |
/** | |
* Determine image type | |
* | |
* @var string | |
*/ | |
protected $image_type; | |
/** | |
* Determine application has ready to use | |
* | |
* @var boolean | |
*/ | |
protected $ready = false; | |
/** | |
* Extension | |
* | |
* @var string | |
*/ | |
protected $extension; | |
/** | |
* Real Extension | |
* | |
* @var string | |
*/ | |
protected $real_extension; | |
/** | |
* Rsource Image created | |
* | |
* @var object|resource | |
*/ | |
protected $resource; | |
/** | |
* Original resource width | |
* | |
* @var integer | |
*/ | |
protected $width; | |
/** | |
* Original resource height | |
* | |
* @var integer | |
*/ | |
protected $height; | |
/** | |
* Resource image resized | |
* | |
* @var resource|object resized | |
*/ | |
protected $image_resized; | |
/** | |
* Imagick Php Extension status | |
* | |
* @access private | |
* @var bool | |
*/ | |
private static $imagick_exist; | |
/** | |
* Allowed output result | |
* | |
* @var array | |
*/ | |
protected $allowed_extensions_output = array('jpg', 'jpeg', 'wbmp', 'bmp', 'gif', 'xbm', 'png'); | |
/** | |
* Last image size set | |
* | |
* @var array | |
*/ | |
protected $last_set_image = array( | |
'height' => false, | |
'width' => false, | |
'mode' => false | |
); | |
/** | |
* ImageResize constructor. | |
*/ | |
public function __construct() | |
{ | |
if (!isset(self::$imagick_exist)) { | |
self::$imagick_exist = class_exists('Imagick'); | |
} | |
} | |
/** | |
* Check if use image magick | |
* | |
* @return bool | |
*/ | |
public function useImagick() | |
{ | |
return self::$imagick_exist; | |
} | |
/** | |
* Initial create | |
* | |
* @param string $fileName | |
* | |
* @return $this | |
*/ | |
public static function create($fileName) | |
{ | |
// create new current class object | |
$res = new self(); | |
// clear | |
$res->clear(); | |
$res->source_file = $fileName; | |
if (is_string($res->source_file) && is_file($res->source_file)) { | |
$res->extension = pathinfo($res->source_file, PATHINFO_EXTENSION); | |
$res->createResourceOfFile(); | |
return $res; | |
} | |
trigger_error( | |
'File does not exists', | |
E_USER_WARNING | |
); | |
$res->ready = false; | |
$res->image_type = null; | |
return $res; | |
} | |
/** | |
* Determine real type and set some property | |
* | |
* @access private | |
* | |
* @return void | |
*/ | |
private function createResourceOfFile() | |
{ | |
$type = @exif_imagetype($this->source_file); | |
if ($type === false || !isset($this->image_type_list[$type])) { | |
return; | |
} | |
$this->image_type = $this->image_type_list[$type][0]; | |
$this->real_extension = $this->image_type_list[$type][1]; | |
if ($this->useImagick() || $this->image_type_function[$this->image_type] == 'class:imagick') { | |
if (!class_exists('Imagick')) { | |
trigger_error( | |
'Php Imagick does not exist in server. Please check configuration', | |
E_USER_WARNING | |
); | |
return; | |
} | |
/*! | |
* Set Property | |
*/ | |
$this->resource = new \Imagick($this->source_file); | |
$this->width = $this->resource->getImageWidth(); | |
$this->height = $this->resource->getImageHeight(); | |
$this->ready = true; | |
return; | |
} | |
if (!function_exists($this->image_type_function[$this->image_type])) { | |
trigger_error( | |
sprintf( | |
'Function %s does not exist in server. Please check configuration', | |
$this->image_type_function[$this->image_type] | |
), | |
E_USER_WARNING | |
); | |
} | |
/*! | |
* Set Property | |
*/ | |
$this->resource = $this->image_type_function[$this->image_type]($this->source_file); | |
/** | |
* if image type PNG save Alpha Blending | |
*/ | |
if ($this->image_type == 'IMAGETYPE_PNG') { | |
imagealphablending($this->resource, true); // setting alpha blending on | |
imagesavealpha($this->resource, true); // save alphablending setting (important) | |
} | |
$this->width = imagesx($this->resource); | |
$this->height = imagesy($this->resource); | |
$this->ready = true; | |
} | |
/** | |
* Clearing default | |
*/ | |
public function clear() | |
{ | |
$this->ready = false; | |
$this->height = null; | |
$this->width = null; | |
$this->source_file = null; | |
$this->extension = null; | |
$this->real_extension = null; | |
// destroy | |
if (is_resource($this->image_resized)) { | |
imagedestroy($this->image_resized); | |
} | |
$this->image_resized = null; | |
if (is_resource($this->resource)) { | |
imagedestroy($this->resource); | |
} | |
$this->resource = null; | |
} | |
/** | |
* Check if is ready | |
* | |
* @return bool | |
*/ | |
public function isReady() | |
{ | |
return $this->ready; | |
} | |
/** | |
* Set As Cropped | |
* | |
* @param integer $newWidth | |
* @param integer $newHeight | |
* | |
* @return ImageResizer|null | |
*/ | |
public function crop($newWidth, $newHeight) | |
{ | |
return $this->resize($newWidth, $newHeight, 'crop'); | |
} | |
/** | |
* Set As auto | |
* | |
* @param integer $newWidth | |
* @param integer $newHeight | |
* | |
* @return ImageResizer|null | |
*/ | |
public function auto($newWidth, $newHeight) | |
{ | |
return $this->resize($newWidth, $newHeight, 'auto'); | |
} | |
/** | |
* Set As exactly | |
* | |
* @param integer $newWidth | |
* @param integer $newHeight | |
* | |
* @return ImageResizer|null | |
*/ | |
public function exact($newWidth, $newHeight) | |
{ | |
return $this->resize($newWidth, $newHeight, 'exact'); | |
} | |
/** | |
* Rotate | |
* | |
* @param integer $degree | |
* | |
* @return $this|bool | |
*/ | |
public function rotate($degree) | |
{ | |
if ($this->isReady()) { | |
$this->image_resized = imagerotate($this->resource, $degree, 0); | |
return $this; | |
} | |
return false; | |
} | |
/** | |
* Set As Potrait | |
* | |
* @param integer $newWidth | |
* @param integer $newHeight | |
* | |
* @return ImageResizer|null | |
*/ | |
public function potrait($newWidth, $newHeight) | |
{ | |
return $this->resize($newWidth, $newHeight, 'potrait'); | |
} | |
/** | |
* Set as Landscape | |
* | |
* @param integer $newWidth | |
* @param integer $newHeight | |
* | |
* @return ImageResizer|null | |
*/ | |
public function landscape($newWidth, $newHeight) | |
{ | |
return $this->resize($newWidth, $newHeight, 'landscape'); | |
} | |
/** | |
* Resize | |
* | |
* @param integer $newWidth | |
* @param integer $newHeight | |
* @param string $option crop|exact|potrait|landscape|auto default crop | |
* | |
* @return $this|null | |
*/ | |
public function resize($newWidth, $newHeight, $option = "crop") | |
{ | |
if (!$this->isReady()) { | |
return null; | |
} | |
$option = !is_string($option) ? 'crop' : trim(strtolower($option)); | |
// Get optimal width and height - based on $option | |
$optionArray = $this->getDimensions($newWidth, $newHeight, $option); | |
$optimalWidth = $optionArray['w']; | |
$optimalHeight = $optionArray['h']; | |
$this->last_set_image = array( | |
'height' => $newHeight, | |
'width' => $newWidth, | |
'mode' => $option | |
); | |
if ($this->useImagick()) { | |
$this->image_resized = clone $this->resource; | |
$this->image_resized->resizeImage($optimalWidth, $optimalHeight, \Imagick::FILTER_LANCZOS, 1); | |
$retval = $this->image_resized->cropImage( | |
$newWidth, | |
$newHeight, | |
(($optimalWidth - $newWidth) / 2), | |
(($optimalHeight - $newHeight) / 2) | |
); | |
$this->ready = $retval; | |
return $this; | |
} | |
$resource = is_resource($this->image_resized) ? $this->image_resized : $this->resource; | |
$this->image_resized = imagecreatetruecolor($optimalWidth, $optimalHeight); | |
// resampling | |
imagecopyresampled( | |
$this->image_resized, | |
$resource, | |
0, | |
0, | |
0, | |
0, | |
$optimalWidth, | |
$optimalHeight, | |
$this->width, | |
$this->height | |
); | |
// *** if option is 'cropProcess', then cropProcess too | |
if ($option == 'crop') { | |
$this->cropProcess($optimalWidth, $optimalHeight, $newWidth, $newHeight); | |
} | |
return $this; | |
} | |
/** | |
* Getting Dimension | |
* | |
* @param integer $newWidth | |
* @param integer $newHeight | |
* @param string $option | |
* | |
* @return array | |
*/ | |
private function getDimensions($newWidth, $newHeight, $option) | |
{ | |
switch ($option) { | |
case 'exact': | |
$optimalWidth = $newWidth; | |
$optimalHeight= $newHeight; | |
break; | |
case 'portrait': | |
$optimalWidth = $this->getSizeByFixedHeight($newHeight); | |
$optimalHeight= $newHeight; | |
break; | |
case 'landscape': | |
$optimalWidth = $newWidth; | |
$optimalHeight= $this->getSizeByFixedWidth($newWidth); | |
break; | |
case 'auto': | |
$optionArray = $this->getSizeByAuto($newWidth, $newHeight); | |
$optimalWidth = $optionArray['w']; | |
$optimalHeight = $optionArray['h']; | |
break; | |
default: | |
$optionArray = $this->getOptimalCrop($newWidth, $newHeight); | |
$optimalWidth = $optionArray['w']; | |
$optimalHeight = $optionArray['h']; | |
break; | |
} | |
return array('w' => $optimalWidth, 'h' => $optimalHeight); | |
} | |
/** | |
* Get fixed size by height | |
* | |
* @access private | |
* | |
* @param integer $newHeight | |
* | |
* @return mixed | |
*/ | |
private function getSizeByFixedHeight($newHeight) | |
{ | |
$ratio = $this->width / $this->height; | |
$newWidth = $newHeight * $ratio; | |
return $newWidth; | |
} | |
/** | |
* Get fixed size by width | |
* | |
* @access private | |
* | |
* @param integer $newWidth | |
* | |
* @return mixed | |
*/ | |
private function getSizeByFixedWidth($newWidth) | |
{ | |
$ratio = $this->height / $this->width; | |
$newHeight = $newWidth * $ratio; | |
return $newHeight; | |
} | |
/** | |
* Get Auto size | |
* | |
* @access private | |
* | |
* @param integer $newWidth | |
* @param integer $newHeight | |
* | |
* @return array | |
*/ | |
private function getSizeByAuto($newWidth, $newHeight) | |
{ | |
// *** Image to be resized is wider (landscape) | |
if ($this->height < $this->width) { | |
$optimalWidth = $newWidth; | |
$optimalHeight= $this->getSizeByFixedWidth($newWidth); | |
} elseif ($this->height > $this->width) { | |
// Image to be resized is taller (portrait) | |
$optimalWidth = $this->getSizeByFixedHeight($newHeight); | |
$optimalHeight= $newHeight; | |
} else { | |
// Image to be resized is a square | |
if ($newHeight < $newWidth) { | |
$optimalWidth = $newWidth; | |
$optimalHeight= $this->getSizeByFixedWidth($newWidth); | |
} else if ($newHeight > $newWidth) { | |
$optimalWidth = $this->getSizeByFixedHeight($newHeight); | |
$optimalHeight= $newHeight; | |
} else { | |
// Square being resized to a square | |
$optimalWidth = $newWidth; | |
$optimalHeight= $newHeight; | |
} | |
} | |
return array('w' => $optimalWidth, 'h' => $optimalHeight); | |
} | |
/** | |
* Get Optimal Crop | |
* | |
* @access private | |
* | |
* @param integer $newWidth | |
* @param integer $newHeight | |
* | |
* @return array | |
*/ | |
private function getOptimalCrop($newWidth, $newHeight) | |
{ | |
$heightRatio = $this->height / $newHeight; | |
$widthRatio = $this->width / $newWidth; | |
if ($heightRatio < $widthRatio) { | |
$optimalRatio = $heightRatio; | |
} else { | |
$optimalRatio = $widthRatio; | |
} | |
$optimalHeight = $this->height / $optimalRatio; | |
$optimalWidth = $this->width / $optimalRatio; | |
return array('w' => $optimalWidth, 'h' => $optimalHeight); | |
} | |
/** | |
* Proccess crop | |
* | |
* @access private | |
* | |
* @param integer $optimalWidth | |
* @param integer $optimalHeight | |
* @param integer $newWidth | |
* @param integer $newHeight | |
* | |
* @return bool | |
*/ | |
private function cropProcess($optimalWidth, $optimalHeight, $newWidth, $newHeight) | |
{ | |
if (!$this->isReady()) { | |
return false; | |
} | |
// *** Find center - this will be used for the cropProcess | |
$cropStartX = ($optimalWidth / 2) - ($newWidth / 2); | |
$cropStartY = ($optimalHeight/ 2) - ($newHeight / 2); | |
$crop = $this->image_resized; | |
// Now cropProcess from center to exact requested size | |
$this->image_resized = imagecreatetruecolor($newWidth, $newHeight); | |
// resampling | |
imagecopyresampled( | |
$this->image_resized, | |
$crop, | |
0, | |
0, | |
$cropStartX, | |
$cropStartY, | |
$newWidth, | |
$newHeight, | |
$newWidth, | |
$newHeight | |
); | |
return true; | |
} | |
/** | |
* Save The image result reSized | |
* | |
* @param string $savePath Full path of file name eg [/path/of/dir/image/image.jpg] | |
* @param integer $imageQuality image quality [1 - 100] | |
* @param bool $overwrite force rewrite existing image if there was savepath exists | |
* | |
* @return bool|array aboolean false if on fail otherwise array | |
*/ | |
public function saveTo($savePath, $imageQuality = 100, $overwrite = false) | |
{ | |
if (!$this->isReady()) { | |
return false; | |
} | |
// check if has on cropProcess | |
if (!isset($this->image_resized)) { | |
if ($this->last_set_image['width'] === false || ! $this->image_resized) { | |
$this->image_resized = clone $this->resource; | |
} else { | |
// set from last result | |
$this->resize( | |
$this->last_set_image['width'], | |
$this->last_set_image['height'], | |
$this->last_set_image['mode'] | |
); | |
} | |
} | |
// Get extension | |
$extension = pathinfo($savePath, PATHINFO_EXTENSION); | |
// file exist | |
if (file_exists($savePath)) { | |
if (!$overwrite) { | |
return false; | |
} | |
if (!is_writable($savePath)) { | |
trigger_error( | |
'File exist! And could not to be replace', | |
E_USER_WARNING | |
); | |
return false; | |
} | |
} | |
// check if image output type allowed | |
if (!in_array($extension, $this->allowed_extensions_output)) { | |
trigger_error( | |
'Invalid file type of target', | |
E_USER_WARNING | |
); | |
return false; | |
} | |
$dir_name = dirname($savePath); | |
if (!$dir_name || ! ($dir_name = realpath($dir_name))) { | |
trigger_error( | |
'Directory Target Does not exist. Resource image resize cleared.', | |
E_USER_WARNING | |
); | |
return false; | |
} | |
if (!is_writable($dir_name)) { | |
trigger_error( | |
'Directory Target is not writable. Please check directory permission.', | |
E_USER_WARNING | |
); | |
return false; | |
} | |
if ($this->useImagick()) { | |
$this->image_resized->setImageFormat($extension); | |
if (!$fp = @fopen($savePath, 'wb')) { | |
$this->image_resized->clear(); | |
$this->image_resized = null; | |
trigger_error( | |
'Could not write into your target directory. Resource image resize cleared.', | |
E_USER_WARNING | |
); | |
return false; | |
} | |
$ret_val = $this->image_resized->writeImageFile($fp); | |
@fclose($fp); | |
$width = $this->image_resized->getImageWidth(); | |
$height = $this->image_resized->getImageHeight(); | |
$path = is_file($savePath) ? realpath($savePath) : $savePath; | |
$this->image_resized->clear(); | |
$this->image_resized = null; | |
return ! $ret_val ? false : array( | |
'width' => $width, | |
'height' => $height, | |
'path' => $path, | |
); | |
} | |
$ret_val = false; | |
switch ($extension) { | |
case 'jpg': | |
case 'jpeg': | |
$ret_val = imagejpeg($this->image_resized, $savePath, $imageQuality); | |
break; | |
case 'wbmp': | |
case 'bmp': | |
$ret_val = imagewbmp($this->image_resized, $savePath); | |
break; | |
case 'gif': | |
$ret_val = imagegif($this->image_resized, $savePath); | |
break; | |
case 'xbm': | |
$ret_val = imagexbm($this->image_resized, $savePath); | |
break; | |
case 'png': | |
$scaleQuality = round(($imageQuality/100) * 9); | |
$invertScaleQuality = 9 - $scaleQuality; | |
$ret_val = imagepng($this->image_resized, $savePath, $invertScaleQuality); | |
break; | |
} | |
$width = imagesx($this->image_resized); | |
$height = imagesy($this->image_resized); | |
$path = is_file($savePath) ? realpath($savePath) : $savePath; | |
// destroy resource to make memory freely | |
@imagedestroy($this->image_resized); | |
$this->image_resized = null; | |
return ! $ret_val ? false : array( | |
'width' => $width, | |
'height' => $height, | |
'path' => $path, | |
); | |
} | |
/** | |
* Optimize Image Only & save into Target | |
* | |
* @param null|string $savePath | |
* @param bool $overwrite overwrite even exists | |
* | |
* @return bool|array | |
*/ | |
public function optimizeTo($savePath = null, $overwrite = false) | |
{ | |
if (!$this->isReady()) { | |
return false; | |
} | |
if (! $savePath) { | |
$savePath = $this->source_file; | |
} elseif (file_exists($savePath)) { | |
if (!$overwrite) { | |
return false; | |
} | |
} | |
if (!is_writable($savePath)) { | |
trigger_error( | |
'Target file exist! could not to be replace', | |
E_USER_WARNING | |
); | |
return false; | |
} | |
// Get extension | |
$extension = pathinfo($savePath, PATHINFO_EXTENSION); | |
// check if image output type allowed | |
if (!in_array($extension, $this->allowed_extensions_output)) { | |
trigger_error( | |
'Invalid file type of target', | |
E_USER_WARNING | |
); | |
return false; | |
} | |
if ($this->useImagick()) { | |
/** | |
* @var \Imagick | |
*/ | |
$image_source = clone $this->resource; | |
$image_source->setImageFormat($extension); | |
if (!$fp = @fopen($savePath, 'wb')) { | |
$image_source->clear(); | |
$image_source = null; | |
trigger_error( | |
'Could not write into your target directory.', | |
E_USER_WARNING | |
); | |
return false; | |
} | |
$ret_val = $image_source->writeImageFile($fp); | |
$width = $image_source->getImageWidth(); | |
$height = $image_source->getImageHeight(); | |
@fclose($fp); | |
$image_source->clear(); | |
$image_source = null; | |
$path = is_file($savePath) ? realpath($savePath) : $savePath; | |
if ($ret_val) { | |
return array( | |
'width' => $width, | |
'height' => $height, | |
'path' => $path, | |
); | |
} | |
return $ret_val; | |
} | |
$image_source = imagecreatetruecolor($this->width, $this->width); | |
imagecopy($image_source, $this->resource, 0, 0, 0, 0, $this->width, $this->width); | |
$ret_val = false; | |
switch ($extension) { | |
case 'jpg': | |
case 'jpeg': | |
$ret_val = imagejpeg($image_source, $savePath, 100); | |
break; | |
case 'wbmp': | |
case 'bmp': | |
$ret_val = imagewbmp($image_source, $savePath); | |
break; | |
case 'gif': | |
$ret_val = imagegif($image_source, $savePath); | |
break; | |
case 'xbm': | |
$ret_val = imagexbm($image_source, $savePath); | |
break; | |
case 'png': | |
$ret_val = imagepng($image_source, $savePath, 100); | |
break; | |
} | |
// destroy resource to make memory freely | |
@imagedestroy($image_source); | |
$path = is_file($savePath) ? realpath($savePath) : $savePath; | |
return ! $ret_val ? false : array( | |
'width' => $this->width, | |
'height' => $this->height, | |
'path' => $path, | |
); | |
} | |
/** | |
* Magic method destruct | |
*/ | |
public function __destruct() | |
{ | |
$this->clear(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment