Login   Register  
PHP Classes
elePHPant
Icontem

File: RImageManipulator.class.php

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of Riccardo Brambilla  >  RImageManipulator  >  RImageManipulator.class.php  >  Download  
File: RImageManipulator.class.php
Role: Class source
Content type: text/plain
Description: Class file
Class: RImageManipulator
Manipulate and apply effects on images
Author: By
Last change: bug if image is cropped and then resized, happened if after the crop operation the resulting image had width = height
Date: 6 years ago
Size: 32,858 bytes
 

Contents

Class file image Download
<?php
//  ------------------------------------------------------------------------ //
//  RImageManipulator.class.php,v 1.0 2007/02/15 19:00:00 R.Brambilla        //
//  ------------------------------------------------------------------------ //
//                RImageManipulator - Image Manipulation Class               //
//                  Copyright (C) 2007  Riccardo Brambilla                   //
//                         <ribrambilla@tiscali.it>                   		 //
//  ------------------------------------------------------------------------ //
//  This program is free software; you can redistribute it and/or modify     //
//  it under the terms of the GNU General Public License as published by     //
//  the Free Software Foundation; either version 2 of the License, or        //
//  (at your option) any later version.                                      //
//                                                                           //
//  You may not change or alter any portion of this comment or credits       //
//  of supporting developers from this source code or any supporting         //
//  source code which is considered copyrighted (c) material of the          //
//  original comment or credit authors.                                      //
//                                                                           //
//  This program is distributed in the hope that it will be useful,          //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of           //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            //
//  GNU General Public License for more details.                             //
//                                                                           //
//  You should have received a copy of the GNU General Public License        //
//  along with this program; if not, write to the Free Software              //
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA //
//  ------------------------------------------------------------------------ //

/**
 * Options are detected from a simple config. file
 * HINT => Change path of the ini file in "parse_ini_file" function to an absolute one
 * 		   dirname(__FILE__) works well only if conf.ini.php file is saved 
 * 		   in the same folder as the class file's one
 */
$conf = parse_ini_file( dirname(__FILE__) . "/conf.ini.php");

$lang = $conf['lang'];
$er_image_w = $conf['er_image_w'];
$er_image_h = $conf['er_image_h'];
$debug_mode = $conf['debug_mode'];

/**
 * Including files
 * [1] Language file
 * HINT => Change path of the lang file to an absolute one
 * 		   dirname(__FILE__) works well only if lang file is saved 
 * 		   in the same folder as the class file's one
 */
if(file_exists( dirname(__FILE__) . "/" . $lang . ".php" )){
	include_once( dirname(__FILE__) . "/" . $lang . ".php" );
}
else {
	die("LANGUAGE FILE NOT FOUND!");
}


/**
 * Handles images 
 * Operations supported: resize ANDOR rotate ANDOR apply filters ANDOR interlace AND save the brand new Image
 * 
 * @author  Riccardo Brambilla aka ricketno <riccardobra@gmail.com>
 * @package RImageManipulator
 * @version 1.0 Feb 2007 PHP5
 *
 */
class RImageManipulator
{
	
	/**
	 * Class var :: $image_pathname
	 *
	 * @var string
	 * @access private
	 */
	private $image_pathname;
	
	/**
	 * Class var :: $image_name
	 *
	 * @var string
	 * @access private
	 */
	private $image_name;
	
	/**
	 * Class var :: $image_path
	 *
	 * @var string
	 * @access private
	 */
	private $image_path;
	
	/**
	 * Class var :: $ext
	 *
	 * @var string
	 * @access private
	 */
	private $ext;
	
	/**
	 * Class var :: $new_w
	 *
	 * @var int
	 * @access private
	 */
	private $new_w;
	
	/**
	 * Class var :: $new_h
	 *
	 * @var int
	 * @access private
	 */
	private $new_h;
	
	/**
	 * Class var :: $new_name
	 *
	 * @var string
	 * @access private
	 */
	private $new_name;
	
	/**
	 * Class var :: $mantain_prop
	 *
	 * @var bool
	 * @access private
	 */
	private $constrain_prop = true;
	
	/**
	 * Class var :: $allowed_ext
	 *
	 * @var array
	 * @access private
	 */
	private $allowed_ext = array( 'jpg', 'jpeg', 'png', 'gif' );
		
	/**
	 * Class var :: image resource
	 *
	 * @var resource
	 * @access private
	 */
	private $im_resource;
	
	/**
	 * Class var :: $img_info
	 *
	 * @var array
	 * @access private
	 */
	private $im_resource_info = array();
	
	/**
	 * Class var :: $error_string
	 * Holds infos about errors occurred
	 *
	 * @var string
	 * @access public
	 */
	public $error_string = "";
	
