원래 십여년전(?)부터 뜯어고쳐 오며 사용하던 소스를 좀 정리했다.
이전에 GD를 직접 컨트롤 했었는데 조금 찾아보니 WideImage 란 라리브러리가 있기에, 그걸로 바꾸다보니 워낙 기능이 막강해서 정작 내가 만든 클래스는 Wrapping Class가 되어 버렸다 ;;
어쨋든, 밤하늘의 별처럼 널려있는 썸네일 라이브러리들을 마다하고 만든 이유는 단 두가지 이다.
- 썸네일의 종횡비율에 따라 중앙부분을 기준으로 Crop해서 썸네일을 만들어 주는 기능
- PNG포맷에서 Resizing시 원본의 Alpha Channel을 유지 해 주는 기능
그 이외에는 잡다한 기능을 다 빼 버렸다. 복잡해지면 오래 못쓰므로…
Licence : GNU LGPL 2.1
Copyright (C) 2013 Aiden, Kihyun Kim.
if (!class_exists('WideImage')) require_once('WideImage/WideImage.php'); /** * Thumbnail * * @category Thumbnail * @package Thumbnail * @copyright 2013 Aiden Kim * @license GNU LGPL 2.1. */ class Thumbnail { const FIT_INSIDE = 'inside'; const FIT_INSIDE_CROP = 'inside-crop'; const FIT_OUTSIDE = 'outside'; const FIT_FILL = 'fill'; const IMAGETYPE_PNG = IMAGETYPE_PNG; const IMAGETYPE_JPEG = IMAGETYPE_JPEG; const IMAGETYPE_GIF = IMAGETYPE_GIF; const IMAGETYPE_BMP = IMAGETYPE_BMP; const IMAGETYPE_ICO = IMAGETYPE_ICO; const IMAGETYPE_AUTO = -1; static public $ImgExts = array( self::IMAGETYPE_PNG => 'png' ,self::IMAGETYPE_JPEG => 'jpg' ,self::IMAGETYPE_GIF => 'gif' ,self::IMAGETYPE_BMP => 'bmp' ,self::IMAGETYPE_ICO => 'ico' ); private $orgImg = null; private $orgImgPath = null; private $prop = array( 'ouputType' => self::IMAGETYPE_AUTO ,'outputWidth' => 120 ,'outputHeight' => 120 ,'ouputFit' => self::FIT_INSIDE ,'useTransparent' => true ,'jpegQuality' => 95 // 1~100 (100 is best) ,'pngCompress' => 3 // 1~9 (1 is best) ,'fileChmod' => -1 ); public function __construct($imgPath=null) { if ($imgPath) { $this->load($imgPath); } } public function __destruct() { if ($this->orgImg && method_exists($this->orgImg,'destroy')) @$this->orgImg->destroy(); unset($this->orgImg); } public function __set($name, $value) { switch ($name) { case 'useTransparent': $this->prop[$name] = ($value ? true : false); break; case 'ouputFit': $this->prop[$name] = strval($value); break; case 'ouputType': if (array_key_exists(intval($value), self::$ImgExts)) $this->prop[$name] = intval($value); break; case 'outputWidth': case 'outputHeight': case 'useTransparent': case 'jpegQuality': case 'pngCompress': case 'fileChmod': $this->prop[$name] = intval($value); break; } } public function __get($name) { switch ($name) { case 'imgIsValid': if ($this->orgImg) { return $this->orgImg->isValid(); } return false; case 'imgWidth': if ($this->orgImg) { return $this->orgImg->getWidth(); } return false; case 'imgHeight': if ($this->orgImg) { return $this->orgImg->getHeight(); } return false; default: if (array_key_exists($name, $this->prop)) { return $this->prop[$name]; } } $trace = debug_backtrace(); trigger_error( 'Undefined property via __get(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_NOTICE); return null; } /** * Load original image file * * @param string $imgPath path or url of image file * @return boolean results */ public function load($imgPath) { $this->orgImgPath = $imgPath; $this->orgImg = WideImage::loadFromFile($imgPath); if ((is_null($this->orgImg)) || (!method_exists($this->orgImg,'isValid')) || (!$this->orgImg->isValid())) { $this->orgImg = null; $trace = debug_backtrace(); trigger_error( 'Unable to load image file via load(): ' . $imgPath . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_NOTICE); return false; } if (($this->orgImg->isTransparent() || $this->orgImg->isTrueColor()) && $this->useTransparent) { $this->orgImg->alphaBlending = false; } return true; } /** * Create thumbnail automatically * * @param string|null $imgPath path or url of image file * @return WideImage_Image thumbnail image handler */ public function createThumbnail($imgPath=null) { if (!is_null($imgPath)) $this->load($imgPath); if (!$this->orgImg) return false; if ($this->ouputFit == self::FIT_INSIDE_CROP) { $imgRatio = $this->imgWidth / $this->imgHeight; $thumbRatio = $this->outputWidth / $this->outputHeight; if ($imgRatio > $thumbRatio) { $cropWidth = $this->imgHeight * $this->outputWidth / $this->outputHeight; $cropHeight = $this->imgHeight; $cropLeft = round(($this->imgWidth - $cropWidth) / 2); $cropTop = 0; }else if ($imgRatio < $thumbRatio) { $cropWidth = $this->imgWidth; $cropHeight = $this->imgWidth * $this->outputHeight / $this->outputWidth; $cropLeft = 0; $cropTop = round(($this->imgHeight - $cropHeight) / 2); }else{ $cropWidth = $this->imgWidth; $cropHeight = $this->imgHeight; $cropLeft = 0; $cropTop = 0; } return $this->createCropThumbnail($cropLeft, $cropTop, $cropWidth, $cropHeight); }else{ $thumbImg = $this->orgImg->resizeDown($this->outputWidth, $this->outputHeight, $this->ouputFit); if ((!is_null($thumbImg)) && (method_exists($thumbImg,'isValid')) && ($thumbImg->isValid())) { if (($thumbImg->isTransparent() || $thumbImg->isTrueColor()) && $this->useTransparent) $thumbImg->alphaBlending = false; return $thumbImg; } } return false; } /** * Create thumbnail manually * * @param int|0 $cropLeft crop position x * @param int|0 $cropTop crop position y * @param int|0 $cropWidth crop width * @param int|0 $cropHeight crop height * @param string|null $imgPath path or url of image file * @return WideImage_Image thumbnail image handler */ public function createCropThumbnail($cropLeft=0, $cropTop=0, $cropWidth=0, $cropHeight=0, $imgPath=null) { if (($cropWidth <= 0) || ($cropHeight <= 0)) return $this->createThumbnail($imgPath); if (!is_null($imgPath)) $this->load($imgPath); if (!$this->orgImg) return false; // Crop $cropedImg = $this->orgImg->crop($cropLeft, $cropTop, $cropWidth, $cropHeight); if ((!is_null($cropedImg)) && (method_exists($cropedImg,'isValid')) && ($cropedImg->isValid())) { try { if (($cropedImg->isTransparent() || $cropedImg->isTrueColor()) && $this->useTransparent) $cropedImg->alphaBlending = false; // Resize $thumbImg = $cropedImg->resizeDown($this->outputWidth, $this->outputHeight, self::FIT_FILL); @$cropedImg->destroy(); unset($cropedImg); if ((!is_null($thumbImg)) && (method_exists($thumbImg,'isValid')) && ($thumbImg->isValid())) { if (($thumbImg->isTransparent() || $thumbImg->isTrueColor()) && $this->useTransparent) $thumbImg->alphaBlending = false; return $thumbImg; } } catch (Exception $e) { @$cropedImg->destroy(); unset($cropedImg); trigger_error( 'Caught the error: ' . $e->getMessage() . ' in ' . $e->getFile() . ' on line ' . $e->getLine() , E_USER_NOTICE); } } return false; } /** * Save thumbnail to file * * @param WideImage_Image $thumbImg thumbnail image handler * @param string $thumbPath save path of thumbnail file * @param int|0 $thumbType image type of thumbnail file * @return string saved file name */ public function saveThumbnail($thumbImg, $thumbPath, $thumbType=null) { if ($thumbImg && $thumbImg->isValid()) { try { if (!is_null($thumbType)) $this->ouputType = $thumbType; if ($this->ouputType == self::IMAGETYPE_AUTO) { $thumbType = self::GetImgTypeFromPath($thumbPath); if ($thumbType !== false) { if ($thumbType === self::IMAGETYPE_PNG) { $thumbImg->saveAlpha = true; $thumbImg->saveToFile($thumbPath, $this->pngCompress); }else if ($thumbType === self::IMAGETYPE_JPEG) { $thumbImg->saveToFile($thumbPath, $this->jpegQuality); }else{ $thumbImg->saveToFile($thumbPath); } } }else{ $thumbType = $this->ouputType; $thumbPath = self::ReplaceExtByImgType($thumbPath, $thumbType); if ($thumbPath !== false) { if ($thumbType === self::IMAGETYPE_PNG) { $thumbImg->saveAlpha = true; $thumbImg->saveToFile($thumbPath, $this->pngCompress); }else if ($thumbType === self::IMAGETYPE_JPEG) { $thumbImg->saveToFile($thumbPath, $this->jpegQuality); }else{ $thumbImg->saveToFile($thumbPath); } } } if ($this->fileChmod >= 0) @chmod($thumbPath, $this->fileChmod); @$thumbImg->destroy(); unset($thumbImg); return basename($thumbPath); } catch (Exception $e) { @$thumbImg->destroy(); unset($thumbImg); trigger_error( 'Caught the error: ' . $e->getMessage() . ' in ' . $e->getFile() . ' on line ' . $e->getLine() , E_USER_NOTICE); } } return false; } /** * Output image to browser * * @param WideImage_Image $thumbImg thumbnail image handler * @param int|0 $thumbType image type of thumbnail file * @return boolean results */ public function responseThumbnail($thumbImg, $thumbType=null) { if ($thumbImg && $thumbImg->isValid()) { try { if (!is_null($thumbType)) $this->ouputType = $thumbType; if ($this->ouputType == self::IMAGETYPE_JPEG) { $thumbImg->output(strtolower(self::$ImgExts[$this->ouputType]), $this->jpegQuality); }else if ($this->ouputType == self::IMAGETYPE_GIF) { $thumbImg->output(strtolower(self::$ImgExts[$this->ouputType])); }else if (($this->ouputType == self::IMAGETYPE_PNG) || ($this->ouputType == self::IMAGETYPE_AUTO)) { $thumbImg->output(strtolower(self::$ImgExts[self::IMAGETYPE_PNG]), $this->pngCompress); }else if (array_key_exists($this->ouputType, self::$ImgExts)) { $thumbImg->output(strtolower(self::$ImgExts[$this->ouputType])); } @$thumbImg->destroy(); unset($thumbImg); return true; } catch (Exception $e) { @$thumbImg->destroy(); unset($thumbImg); trigger_error( 'Caught the error: ' . $e->getMessage() . ' in ' . $e->getFile() . ' on line ' . $e->getLine() , E_USER_NOTICE); } } return false; } /** * Save thumbnail to file automatically * * @param string $thumbPath save path of thumbnail file * @param string|null $imgPath path or url of image file * @param int|null $thumbWidth thumbnail width * @param int|null $thumbHeight thumbnail height * @param string|null $thumbFit thumbnail fit type * @param int|null $thumbType image type of thumbnail file * @param boolean|null $transparent allow transparent * @param int|null $jpegQuality jpeg quality 1~100 (100 is best) * @param int|null $pngCompress png compress level 1~9 (1 is best) * @return string saved file name */ public function createThumbnailFile($thumbPath, $imgPath=null, $thumbWidth=null, $thumbHeight=null, $thumbFit=null, $thumbType=null, $transparent=null, $jpegQuality=null, $pngCompress=null ) { if (!is_null($thumbWidth)) $this->outputWidth = $thumbWidth; if (!is_null($thumbHeight)) $this->outputHeight = $thumbHeight; if (!is_null($thumbFit)) $this->ouputFit = $thumbFit; if (!is_null($thumbType)) $this->ouputType = $thumbType; if (!is_null($transparent)) $this->useTransparent = $transparent; if (!is_null($jpegQuality)) $this->jpegQuality = $jpegQuality; if (!is_null($pngCompress)) $this->pngCompress = $pngCompress; if (!is_null($imgPath)) $this->load($imgPath); $thumbImg = $this->createThumbnail(); if (!$thumbImg) return false; return $this->saveThumbnail($thumbImg, $thumbPath); } /** * Save thumbnail to file manually * * @param string $thumbPath save path of thumbnail file * @param int $cropLeft crop position x * @param int $cropTop crop position y * @param int $cropWidth crop width * @param int $cropHeight crop height * @param string|null $imgPath path or url of image file * @param int|null $thumbWidth thumbnail width * @param int|null $thumbHeight thumbnail height * @param string|null $thumbFit thumbnail fit type * @param int|null $thumbType image type of thumbnail file * @param boolean|null $transparent allow transparent * @param int|null $jpegQuality jpeg quality 1~100 (100 is best) * @param int|null $pngCompress png compress level 1~9 (1 is best) * @return string saved file name */ public function createCropThumbnailFile($thumbPath, $cropLeft, $cropTop, $cropWidth, $cropHeight, $imgPath=null, $thumbWidth=null, $thumbHeight=null, $thumbFit=null, $thumbType=null, $transparent=null, $jpegQuality=null, $pngCompress=null ) { if (!is_null($thumbWidth)) $this->outputWidth = $thumbWidth; if (!is_null($thumbHeight)) $this->outputHeight = $thumbHeight; if (!is_null($thumbFit)) $this->ouputFit = $thumbFit; if (!is_null($thumbType)) $this->ouputType = $thumbType; if (!is_null($transparent)) $this->useTransparent = $transparent; if (!is_null($jpegQuality)) $this->jpegQuality = $jpegQuality; if (!is_null($pngCompress)) $this->pngCompress = $pngCompress; if (!is_null($imgPath)) $this->load($imgPath); $thumbImg = $this->createCropThumbnail($cropLeft, $cropTop, $cropWidth, $cropHeight); if (!$thumbImg) return false; return $this->saveThumbnail($thumbImg, $thumbPath); } /** * Get image type from path * * @param string $path image path * @return int image type */ static public function GetImgTypeFromPath($path) { $path_parts = pathinfo($path); if (!is_array($path_parts)) return false; return array_search(strtolower($path_parts['extension']), self::$ImgExts); } /** * Change save path to image type * * @param string $path image path * @param int $imgType image type * @return string new path */ static public function ReplaceExtByImgType($path, $imgType) { if (!array_key_exists($imgType, self::$ImgExts)) return false; $path_parts = pathinfo($path); return $path_parts['dirname'].DIRECTORY_SEPARATOR.$path_parts['filename'].'.'.strtolower(self::$ImgExts[$imgType]); } }