<?php

class GDImageFromFile implements IGDImage
{
	protected $image;
	protected $width;
	protected $height;
	protected $format;
	
	public function __construct($imagePath, $format = null)
	{
		$this->__destruct();
		
		if ( !$format )
		{
			$format = array_reverse(explode('.', $imagePath));
			$format = strtolower($format[0]);
		}
		
		switch ( $format )
		{
			case 'jpg':
			case 'jpe':
			case 'jpeg':
				if ( !($this->image = @imagecreatefromjpeg($imagePath)) )
				{
					throw new Exception('Unable to create image from file "' . $imagePath . '".');
				}
				$this->format = 'jpeg';
				break;
			case 'png':
				if ( !($this->image = @imagecreatefrompng($imagePath)) )
				{
					throw new Exception('Unable to create image from file "' . $imagePath . '".');
				}
				$this->format = 'png';
				break;
			case 'gif':
				if ( !($this->image = @imagecreatefromgif($imagePath)) )
				{
					throw new Exception('Unable to create image from file "' . $imagePath . '".');
				}
				$this->format = 'gif';
				break;
			default:
				throw new Exception('Unsupported image format "' . $format . '".');
		}
		
		$this->width = imagesx($this->image);
		$this->height = imagesy($this->image);
	}
	
	public function __destruct()
	{
		@imagedestroy($this->image);
		$this->width = null;
		$this->height = null;
		$this->mimetype = null;
	}
	
	public function getWidth()
	{
		return $this->width;
	}
	
	public function getHeight()
	{
		return $this->height;
	}
	
	public function getMIME()
	{
		return 'image/' . $this->format;
	}
	
	public function getImage($format = null, $level = null, $filters = null)
	{
		switch ( strtolower($format) )
		{
			case null:
				$format = $this->format;
				break;
			case 'jpg':
			case 'jpe':
			case 'jpeg':
				$format = 'jpeg';
				break;
			case 'png':
				$format = 'png';
				break;
			case 'gif':
				$format = 'gif';
				break;
			default:
				throw new Exception('Unsupported image format "' . $format . '".');
		}
		
		$outputFunction = 'image' . $format;
		
		if ( !function_exists($outputFunction) )
		{
			throw new Exception('Unable to output image: required function "' . $outputFunction . '" does not exist.');
		}
		
		switch ( $format )
		{
			case 'jpeg':
				if ( (($level >= 0 && $level <= 100) || $level === null)
					&& $outputFunction($this->image, null, $level) )
				{
					return true;
				}
				else
				{
					throw new Exception('Unable to output image: $level "' . $level . '" not within range 0-100, or function "' . $outputFunction . '" failed.');
				}
				break;
			case 'png':
				if ( (($level >= 0 && $level <= 9) || $level === null)
					&& $outputFunction($this->image, null, $level, $filters) )
				{
					return true;
				}
				else
				{
					throw new Exception('Unable to output image: $level "' . $level . '" not within range 0-9, or function "' . $outputFunction . '" failed.');
				}
				break;
			case 'gif':
				if ( $outputFunction($this->image, null) )
				{
					return true;
				}
				else
				{
					throw new Exception('Unable to save image to file "' . $filename . '".');
				}
				break;
			default:
				throw new Exception('Unsupported image format "' . $format . '".');
		}
	}
	
	public function save($format = null, $filename, $level = null, $filters = null)
	{
		switch ( strtolower($format) )
		{
			case null:
				$format = $this->format;
				break;
			case 'jpg':
			case 'jpe':
			case 'jpeg':
				$format = 'jpeg';
				break;
			case 'png':
				$format = 'png';
				break;
			case 'gif':
				$format = 'gif';
				break;
			default:
				throw new Exception('Unsupported image format "' . $format . '".');
		}
		
		$outputFunction = 'image' . $format;
		
		if ( !function_exists($outputFunction) )
		{
			throw new Exception('Unable to save image: required function "' . $outputFunction . '" does not exist.');
		}
		
		switch ( $format )
		{
			case 'jpeg':
				if ( is_writable($filename) && (($level >= 0 && $level <= 100) || $level === null)
					&& $outputFunction($this->image, $filename, $level) )
				{
					return true;
				}
				else
				{
					throw new Exception('Unable to save image to file "' . $filename . '".');
				}
				break;
			case 'png':
				if ( is_writable($filename) && (($level >= 0 && $level <= 9) || $level === null)
					&& $outputFunction($this->image, $filename, $level, $filters) )
				{
					return true;
				}
				else
				{
					throw new Exception('Unable to save image to file "' . $filename . '".');
				}
				break;
			case 'gif':
				if ( is_writable($filename) && $outputFunction($this->image, $filename) )
				{
					return true;
				}
				else
				{
					throw new Exception('Unable to save image to file "' . $filename . '".');
				}
				break;
			default:
				throw new Exception('Unsupported image format "' . $format . '".');
		}
	}
	
