<?php
/*
	DevSaver Web Framework
	Copyright (c) 2013-2016 DevSaver. 
	All rights reserved.
		web:  www.devsaver.com
		mail: support@devsaver.com				
*/

//use Image\ImageException;

//namespace core\Image;

//use core\Image\ImageConst;

/**
* description
*
* @library	
* @author	
* @since	
*/
class Image {

		/**
		* description
		*
		* @var type
		*
		* @access type
		*/
		var $debug = 0;
		/**
		* description
		*
		* @var type
		*
		* @access type
		*/
		var $processed = null;
		/**
		* description
		*
		* @var type
		*
		* @access type
		*/
		var $data = null;
		
		/**
		* description
		*
		* @var type
		*
		* @access type
		*/
		var $imagick = null;
		
		

	/*
		
	*/

		public static function newInstance(){
			return new self();
		}

		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function Read($path) {

			if (extension_loaded('imagick')) {
				$this->imagick = new Imagick($path);
			}

			$tmp = $this->__get($path);
			$this->data = $tmp["image"];
			$this->data_type = $tmp["type"];		

			return $this;
		}

		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function ReadString($data) {
				//imagecreatefromstring() 
		}
		

		function __get($path)  {

			//debug($path);
			
			$info = GetImageSize($path);

			switch ($info["2"]) {
				case IMAGETYPE_GIF:

					if ($this->isAnimated($path)) {
						$this->__gif = array(
							"is_animated" => true,
							"file"			=> $path,
						);

						$this->gifReadAnimated($path);

					} else {
						$img = imagecreatefromgif($path);
					}
				break;

				case IMAGETYPE_PNG:
					$img = imagecreatefrompng($path);
				break;

				case IMAGETYPE_JPEG:
					$img = imagecreatefromjpeg($path);
				break;
			}

			return array(
				"image"	=> $img , 
				"type"	=> $info[2]
			);
			
		}


		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function destroy() {
			imagedestroy($this->data);

			if ($this->processed) {
				imagedestroy($this->processed);
			}			


			if (is_array($this->__gif) && is_array($this->__gif["files"]) && $this->__gif["folder"]) {
				foreach ($this->__gif["files"] as $key => $val) {
//					CFile::Remove($val);
				}				
				//CDir::Delete($this->__gif["folder"]);
			}
			
		}
				
		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function Save($path , $quality = 100 , $chmod = 0644) {

			if (!$quality) {
				$quality = 90;
			}
			//switch ($this->data_type) {
			switch (strtolower(CFile::Extension($path))) {
				
				//case IMAGETYPE_GIF:
				case "gif":
					return $this->SaveGif($path , $chmod);
				break;

				//case IMAGETYPE_PNG:
				case "png":
					return $this->SavePNG($path, $quality , $chmod);
				break;

				//case IMAGETYPE_JPEG:
				case "jpeg":
				case "jpg":
					return $this->SaveJpeg($path , $quality , $chmod);
				break;
			}

			return $this;

		}
		
		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function SaveGif($path , $chmod = 0644) {

			if ($this->imagick) {	
				$this->imagick->SetFormat("GIF");
				CFile::Save(
					$path , 
					$this->imagick->getImageBlob()
				);

				return $this;
			}


			if (is_array($this->__gif) && $this->__gif["is_animated"]) {
				return $this->gifSaveAnimated($path);
			}			

			imagegif(
				$this->processed ? $this->processed :$this->data , 
				$path 
			);

			chmod ( $path , $chmod);
			return $this;
		}
		
		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function SavePNG($path , $quality = 90  , $chmod = 0644) {

			if (!$quality) {
				$quality = 90;
			}

			imagepng(
				$this->processed ? $this->processed :$this->data , 
				$path
			);

			chmod ( $path , $chmod);
			return $this;
		}
		
		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function SaveJpeg($path , $quality = 90 , $chmod = 0644) {
			imagejpeg(
				$this->processed ? $this->processed :$this->data , 
				$path ,
				$quality
			);

			chmod ( $path , $chmod);

			if ($this->debug) {
//				debug($path);
//				debug($info,1);
			}

			return $this;
		}
		

		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function Restore(){
			$this->processed = $this->data;
			return $this;
		}

		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function DetectAction($width = "" , $height = "") {

			if ($width && $height) {
				return $this->ScaleCrop($width , $height);
			} elseif ($width && !$height ) {
				return $this->ResizeByWidth($width);
			} elseif (!$width && $height ) {
				return $this->ResizeByHeight($height);
			}
			
		}
		

		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function ResizeByWidth($width) {

			if (is_array($this->__gif) && $this->__gif["is_animated"]) {
				return $this->gifResizeByWidth($width);			
			}

			$this->__freeProcessed();

			$ratio = imagesx($this->data ) / imagesy($this->data);

			
			$height = $width / $ratio;

			$this->__newImage($width , $height); 

			imageCopyResampled(
				$this->processed , 
				$this->data , 
				0 , 
				0 , 
				0 ,
				0 ,
				$width , 
				$height , 

				imagesx($this->data),
				imagesy($this->data)
			);

			return $this;

		}

		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function ResizeByHeight($height) {


			if (is_array($this->__gif) && $this->__gif["is_animated"]) {
				return $this->gifResizeByHeight($height);			
			}


			$this->__freeProcessed();

			$ratio = imagesx($this->data ) / imagesy($this->data);
			
			$width = $height * $ratio;

			$this->__newImage($width , $height); 

			imageCopyResampled(
				$this->processed , 
				$this->data , 
				0 , 
				0 , 
				0 ,
				0 ,
				$width , 
				$height , 

				imagesx($this->data),
				imagesy($this->data)
			);

			return $this;

		}

		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function ResizeScale($width = null , $height = null) {
			if ($width) {
				return $this->ResizeByWidth($width);
			} elseif ($height) {
				return $this->ResizeByHeight($height);
			} else {
				throw new Exception("Image:ResizeScale($width , $height); must have one parameter set");
			}			
		}
		

		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function ResizeFit($width , $height) {

			if (is_array($this->__gif) && $this->__gif["is_animated"]) {
				return $this->gifResizeFit($width , $height);			
			}


			$sourceWidth = imagesx($this->data);
			$sourceHeight = imagesy($this->data);

			$pRatio = $width / $height;
			$sRatio = $sourceWidth / $sourceHeight;


			if ($pRatio > $sRatio) {
				return $this->ResizeByHeight($height);
			} elseif ($pRatio < $sRatio) {
				return $this->ResizeByWidth($width);
			} else {
				//same ratios
				return $this->Resize($width , $height);
			}
			
			
		}
		
		

		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function Resize($width , $height) {
						

			if (is_array($this->__gif) && $this->__gif["is_animated"]) {
				return $this->gifResize($width , $height);			
			}

			if ($this->imagick) {	
				$this->imagick->resizeImage($width , $height);
				return $this;
			}
		

			$this->__freeProcessed();


			$this->__newImage($width , $height); 

			imageCopyResampled(
				$this->processed , 
				$this->data , 
				0 , 
				0 , 
				0 ,
				0 ,
				$width , 
				$height , 

				imagesx($this->data),
				imagesy($this->data)
			);

			return $this;
				
		}
		
		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function ScaleCrop($width , $height , $posX = "left", $posY = "top") {

			if (is_array($this->__gif) && $this->__gif["is_animated"]) {
				return $this->gifScaleCrop($width , $height , $posX , $posY );
			}
			

			if (!$posX)
				$posX = "left";

			if (!$posY) 
				$posY = "top";

			if ($this->debug) {
				debug("W:" . $width . "H" . $height);
			}
			
			

			$this->__freeProcessed();
			$this->__newImage($width , $height); 

			$sourceWidth = imagesx($this->data);
			$sourceHeight = imagesy($this->data);


			$pRatio = $width / $height;
			$sRatio = $sourceWidth / $sourceHeight;

			if ($pRatio == $sRatio) {
				$sx = 0; 
				$sy = 0; 
				$sw = $sourceWidth;
				$sh = $sourceHeight;
			} elseif ($pRatio > $sRatio) {			//checked and works fine
				$sx = 0;
				$sy = 0;
				$sw = $sourceWidth;
				$sh = $sourceWidth / $pRatio;
			} else {

				$sx = 0;
				$sy = 0;
				$sh = $sourceHeight;
				$sw = $sourceHeight * $pRatio;
			}

			//position 
			switch ($posX) {
				case "left":
					//do nothing default
				break;

				case "right":
					if ($sw < $sourceWidth) {
						$sx = ($sourceWidth - $sw);
					}

				break;

				case "center":
					if ($sw < $sourceWidth) {
						$sx = ($sourceWidth - $sw) / 2;
					}					
				break;
			}

			switch ($posY) {
				case "top":
					//default
				break;

				case "bottom":
					if ($sh < $sourceHeight) {
						$sy = ($sourceHeight - $sh);
					}
				break;

				case "center":
					if ($sh < $sourceHeight) {
						$sy = ($sourceHeight - $sh) /2;
					}					
				break;
			}


			imageCopyResampled(
				$this->processed , 
				$this->data , 
				0 , 
				0 , 
				$sx ,
				$sy ,
				$width , 
				$height , 

				$sw,
				$sh
			);

			//var_dump($sw, $sx, $cropLeft, $cropTop);

			return $this;
		}


		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function Crop($width , $height , $x , $y , $c_width , $c_height) {

			$this->__freeProcessed();
			$this->__newImage($width , $height); 

		
			ImageCopyResampled(
				$this->processed,
				$this->data,
				0,
				0,
				$x,
				$y,
				$width,
				$height,
				$c_width,
				$c_height
			);

			return $this;
		}
		
		
		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function Watermark($file , $posX = "left" , $posY = "bottom") {

			if (!$posX)
				$posX = "left";

			if (!$posY) 
				$posY = "bottom";

			if ($file) {
				$watermark = $this->__get($file);
	
				$wW = imagesx($watermark["image"]);
				$wY = imagesy($watermark["image"]);
				$sX = imagesx($this->processed);
				$sY = imagesy($this->processed);

				switch ($posX) {
					case "left":
						$sdX = 0;
					break;

					case "right":
						$sdX = $sX - $wW;
					break;

					case "center":
						$sdX = ($sX / 2) - ($wW/2);
					break;
				}

				switch ($posY) {
					case "top":
						$sdY = 0;
					break;

					case "bottom":
						$sdY = $sY - $wY;
					break;

					case "center":
						$sdY = ($sY / 2) - ($wY/2);
					break;

				}
				
				imagecopyresampled ( 
					
					$this->processed,
					$watermark["image"] , 
									
					$sdX , 
					$sdY , 
					0 , 
					0 , 
					$wW,
					$wY,
					$wW,
					$wY
				);


				imagedestroy($watermark["image"]);
			}
			
			return $this;


		}
		


		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function __freeProcessed() {

			if (is_resource($this->processed)) {
				imagedestroy($this->processed);
			}
		}

		
		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function __newImage($width , $height) {


			$this->processed = imagecreatetruecolor($width , $height);

	        $white = imagecolorallocatealpha($this->processed, 255, 255, 255, 127);

			imagefill($this->processed, 0, 0, $white);
			//imagealphablending($this->processed , false);
			imagesavealpha( $this->processed, true );				
			imagealphablending($this->processed , true);

			//saving transparency to the new image
			if ( ($this->data_type  == IMAGETYPE_GIF) || ($this->data_type ==  IMAGETYPE_PNG) ) {

				$trnprt_indx = imagecolortransparent($this->data);


				if ($trnprt_indx >= 0) {

					$trnprt_color    =  imagecolorsforindex($this->data,  $trnprt_indx);
					$trnprt_indx    =   imagecolorallocate($this->processed, $trnprt_color['red'], $trnprt_color['green'], $trnprt_color['blue']);
					$trnprt_indx    =   imagecolorallocate($this->processed, 0, 0, 0);
					imagefill($this->processed, 0, 0,  $trnprt_indx);
					imagecolortransparent($this->processed, $trnprt_indx);
				} 


//				imagecolortransparent($this->processed, imagecolorallocatealpha($this->processed, 0, 0, 0, 127));
//				imagealphablending($this->processed, false);
//				imagesavealpha($this->processed, true);
//				imagefill($this->processed, 0, 0, imagecolorallocatealpha($this->processed, 0, 0, 0, 127));

			}

		}


		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function enableDebug() {
			
			$this->debug = 1;
	
			return $this;
		}


