[PHP5] Thumbnail Class

원래 십여년전(?)부터 뜯어고쳐 오며 사용하던 소스를 좀 정리했다.
이전에 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]);
	}

}

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.