	// Mutually exclusive with setSaveAlpha.
	function setAlphaBlending($bool)
	{
		if ( $bool && !imagesavealpha($this->image, false) || !imagealphablending($this->image, (bool) $bool) )
		{
			throw new Exception('Unable to set "imagesavealpha" to "false", and/or "imagealphablending" to "' . (string) $bool . '".');
		}
		else if ( !imagealphablending($this->image, (bool) $bool) )
		{
			throw new Exception('Unable to set "imagealphablending" to "' . (string) $bool . '".');
		}
	}
	
	// Mutually exclusive with setAlphaBlending.
	function setSaveAlpha($bool)
	{
		if ( !$bool && !imagesavealpha($this->image, (bool) $bool) )
		{
			throw new Exception('Unable to set "imagesavealpha" to "' . (string) $bool . '".');
		}
		else if ( !imagealphablending($this->image, false) || !imagesavealpha($this->image, (bool) $bool) )
		{
			throw new Exception('Unable to set "imagealphablending" to "false", and/or "imagesavealpha" to "' . (string) $bool . '".');
		}
	}
	
	// Apparently does not support alpha data.
	function setAntiAlias($bool)
	{
		if ( $bool && !imagesavealpha($this->image, false) || !imageantialias($this->image, (bool) $bool) )
		{
			throw new Exception('Unable to set "imagesavealpha" to "false", and/or "imageantialias" to "' . (string) $bool . '".');
		}
		else if ( !imageantialias($this->image, (bool) $bool) )
		{
			throw new Exception('Unable to set "imageantialias" to "' . (string) $bool . '".');
		}
	}
	
	// It seems imagecopy() must be used in lieu of imagecopymerge() if alpha-channel support is required.
	public function watermarkWith(IGDImage $watermark, $dstX = 0, $dstY = 0, $srcX = 0, $srcY = 0, $srcW = null, $srcH = null)
	{
		$srcW = ($srcW === null) ? $watermark->getWidth() : $srcW;
		$srcH = ($srcH === null) ? $watermark->getHeight() : $srcH;
		
		if ( !imagecopy($this->image, $watermark->image, $dstX, $dstY, $srcX, $srcY, $srcW, $srcH) )
		{
			throw new Exception('Unable to watermark image.');
		}
	}
	
	public function overlayWith(IGDImage $overlay, $dstX = 0, $dstY = 0, $srcX = 0, $srcY = 0, $srcW = null, $srcH = null, $pct = 100)
	{
		$srcW = ($srcW === null) ? $overlay->getWidth() : $srcW;
		$srcH = ($srcH === null) ? $overlay->getHeight() : $srcH;
		
		if ( !imagecopymerge($this->image, $overlay->image, $dstX, $dstY, $srcX, $srcY, $srcW, $srcH, $pct) )
		{
			throw new Exception('Unable to overlay image.');
		}
	}
	
	public function resample($dstW = null, $dstH = null, $preserveRatio = true)
	{
		if ( $preserveRatio && ($dstW === null xor $dstH === null) && ($dstW > 0 || $dstH > 0) )
		{
			$dstW = ($dstW !== null) ? $dstW : ($this->height / ($this->width / $dstH));
			$dstH = ($dstH !== null) ? $dstH : ($this->height / ($this->width / $dstW));
		}
		else if ( ($dstW === null xor $dstH === null) && ($dstW > 0 || $dstH > 0) )
		{
			$dstW = ($dstW !== null) ? $dstW : $this->width;
			$dstH = ($dstH !== null) ? $dstH : $this->height;
		}
		
		if ( !($dstW > 0 && $dstH > 0 ) || !($newImage = imagecreatetruecolor($dstW, $dstH))
			|| !imagecopyresampled($newImage, $this->image, 0, 0, 0, 0, $dstW, $dstH, $this->width, $this->height)
			|| !(imagedestroy($this->image) && $this->image = $newImage) )
		{
			throw new Exception('Unable to resample image.');
		}
	}
	
	public function resize($dstW = null, $dstH = null, $preserveRatio = true)
	{
		if ( $preserveRatio && ($dstW === null xor $dstH === null) && ($dstW > 0 || $dstH > 0) )
		{
			$dstW = ($dstW !== null) ? $dstW : ($this->height / ($this->width / $dstH));
			$dstH = ($dstH !== null) ? $dstH : ($this->height / ($this->width / $dstW));
		}
		else if ( ($dstW === null xor $dstH === null) && ($dstW > 0 || $dstH > 0) )
		{
			$dstW = ($dstW !== null) ? $dstW : $this->width;
			$dstH = ($dstH !== null) ? $dstH : $this->height;
		}
		
		if ( !($dstW > 0 && $dstH > 0 ) || !($newImage = imagecreatetruecolor($dstW, $dstH))
			|| !imagecopyresized($newImage, $this->image, 0, 0, 0, 0, $dstW, $dstH, $this->width, $this->height)
			|| !(imagedestroy($this->image) && $this->image = $newImage) )
		{
			throw new Exception('Unable to resize image.');
		}
	}
}

?>