<?php
/* PageCookery Framework
*
* Author: Explon
*
* Copyright (C) 2009 Explon <explon@gmail.com>
*/

define(RESIZE_OP_TO_FILE, 1);              // Output to file
define(RESIZE_OP_OUTPUT, 2);               // Output to browser
define(RESIZE_OP_NOT_KEEP_SCALE, 4);       // Free scale
define(RESIZE_OP_BEST_RESIZE_WIDTH, 8);    // Scale to width
define(RESIZE_OP_BEST_RESIZE_HEIGHT, 16);  // Scale to height

define(RESIZE_CM_DEFAULT, 0);               // Clipping method: default
define(RESIZE_CM_LEFT_OR_TOP, 1);           // Clipping method: left or top
define(RESIZE_CM_MIDDLE, 2);                // Clipping method: middle
define(RESIZE_CM_RIGHT_OR_BOTTOM, 3);       // Clipping method: right or bottom

class Image
{

	/**
	 * Resize an image.
	 *
	 * @param string $source source file
	 * @param string $source destination file
	 * @param int $width width of destination file (pixel)
	 * @param int $height height of destination file (pixel)
	 * @param int $quality quality of jpeg file
	 * @param int $option options, you add multiple options like 1+2(or 1|2), this utilize function 1 & 2
	 *      1: default, output to file 2: output to browser stream 4: free scale
	 *      8：scale to width 16：scale to height
	 * @param int $clipping clipping method 0: default 1: left or top 2: middle 3: right or bottom
	 * @param int $start_x start X axis (pixel)
	 * @param int $start_y start Y axis (pixel)
	 * @return true
	 */
	