	/**
	 * Class var :: $memory_needed
	 * Memory needed for script to perform actions
	 *
	 * @var int
	 * @access private
	 */
	private $memory_needed = 0;
		
	
	/**
	 * CLASS Constants
	 * Defaul Size values
	 * Note: _WIDTH & _HEIGHT are both of type int
	 *
	 */
	const _WIDTH  = 120;
	const _HEIGHT = 120;	
		
	/**
	 * CLASS Constants
	 * Constants used by the alloc(), predictMemoryUsage() functions
	 *
	 */
	const _MB      = 1048576;         // Bytes in a MB 
   	const _64K     = 65536;    		  // Bytes in 64K
   	const _TFACTOR = 4.8;      		  // This value works for me :: you can change it
   	const _UPPERLIMIT   = 83886080;   // 80MB ( in Bytes ) Limit :: you can change it

	
	/**
	 * Class Constructor
	 *
	 * @param  string $image_pathname
	 * @param  string $new_name
	 * @return RImageManipulator
	 * 
	 * @access public
	 */
	public function __construct( $image_pathname, $new_name = "" )
	{
		
		// :: CHECKING INPUTS
			
		if(!is_string($image_pathname) || !ereg(".", $image_pathname))
		{
			$this->setError(_ERR_PATHINVALID, 1);
		}
		
		if(!is_file($image_pathname) || !file_exists($image_pathname))
		{
			$this->setError(_ERR_NOTAFILE, 1);
		}

		// :: SETTING CLASS VARS
		
		// : Extension manipulation
		$this->image_pathname = $image_pathname;
		
		$path_components  = explode("/", $this->image_pathname);
		$image_name_ext   = array_pop($path_components);// returns the last element of the path array ( name.ext )
		
		// : If image is in the same directory of the script then path is dirname(__FILE__)
		// : else path is the result of the implode function 
		$this->image_path = sizeof($path_components > 0) ? implode("/", $path_components) : dirname(__FILE__);	
		
		// : Detecting image name and extension
		$name_components  = explode(".", $image_name_ext);
		$this->ext        = strtolower(array_pop($name_components));// extracts last component
		$this->image_name = implode(".", $name_components);
	
		// : Checking if extension is allowed
		if( empty($this->ext) ) 
			$this->setError(_ERRNOTALLOWED, 1);
		
		if( !in_array($this->ext, $this->allowed_ext) )
			$this->setError(_ERRNOTALLOWED, 1);
		
		// : New name if any ; else original name is used
		$this->new_name = empty($new_name) ? $this->image_name : $new_name;
		
		// : Creates the image resource to work on
		$this->createImageResource();
		
	}// ef
	
	
	/**
	 * Get class var value using class var name
	 *
	 * @param string $var_name 
	 * @return mixed
	 * 
	 * @example $extension = $object->getVar('ext');
	 * @access public
	 */
	public function getVar($var_name = "")
	{
		return (isset($this->$var_name) && !empty($var_name)) ? $this->$var_name : "";	
			
	}// ef

	
	/**
	 * Creates an image resource from filesystem 
	 * Appropriate function is chose on the base of file's extension
	 * 
	 * @access private
	 */
	private function createImageResource()
	{
		
// : Get current image size, extra infos like channels and bits
$this->im_resource_info = getimagesize($this->image_pathname);		
		
		// : We may have to dinamically allocate some extra memory
		$this->alloc();
		
		// :: Creating an empty image
		switch($this->ext)
		{
			
			default:
			case "jpg":// JPG Images
			case "jpeg":
				
				if(!($img = imagecreatefromjpeg($this->image_pathname)))
				{
					 // : Setting the error string
					 $this->setError(_ERR_IMAGECREATION);
					 $this->showImgError();
				}
				
			
				break;
			
			case "png":// PNG Images :: Note: Right pronunciation is "ping" NOT "pee en gee" :)
				
				if(!($img = @imagecreatefrompng($this->image_pathname)))
				{
					 // : Setting the error string
					 $this->setError(_ERR_IMAGECREATION);
					 $this->showImgError();
				}
				
				break;
			
			case "gif":// GIF Images :: Note: Right pronunciation is "jif" NOT "GIF" :)
			
				// Note: GIF Support is expected to return in a GD library version released after mid 2004
				if( function_exists('imagecreatefromgif') && function_exists('imagegif'))
				{
					if((!$img = @imagecreatefromgif($this->image_pathname)))
					{
						 // : Setting the error string
						 $this->setError(_ERR_IMAGECREATION);
						 $this->showImgError();
					}
				}
				else 
				{
					// Gif Support disabled
					$this->setError(_ERR_GIFNOTSUPPORTED);
					$this->showImgError();
				}
				
				break;
			
		}// ends switch
		
		// : Image created is saved as a class var
		// : Type is 'resource'
		$this->im_resource = $img;
		
	}// ef
	
	
	/**
	 * Resizes the image, constrains proportions if asked
	 * 
	 * @param  int $new_w
	 * @param  int $new_h
	 * @param  bool $mantain_prop
	 *   
	 * @access private
	 */
	public function resize( $new_w = 0, $new_h = 0, $constrain_prop = true)
	{
		
		// : We may have to dinamically allocate some extra memory
		$this->alloc();
		
		// :: CHECKING INPUTS
		
		if(!is_int($new_w) || empty($new_w))
		{
			$new_w = self::_WIDTH;
		}
		
		if(!is_int($new_h) || empty($new_h))
		{
			$new_h = self::_HEIGHT;
		}
		
		if(!is_bool($constrain_prop))
		{
			$constrain_prop = true;
		}
		
		// Check if resource is valid
		if (!is_resource($this->im_resource))
		{ 
			$this->setError(_ERR_NOTAVALIDRESOURCE, 1);
		}
		
		// : New Size
		$this->new_w = $new_w;
		$this->new_h = $new_h;
		
		// : Proportion must or must not be kept
		$this->constrain_prop = $constrain_prop;
		
		// Image Size
		$img_width  = $this->im_resource_info[0];// W
		$img_height = $this->im_resource_info[1];// H
		
		// :: CONSTRAIN PROPORTION
		if( $this->constrain_prop )
		{			
				
			// Ratio
			$size_prop  = round( ($img_width/$img_height), 3 );
			
			// W = H square
			if( $size_prop == 1 )
			{		
				
				if( $img_width === $this->new_w && $img_height === $this->new_h)
				{
					$tmp_img = $this->im_resource;// no changes needed
				}
				else 
				{	
					// New W-H
					$new_width  = $this->new_w;
					$new_height = $this->new_h;
					
					// Copy ( resample )
					$tmp_img = ImageCreateTrueColor( $new_width, $new_height );
					imagecopyresampled( $tmp_img, $this->im_resource, 0, 0, 0, 0,$this->new_w, $this->new_h, $img_width, $img_height );
				}			
				
			}// ends square case
			elseif( $size_prop > 1 )// W > H
			{
				
				// : Ratio must be computed
				if( $img_width > $this->new_w )
				{
					$ratio   = ( $img_width/$this->new_w );					
					$new_height = floor( $img_height/$ratio );
				}
				else 
				{
					$ratio   = ( $this->new_w/$img_width );
					$new_height = floor( $img_height*$ratio );
				}
				
				$new_width  = $this->new_w;
				
				// Copy ( resample )
				$tmp_img = ImageCreateTrueColor($new_width,$new_height);
				imagecopyresampled($tmp_img, $this->im_resource, 0, 0, 0, 0,$new_width,$new_height, $img_width, $img_height);
			
			}// ends W > H
			elseif( $size_prop < 1 )// W < H
			{
				
				// : Ratio must be computed
				if( $img_height > $this->new_h )
				{
					$ratio   = ( $img_height/$this->new_h );
					$new_width  = floor( $img_width/$ratio );
				}
				else 
				{
					$ratio   = ( $this->new_h/$img_height );
					$new_width  = floor( $img_width*$ratio );
				}
				
				$new_height = $this->new_h;
				
				// Copy ( resample )
				$tmp_img    = ImageCreateTrueColor($new_width,$new_height);
				imagecopyresampled( $tmp_img, $this->im_resource, 0, 0, 0, 0, $new_width, $new_height, $img_width, $img_height);
			
			}// ends W < H
			else
			{
				$this->setError(_ERR_RESIZE);// Error
			}
			
			// "Refreshing" resource
			$this->im_resource = $tmp_img;
			
		}// ends if costrain proportions
		else // Note: PROPORTIONS WILL BE LOST
		{
			
			// Copy ( resample ) width are height are taken directly from user input
			$tmp_img = ImageCreateTrueColor($this->new_w, $this->new_h);
			imagecopyresampled( $tmp_img, $this->im_resource, 0, 0, 0, 0, $this->new_w, $this->new_h, $img_width, $img_height);
			
			// "Refreshing" resource
			$this->im_resource = $tmp_img;
		
		}// ends main else
		
		$this->im_resource = $tmp_img;
		
		// :: Size infos must be refreshed
		// :: Now size infos are taken directly from the resource itself
		$this->im_resource_info[0] = imagesx($this->im_resource);
		$this->im_resource_info[1] = imagesy($this->im_resource);
		
	}// ef
	
	
	/**
	 * Rotates image by $angle degrees
	 * IMPORTANT NOTE: imagerotate function does not preserve infos about transparency
	 * 
	 * @param int $angle 0 < angle < 360 
	 * @param int $bgc  "background" color :: specifies the color of the uncovered zone after the rotation. 
	 * @param bool $clockwise rotation is clockwise ( default ) or counter clockwise
	 * 
	 * @access public
	 */
	public function rotate($angle, $bgc = 0, $clockwise = true)
	{	
		
		// : We may have to dinamically allocate some extra memory
		$this->alloc();
		
		// :: CHECKING INPUTS
		
		if(!is_int($angle) || ($angle % 90) != 0)
		{
			$angle = 90;// angle must be a multiple of 90
		}
		
		if(!is_int($bgc) || $bgc > 360 || $bgc < 0 )
		{
			$bgc = 0;
		}
		
		if( $clockwise == true ) 
		{
			$angle = abs(360 - $angle);
		}
       
		// Check if resource is valid
		if (!is_resource($this->im_resource))
		{ 
			$this->setError(_ERR_NOTAVALIDRESOURCE, 1);
		}
		
		// : Rotation
		$this->im_resource = @imagerotate($this->im_resource, $angle, $bgc ) or $this->setError(_ERR_ROTATION);
		
	}// ef
	
	
	/**
	 * Applies filters provided by imagefilter function
	 * Filter Constants are numeric; range is between 0 and 10
	 * Performs checks on optional args number
	 * Filter names are:
	 * 
	 * IMG_FILTER_NEGATE            => 0
	 * IMG_FILTER_GRAYSCALE 		=> 1
	 * IMG_FILTER_BRIGHTNESS 		=> 2
	 * IMG_FILTER_CONTRAST 			=> 3
	 * IMG_FILTER_COLORIZE 			=> 4
	 * IMG_FILTER_EDGEDETECT 		=> 5
	 * IMG_FILTER_EMBOSS 			=> 6
	 * IMG_FILTER_GAUSSIAN_BLUR 	=> 7
	 * IMG_FILTER_SELECTIVE_BLUR 	=> 8
	 * IMG_FILTER_MEAN_REMOVAL 		=> 9
	 * IMG_FILTER_SMOOTH 			=> 10
	 * 
	 * @param int $filter_index
	 * @param array $args optional args
	 * 
	 * @access public
	 */
	public function applyFilter( $filter_index = 0 )
	{
		
		// : We may have to dinamically allocate some extra memory
		$this->alloc();
		
		// :: CHECK
		// Index is not in the range 0 - 10
		if($filter_index < 0 || $filter_index > 10)
		{
			$this->setError(_ERR_FILTERDOESNOTEXIST);
		}
		
		// Check if resource is valid
		if (!is_resource($this->im_resource))
		{ 
			$this->setError(_ERR_NOTAVALIDRESOURCE, 1);
		}
		
		// : How many args were sent to the function
		$args_num = func_num_args();
		
		// : Args
		$args = func_get_args();
		
		// :: Switch between indexes, options have their own number of parameters
		switch($filter_index)
		{
			
			default:
			case IMG_FILTER_NEGATE:
				@imagefilter($this->im_resource, $filter_index); 
				break;
				
			case IMG_FILTER_GRAYSCALE:
				@imagefilter($this->im_resource, $filter_index); 
				break;
				
			case IMG_FILTER_BRIGHTNESS:
				
				if($args_num != 2)
					$this->setError(_ERR_FILTERPARAMNUMBER);
					
				@imagefilter($this->im_resource, $filter_index, $args[1]); 
				break;
				
			case IMG_FILTER_CONTRAST:
				
				if($args_num != 2)
					$this->setError(_ERR_FILTERPARAMNUMBER);
					
				@imagefilter($this->im_resource, $filter_index, $args[1]); 
				break;
				
			case IMG_FILTER_COLORIZE:
				
				if($args_num != 4)
					$this->setError(_ERR_FILTERPARAMNUMBER);
					
				@imagefilter($this->im_resource, $filter_index, $args[1], $args[2], $args[3]); 
				break;
			
			case IMG_FILTER_EDGEDETECT:
				@imagefilter($this->im_resource, $filter_index); 
				break;
				
			case IMG_FILTER_EMBOSS:
				@imagefilter($this->im_resource, $filter_index); 
				break;
				
			case IMG_FILTER_GAUSSIAN_BLUR:
				@imagefilter($this->im_resource, $filter_index); 
				break;
				
			case IMG_FILTER_SELECTIVE_BLUR:
				@imagefilter($this->im_resource, $filter_index); 
				break;
				
			case IMG_FILTER_MEAN_REMOVAL:
				@imagefilter($this->im_resource, $filter_index); 
				break;		
			
			case IMG_FILTER_SMOOTH:
								
				if($args_num != 2)
					$this->setError(_ERR_FILTERPARAMNUMBER);
					
				@imagefilter($this->im_resource, $filter_index, $args[1]); 
				break;				

		}// ends switch
	
	}// ef
	