		/**
		* description	source: http://www.php.net/manual/en/function.imagecreatefromgif.php#88005
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function isAnimated($filename) {
			if(!($fh = @fopen($filename, 'rb')))
				return false;
			$count = 0;
			//an animated gif contains multiple "frames", with each frame having a
			//header made up of:
			// * a static 4-byte sequence (\x00\x21\xF9\x04)
			// * 4 variable bytes
			// * a static 2-byte sequence (\x00\x2C)

			// We read through the file til we reach the end of the file, or we've found
			// at least 2 frame headers
			while(!feof($fh) && $count < 2) {
				$chunk = fread($fh, 1024 * 100); //read 100kb at a time
				$count += preg_match_all('#\x00\x21\xF9\x04.{4}\x00[\x2C\x21]#s', $chunk, $matches);
			}

			fclose($fh);
			return $count > 1;
		}


		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function gifReadAnimated($path) {
			global $_CONF;

			do {				
				$tmp = $_CONF["upload"] . "tmp/" . uniqid() . "/";

				if (!is_dir($tmp)) {
					mkdir($tmp);
					$found = true;
				}
				
			} while (!$found);


			$this->__gif["folder"] = $tmp;

			$gif = new GIFDecoder ( CFile::GetContents($path));


			$this->__gif["frames"] = 0;

			
			foreach ($gif->GIFGetFrames() as $frame) {
				$this->__gif["frames"] ++;

				$file = $this->__gif["folder"] . $this->__gif["frames"] . ".gif";

				CFile::SaveContents(
					$file,
					$frame
				);

				$this->__gif["files"][] = $file;
			}


			$this->__gif["loop"] = $gif->GIFGetLoop();
			$this->__gif["delay"] = $gif->GIFGetDelays();
			

		}
		


		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function gifScaleCrop($width , $height , $posX , $posY) {

			if ($this->__gif["files"]) {
				foreach ($this->__gif["files"] as $key => $val) {
					$file = str_replace(".gif" , "_resize.gif" , $val);

					$this->__gif["resized"][] = $file;
					
					$img = Image::newInstance()
						->Read($val)
						->ScaleCrop($width, $height , $posX , $posY)
						->Save($file)
						->Destroy();
				}				
			}
			
			return $this;
			
		}


		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function gifResize($width , $height) {

			if ($this->__gif["files"]) {
				foreach ($this->__gif["files"] as $key => $val) {
					$file = str_replace(".gif" , "_resize.gif" , $val);

					$this->__gif["resized"][] = $file;
					
					$img = Image::newInstance()
						->Read($val)
						->Resize($width, $height)
						->Save($file)
						->Destroy();
				}				
			}
			
			return $this;
			
		}

		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function gifResizeFit($width , $height) {

			if ($this->__gif["files"]) {
				foreach ($this->__gif["files"] as $key => $val) {
					$file = str_replace(".gif" , "_resize.gif" , $val);

					$this->__gif["resized"][] = $file;
					
					$img = Image::newInstance()
						->Read($val)
						->ResizeFit($width, $height)
						->Save($file)
						->Destroy();
				}				
			}
			
			return $this;
			
		}
		

		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function gifResizeByWidth($width) {

			if ($this->__gif["files"]) {
				foreach ($this->__gif["files"] as $key => $val) {
					$file = str_replace(".gif" , "_resize.gif" , $val);

					$this->__gif["resized"][] = $file;
					
					$img = Image::newInstance()
						->Read($val)
						->ResizebyWidth($width)
						->Save($file)
						->Destroy();
				}				
			}
			
			return $this;
			
		}

		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function gifResizeByHeight($width) {

			if ($this->__gif["files"]) {
				foreach ($this->__gif["files"] as $key => $val) {
					$file = str_replace(".gif" , "_resize.gif" , $val);

					$this->__gif["resized"][] = $file;
					
					$img = Image::newInstance()
						->Read($val)
						->ResizeByHeight($height)
						->Save($file)
						->Destroy();
				}				
			}
			
			return $this;
			
		}


		/**
		* description
		*
		* @param
		*
		* @return
		*
		* @access
		*/
		function gifSaveAnimated($path) {

			$gifThumb = new GIFEncoder (
					$this->__gif["resized"],
					$this->__gif["delay"],
					$this->__gif["loop"],
					0,
					0, 0, 0,
					"url"
			);
			 
			// save the new gif file
			$fpThumb = fopen($path, 'w');
			fwrite($fpThumb, $gifThumb->GetAnimation());
			fclose($fpThumb);

			return $this;
		}
		
		
		
}


final class ImageConst {

	const FLIP_HORIZONTAL	= 1;
	const FLIP_VERTICAL		= 2;
	const FLIP_BOTH			= 3;

	const MAX_WIDTH			= 2500;
	const MAX_HEIGHT		= 2500;

}


class ImageException extends Exception {
}


?>