	static function Resize($source, $destination, $width, $height, $quality = 95, $option = RESIZE_OP_TO_FILE, $clipping = RESIZE_CM_MIDDLE, $start_x = 0, $start_y = 0)
	{
		if (!file_exists($source))
		{
			exit('Image::Resize() - Source file not exists: ' . $source);
		}
		
		$image_type = array(
			1 => 'gif',
			2 => 'jpeg',
			3 => 'png'
		);
		
		$type_index = array(
			'gif' => 1,
			'jpg' => 2,
			'jpeg' => 2,
			'jpe' => 2,
			'png' => 3
		);
		
		$imageinfo = @getimagesize($source);

		switch ($imageinfo[2])
		{
			default:
				exit('Image::Resize() - Can\'t detect output image\'s type.');
			break;
			
			case 1:
				$ext = 'gif';
			break;
			
			case 2:
				$ext = 'jpg';
			break;
			
			case 3:
				$ext = 'png';
			break;
		}

		$func_output = 'image' . $image_type[$type_index[$ext]];

		if (!function_exists($func_output))
		{
			exit('Image::Resize() - Function not exists for output: ' . $func_output);
		}

		$func_create = 'imagecreatefrom' . $image_type[$imageinfo[2]];

		if (!function_exists($func_create))
		{
			exit('Image::Resize() - Function not exists for output: ' . $func_create);
		}

		$im = $func_create($source);

		$source_w = $imageinfo[0];
		$source_h = $imageinfo[1];
		
		$source_x = 0;
		$source_y = 0;
		
		$dst_x = 0;
		$dst_y = 0;

		if ($option & RESIZE_OP_BEST_RESIZE_WIDTH)
		{
			$height = round($width * $source_h / $source_w);
		}

		if ($option & RESIZE_OP_BEST_RESIZE_HEIGHT)
		{
			$width = round($height * $source_w / $source_h);
		}

		$fdst_w = $width;
		$fdst_h = $height;

		if ($clipping != RESIZE_CM_DEFAULT)
		{
			// clipping method 1: left or top 2: middle 3: right or bottom

			$source_w -= $start_x;
			$source_h -= $start_y;

			if ($source_w * $height > $source_h * $width)
			{
				$match_w = round($width * $source_h / $height);
				$match_h = $source_h;
			}
			else
			{
				$match_h = round($height * $source_w / $width);
				$match_w = $source_w;
			}

			switch ($clipping)
			{
				case RESIZE_CM_LEFT_OR_TOP:
					$source_x = 0;
					$source_y = 0;
				break;
				
				case RESIZE_CM_MIDDLE:
					$source_x = round(($source_w - $match_w) / 2);
					$source_y = round(($source_h - $match_h) / 2);
				break;
				
				case RESIZE_CM_RIGHT_OR_BOTTOM:
					$source_x = $source_w - $match_w;
					$source_y = $source_h - $match_h;
				break;
			}

			$source_w = $match_w;
			$source_h = $match_h;
			$source_x += $start_x;
			$source_y += $start_y;

		}
		else if (!($option & RESIZE_OP_NOT_KEEP_SCALE))
		{
			if ($source_w * $height > $source_h * $width)
			{
				$fdst_h = round($source_h * $width / $source_w);
				$dst_y = floor(($height - $fdst_h) / 2);
				$fdst_w = $width;
			}
			else
			{
				$fdst_w = round($source_w * $height / $source_h);
				$dst_x = floor(($width - $fdst_w) / 2);
				$fdst_h = $height;
			}
			
			if ($dst_x < 0)
			{
				$dst_x = 0;
				$dst_y = 0;
			}
			
			if ($dst_x > ($width / 2))
			{
				$dst_x = floor($width / 2);
			}
			
			if ($dst_y > ($height / 2))
			{
				$dst_y = floor($height / 2);
			}
		}
		
		if (function_exists('imagecopyresampled') AND function_exists('imagecreatetruecolor'))	// GD Version Check
		{			
			$func_create = 'imagecreatetruecolor';
			$func_resize = 'imagecopyresampled';
		}
		else
		{			
			$func_create = 'imagecreate';
			$func_resize = 'imagecopyresized';
		}

		$newim = $func_create($width, $height);
		
		$black = imagecolorallocate($newim, 0, 0, 0);
		
		//$background = imagecolortransparent($newim, $black);
		
		imagefilledrectangle($newim, 0, 0, $width, $height, $white);
		
		$func_resize($newim, $im, $dst_x, $dst_y, $source_x, $source_y, $fdst_w, $fdst_h, $source_w, $source_h);
		
		if ($source_w > $width AND $source_h > $height AND $width > 200 AND $height > 200 AND $ext != 'gif')
		{
			self::Sharpen($newim, 3);
		}

		if ($option & RESIZE_OP_TO_FILE)
		{
			if (file_exists($destination))
			{
				@unlink($destination);
			}
			
			switch ($type_index[$ext])
			{
				case 1:
				case 3:
					$func_output($newim, $destination);
				break;
				
				case 2:	// JPEG
					$func_output($newim, $destination, $quality);
				break;
			}
		}
		else if ($option & RESIZE_OP_OUTPUT)
		{
			if (function_exists("headers_sent") AND headers_sent())
			{
				exit('Image::Resize() - HTTP already sent, can\'t output image to browser.');
			}
			
			header('Content-Type: image/' . $image_type[$type_index[$ext]]);
			
			$func_output($newim);
		}

		@imagedestroy($im);
		@imagedestroy($newim);
		
		return true;
	}
	
	/**
	 * Sharpen an image.
	 *
	 * @param   resource  image resource
	 * @param   integer  amount to sharpen
	 * @return  object
	 */
	static function Sharpen($im, $amount)
	{
		// Amount should be in the range of 18-10
		$amount = round(abs(-18 + ($amount * 0.08)), 2);

		// Gaussian blur matrix
		$matrix = array
		(
			array(-1,   -1,    -1),
			array(-1, $amount, -1),
			array(-1,   -1,    -1),
		);

		// Perform the sharpen
		return imageconvolution($im, $matrix, $amount - 8, 0);
	}
}