	/**
	 * Interlace or deinterlace image
	 *
	 * @param int $interlace_bit_val
	 */
	public function interlace( $interlace_bit_val = 0 )
	{
		
		// :: CHECKING INPUTS
		
		if( !in_array($interlace_bit_val, array(1, 0)) )
			$interlace_bit_val = 0;
			
		// : We may have to dinamically allocate some extra memory
		$this->alloc();
		
		// Check if resource is valid
		if (!is_resource($this->im_resource))
		{ 
			$this->setError(_ERR_NOTAVALIDRESOURCE, 1);
		}		
		
		// : Interlace op
		@imageinterlace($this->im_resource, $interlace_bit_val);
		
	}// ef
	
	
	/**
	 * Crops image
	 *
	 * @param int $crop_width
	 * @param int $crop_height
	 * @param int $d_x
	 * @param int $d_y
	 * @param int $src_x
	 * @param int $src_y
	 * @param array $bgc 
	 * 
	 * @access public
	 */
	public function crop($crop_width, $crop_height, $d_x = 0, $d_y = 0, $src_x = 0, $src_y = 0, $bgc = array())
	{
		
		// : We may have to dinamically allocate some extra memory
		$this->alloc();
		
		// :: CHECKING INPUTS
		
		// Is resource a valid one
		if (!is_resource($this->im_resource))
		{ 
			$this->setError(_ERR_NOTAVALIDRESOURCE, 1);
		}		
		
		if(!is_int($crop_width))
		{
			$crop_width = $this->im_resource_info[0];
		}
		
		if(!is_int($crop_height))
		{
			$crop_height = $this->im_resource_info[1];
		}
		
		if(!is_array($bgc) || count($bgc) !== 3)
		{
			$bgc = array(255, 255, 255);
		}
	
		// : Fill
		$tmp_img  = ImageCreateTrueColor($crop_width, $crop_height);
		$bg_color = imagecolorallocate($tmp_img, $bgc[0], $bgc[1], $bgc[2]);
		imagefill($tmp_img, 0, 0, $bg_color);
		
		// Copy ( resample )
		@imagecopyresampled( $tmp_img, $this->im_resource, (int)$d_x, (int)$d_y, (int)$src_x, (int)$src_y, $crop_width, $crop_height, $crop_width, $crop_height);
		
		// :: Size infos must be refreshed
		$this->im_resource_info[0] = $crop_width;
		$this->im_resource_info[1] = $crop_height;
		$this->im_resource = $tmp_img;
		
	}// ef
	
	
	/**
	 * Draw a message on the image
	 *
	 * @param string $msg
	 * @param int $font
	 * @param int $x
	 * @param int $y
	 * @param array $text_color
	 * 
	 * @access public
	 */
	public function drawString($msg, $font = 2, $x = 0, $y = 0, $text_color = array())
	{
		
		// : We may have to dinamically allocate some extra memory
		$this->alloc();
		
		// :: CHECKING INPUTS
	
		if (!is_resource($this->im_resource))
		{ 
			$this->setError(_ERR_NOTAVALIDRESOURCE, 1);
		}		
		
		if(!is_string($msg))
		{
			$this->setError(_ERR_DRAWNOMSG);
		}
		
		if(!is_int($x) || !is_int($y))
		{
			$x = $y = 0;
		}
		
		if($font < 1 || $font > 5)
		{
			$font = 2;
		}
		
		if(!is_array($text_color) || count($text_color) !== 3)
		{
			$text_color = array(255, 255, 255);
		}
		
		$text_color = imagecolorallocate($this->im_resource, $text_color[0], $text_color[1], $text_color[2]);// a kind of white
		
		// : Writing a message on the canvas
        @imagestring($this->im_resource, $font, $x, $y, $msg, $text_color) or $this->setError(_ERR_DS);
	
	}// ef  
	
		
	/**
	 * Generate a fog-like effect 
	 * It draws an alpha indexed gray rectangle on the image
	 * Note: alpha is 0 to 127, where 0 is opaque
	 *
	 * @param int $alpha
	 * 
	 * @access public
	 */
	function fog($alpha = 63)
	{
		
		// : We may have to dinamically allocate some extra memory
		$this->alloc();
		
		if (!is_resource($this->im_resource))
		{ 
			$this->setError(_ERR_NOTAVALIDRESOURCE, 1);
		}
				
		if(!is_numeric($alpha) || $alpha < 0 || $alpha >127)
		{
			$alpha = 63;
		}
		
		// :: $fog is a light gray, put on the image generate a fog-like effect
		$fog = imagecolorallocatealpha($this->im_resource, 211, 211, 211, (int)$alpha);
		@imagefilledrectangle($this->im_resource, 0, 0, $this->im_resource_info[0], $this->im_resource_info[1], $fog) or $this->setError(_ERR_FOG);

	}// ef
	
	
	/**
	 * Draw a string ( TRUE TYPE FONTS ) on image with a given angle
	 *
	 * @param int $size font size
	 * @param int $angle angolo
	 * @param int $x
	 * @param int $y
	 * @param array $text_color
	 * @param string $font
	 * @param string $msg
	 * 
	 * @access public
	 */
	public function drawTText($size, $angle, $x, $y, $text_color = array(), $msg, $font = 'arial.ttf')
	{
		
		// : We may have to dinamically allocate some extra memory
		$this->alloc();
		
		// :: CHECKING INPUTS
	
		if (!is_resource($this->im_resource))
		{ 
			$this->setError(_ERR_NOTAVALIDRESOURCE, 1);
		}		
		
		if(!is_numeric($size))
		{
			$size = 10;
		}
		
		if(!is_numeric($angle))
		{
			$angle = 0;
		}
		
		if(!is_int($x) || !is_int($y))
		{
			$x = $y = 0;
		}
		
		if(!is_string($font))
		{
			$font = 'arial.ttf';
		}
		
		if(!is_array($text_color) || count($text_color) !== 3)
		{
			$text_color = array(255, 255, 255);
		}
		
		if(!is_string($msg))
		{
			$this->setError(_ERR_DRAWNOMSG);
		}

		// : Allocating text color
		$text_color = imagecolorallocate($this->im_resource, $text_color[0], $text_color[1], $text_color[2]);// a kind of white
		
		// : Draw string
		@imagettftext($this->im_resource, $size, $angle, $x, $y, $text_color, $font, $msg) or $this->setError(_ERR_TTEXT);
	
	}// ef 

	
	/**
	 * Gives a sepia-like effect 
	 * [1] Image is converted in grayscale
	 * [2] Image is converted from RGB to CMYK
	 * [3] CMYK values are changed to obtain a sepia-like effect
	 * [4] CMKY values are converted to RGB
	 * 
	 * @access public
	 */
	public function sepia()
	{
		$this->alloc();
		
		// : First we need to grayscale the image
		$this->applyFilter(1);
		
		// : Let's create an empty image
		$im = imagecreatetruecolor($this->im_resource_info[0],  $this->im_resource_info[1]);
				
		// :: Looping image's pixels
		for ($y = 0; $y < $this->im_resource_info[1]; $y++)
		{
			
			for ($x = 0; $x < $this->im_resource_info[0]; $x++)
			{
				/**
				 * THESE LINES FROM PHP MANUAL
				 * FUNCTION: imagecolorat
				 * Returns the index of the color of the pixel at the specified location
				 * Use bitshifting and masking to access the distinct red, green and blue component values
				 */
				$rgb = imagecolorat($this->im_resource, $x, $y);
				
				$r = ($rgb >> 16) & 0xFF;
				$g = ($rgb >> 8) & 0xFF;
				$b = $rgb & 0xFF;
				
				// : We need CMYK values to obtain our sepia-like effect 
				list($C, $M, $Y, $K) = $this->rgbToCMYK($r, $g, $b);
			
				// : These are custom values, they works for me
				// : You can increase $M ( magenta channel ) to obtain 
				// : a more "purpled" sepia effect
				$M = $M + 0.14;				
				$Y = $Y + 0.30;
				
				// : Reconvert to RGB
				list($r, $g, $b) = $this->cmykToRGB($C, $M, $Y, $K);
				
				// : This is our new sepia-like color
				$col = imagecolorallocate($im, $r, $g, $b);
				
				// : Setting pixel RGB Value
				imagesetpixel($im, $x, $y, $col);

			}// end inner for
		
		}// ends 2D loop
		
		$this->im_resource = $im;
	
	}// ef
	
	
	/**
	 * Converts RGB values to CMYK values
	 * Function first converts to CMY and then
	 * performs the convertion between CMY and CMYK
	 * 
	 * Algorithm found at easyrgb http://www.easyrgb.com/ ( pseudocode )
	 * Here's my php implementation
	 *
	 * @param int $r
	 * @param int $g
	 * @param int $b
	 * @return array
	 * 
	 * @access private
	 */
	private function rgbToCMYK($r, $g, $b)
	{
		
		// RGB to CMY
		$C = 1 - ( $r / 255 );
		$M = 1 - ( $g / 255 );
		$Y = 1 - ( $b / 255 );
		
		//Where CMYK and CMY values = 0  1
		$var_K = 1;
		
		if ( $C < $var_K )   $var_K = $C;
		if ( $M < $var_K )   $var_K = $M;
		if ( $Y < $var_K )   $var_K = $Y;
		
		if ( $var_K == 1 ) { //Black
		   $C = 0;
		   $M = 0;
		   $Y = 0;
		}
		else {
		   $C = ( $C - $var_K ) / ( 1 - $var_K );
		   $M = ( $M - $var_K ) / ( 1 - $var_K );
		   $Y = ( $Y - $var_K ) / ( 1 - $var_K );
		}
		
		// $K ( black value )
		$K = $var_K;
		
		return array($C, $M, $Y, $K);
	
	}// ef
	
	
	/**
	 * Converts CMYK to RGB
	 * First converts from CMYK to CMY
	 * then performs the convertion between CMY and RGB
	 *
	 * Algorithm found at easyrgb http://www.easyrgb.com/ ( pseudocode )
	 * Here's my php implementation
	 * 
	 * @param float $C
	 * @param float $M
	 * @param float $Y
	 * @param float $K
	 * @return array
	 * 
	 * @access private
	 */
	private function cmykToRGB($C, $M, $Y, $K)
	{
				
		// : CMYK and CMY values are 0  1
		// : CMYK to CMY
		$C = doubleval( $C * ( 1 - $K ) + $K );
		$M = doubleval( $M * ( 1 - $K ) + $K );
		$Y = doubleval( $Y * ( 1 - $K ) + $K );
		
		// : CMY to RGB
		$R = ( 1 - $C ) * 255;
		$G = ( 1 - $M ) * 255;
		$B = ( 1 - $Y ) * 255;	
		
		return array($R, $G, $B);
	
	}// ef

	
	/**
	 * Allows multiple manipulations to take effect with a single function call
	 * Optional args for applyFilter are hardcoded here 
	 * For custom behaviours use applyFilter function alone
	 * A funny method isn't it? Enjoy :) 
	 * 
	 * @param  string $combo_code
	 * @param  int $angle rotation angle
	 * @access public
	 * 
	 * @example $istance->$a->rcombo("introtneg", 90);
	 * @example $istance->$a->rcombo("bluebrig");
	 */
	public function rcombo($combo_code = "", $angle = 0)
	{
		
		// : We may have to dinamically allocate some extra memory
		$this->alloc();
		
		if(eregi("int",   $combo_code))
			$this->interlace(1);
		
		if(eregi("rot",   $combo_code))
			$this->rotate($angle);
			
		if(eregi("neg",   $combo_code))
			$this->applyFilter(0);
			
		if(eregi("gray",  $combo_code))
			$this->applyFilter(1);
		
		if(eregi("emb",   $combo_code))
			$this->applyFilter(6);	
			
		if(eregi("blue",  $combo_code))
			$this->applyFilter(4, 0, 0, 255);	
			
		if(eregi("red",   $combo_code))
			$this->applyFilter(4, 255, 0, 0);		
			
		if(eregi("green", $combo_code))
			$this->applyFilter(4, 0, 255, 0);
			
		if(eregi("smo", $combo_code))
			$this->applyFilter(10, 50);		
		
		if(eregi("blur", $combo_code))
			$this->applyFilter(7);	
			
		if(eregi("brig", $combo_code))
			$this->applyFilter(2, 50);	

		if(eregi("edge", $combo_code))
			$this->applyFilter(5);		
		
		if(eregi("cont", $combo_code))
			$this->applyFilter(3, 50);
			
		if(eregi("sketch", $combo_code))
			$this->applyFilter(9);		
		
		if(eregi("sepia",  $combo_code))
			$this->sepia();
		
	}// ef
	
	
	/**
	 * Saves the brand new image to filesystem
	 *
	 * @param  string $suffix string will be appended at the image name 
	 * @access public
	 * 
	 * @example $this->save('_rotated') => filename will be : imagename_rotated.ext 
	 */
	public function save( $suffix = "" )
	{
		
		global $debug_mode;
		
		// : Saving image according to extension
		switch($this->ext)
		{
			// imagejpeg has a third parameter ( int quality :: hard coded here value=100)
			default:
			case "jpg":
			case "jpeg": 
				imagejpeg($this->im_resource, $this->new_name . $suffix . "." . $this->ext, 100);
			break;
			
			case "gif":
				imagegif($this->im_resource,  $this->new_name . $suffix . "." . $this->ext);
				break;
				
			case "png":
				imagepng($this->im_resource,  $this->new_name . $suffix . "." . $this->ext);
				break;
		}// ends switch
		
		// :: Free
		imagedestroy($this->im_resource);
		
		// : Show errors if any
		echo ( empty($this->error_string) && $debug_mode) ? "Op Successfull<br />" : $this->showError();
		
	}// ef
		
	
	/**
	 * Alloc as much memory as needed to work with the image
	 * 
	 * This function inspired by posts appeared on php.net site in the manual page of
	 * imagecreatefromjpeg function
	 * Thanks to yaroukh at gmail dot com, Karolis Tamutis karolis.t_AT_gmail.com,  JohnBrook at pobox dot com
	 *
	 * @access private
	 */
	private function alloc()
	{
		
		// : How many bytes we'll need to allocate
		$memory_needed = !empty($this->memory_needed) ? $this->memory_needed : $this->predictMemoryUsage();
		
		// : Retrieving data on current memory limit
		$current_limit = ceil(ini_get('memory_limit')) * 1024 * 1024; 
		$current_usage = ceil(memory_get_usage());

		// : New limit 
		$new_limit = $current_limit + ceil(($current_usage + $memory_needed - $current_limit ));
		
		if(($new_limit > $current_limit))
		{
			if(!($new_limit > self::_UPPERLIMIT))
			{
				// : Malloc
				ini_set( 'memory_limit', ceil($new_limit/self::_MB)  . 'M' );
			}
			else
			{ 
				$this->setError( sprintf(_MEM_LIMIT_REACHED, $new_limit), 1);
			}
				
		}// endif
		
	}// ef
	
	
	/**
	 * Computes memory needed forn performing operations
	 * 
	 * This function inspired by posts appeared on php.net site in the manual page of
	 * imagecreatefromjpeg function
	 * Thanks to yaroukh at gmail dot com, Karolis Tamutis karolis.t_AT_gmail.com, JohnBrook at pobox dot com
	 * 
	 * @return int
	 * @access private
	 * 
	 */
	private function predictMemoryUsage()
	{
		// : Computing memory needed
		$memory_needed = ceil( (    $this->im_resource_info[0] 
								 *  $this->im_resource_info[1]
							     *  $this->im_resource_info['bits']
							     *  $this->im_resource_info['channels'] / 8
							 	 + self::_64K 
							) 	 							
							* self::_TFACTOR 
							);
							
		return $memory_needed;
		
	}// ef
	
	
	/**
	 * Appends data ($er_msg) to the $error_string Class var
	 * If code is set to 1 execution is stopped
	 *
	 * @param string $er_msg
	 * @param int $code 
	 * 
	 * @access private
	 */
	private function setError($er_msg = "", $code = 0)
	{
		
		$this->error_string .= $er_msg . " ";	
		
		// If code is 1 execution must be stopped
		// Error is fatal 
		if( $code === 1 )
		{	
			$this->showError();
			exit();
		}
		
	}// ef

	
	/**
	 * Shows Current Error String
	 * 
	 * @access public
	 *
	 */
	public function showError()
	{
		global $debug_mode;
		
		if( $debug_mode )
			echo $this->error_string;
		
	}// ef
	
	
	/**
	 * Shows a PNG image or a string with an error message
	 * if something has gone wrong during the image creation
	 * 
	 * @access private
	 */
	private function showImgError()
	{  
		
		// : Image size is a configuration option
		global $er_image_w;
		global $er_image_h;
		
		// :: Creating an empty image
		$im = imagecreate($er_image_w, $er_image_h);
		
		// : Color Allocation
		$bg_color    = imagecolorallocate($im, 160, 0, 0);// a kind of red
        $text_color  = imagecolorallocate($im, 240, 240, 240);// a kind of white
        
        // : Painting the background rectangle with $bg_color
        imagefilledrectangle($im, 0, 0, $er_image_w, $er_image_h, $bg_color);
       
        // : Writing a message on the canvas
        imagestring($im, 4, 20, 10, $this->error_string, $text_color);
        
        // :: Output, checks if headers are already sent
        if(!headers_sent())
        {
			header("Content-type: image/png");
			if(!@imagepng($im)) { $this->showError(); }
        }
        else { echo $this->showError(); }
        
		// :: Free
        imagedestroy($im);
    
	}// ef

	
	/**
	 * Class Destructor
	 * 
	 * @access public
	 */
	public function __destruct()
	{ 
		// If save() has not been called let's destroy the image resource
		if($this->im_resource)
		{
			@imagedestroy($this->im_resource);
		}
	
	}// ef

}// END OF CLASS
?>