<?php
/***************************************************************************
 *                                                                          *
 * Copyright (c) 2005 ISPG Technologies (I) Pvt Ltd. All rights reserved.   *
 *                                                                          *
 * This  is  commercial  software,  only  users  who have purchased a valid *
 * license  and  accept  to the terms of the  License Agreement can install *
 * and use this program.                                                    *
 *                                                                          *
 ****************************************************************************
 * PLEASE READ THE FULL TEXT  OF THE SOFTWARE  LICENSE   AGREEMENT  IN  THE *
 * "copyright.txt" FILE PROVIDED WITH THIS DISTRIBUTION PACKAGE.            *
 ****************************************************************************/
//
//$Id: Newimagemodel.php, Created on 2012/11/14 for image croping and advance resizing functions
//
class NewImageModel
{
	private $fileName;
	private $image;
	protected $imageResized;
	private $widthOriginal;
	private $heightOriginal;
	private $width;
	private $height;
	private $imageSize;
	private $fileExtension;
	private $debug = true;
	private $errorArray = array();
	private $forceStretch = true;
	private $aggresiveSharpening = false;
	private $transparentArray = array('.png', '.gif');
	private $keepTransparency = true;
	private $fillColorArray = array('r' => 255, 'g' => 255, 'b' => 255);
	private $sharpenArray = array('jpg');
	private $psdReaderPath;
	private $filterOverlayPath;
	private $isInterlace;
	private $captionBoxPositionArray = array();
	private $fontDir = 'fonts';
	private $cropFromTopPercent = 10;
	function __construct($fileName)
	{
		if (!$this->testGDInstalled())
		{
			if ($this->debug)
			{
				die('The GD Library is not installed.');
			}
			else
			{
				die();
			}
		}
		;
		$this->initialise();
		// *** Save the image file name. Only store this incase you want to display it
		$this->fileName = $fileName;
		$extension      = exif_imagetype($fileName);
		if ($extension == 1)
		{
			$this->fileExtension = '.gif';
		}
		else if ($extension == 2)
		{
			$this->fileExtension = '.jpeg';
		}
		else if ($extension == 3)
		{
			$this->fileExtension = '.png';
		}
		else if ($extension == 6)
		{
			$this->fileExtension = '.bmp';
		}
		else
		{
			$this->fileExtension = strtolower(strrchr($fileName, '.'));
		}
		// *** Open up the file
		$this->image        = $this->openImage($fileName);
		// *** Assign here so we don't modify the original
		$this->imageResized = $this->image;
		// *** If file is an image
		if ($this->testIsImage($this->image))
		{
			// *** Get width and height
			$this->width          = imagesx($this->image);
			$this->widthOriginal  = imagesx($this->image);
			$this->height         = imagesy($this->image);
			$this->heightOriginal = imagesy($this->image);
			$this->imageSize      = getimagesize($this->fileName);
		}
		else
		{
			$this->errorArray[] = 'File is not an image';
		}
	}
	private function initialise()
	{
		// *** Set if image should be interlaced or not. 
		$this->isInterlace = false;
	}
	public function resizeImage($newWidth, $newHeight, $option = 0, $sharpen = false)
	{
		// *** We can pass in an array of options to change the crop position
		$cropPos = 'm';
		if (is_array($option) && strtolower($option[0]) == 'crop')
		{
			$cropPos = $option[1]; # get the crop option
		}
		else if (strpos($option, '-') !== false)
		{
			// *** Or pass in a hyphen seperated option
			$optionPiecesArray = explode('-', $option);
			$cropPos           = end($optionPiecesArray);
		}
		// *** Check the option is valid
		$option = $this->prepOption($option);
		// *** Make sure the file passed in is valid
		if (!$this->image)
		{
			if ($this->debug)
			{
				die('file ' . $this->getFileName() . ' is missing or invalid');
			}
			else
			{
				die();
			}
		}
		;
		
		
		/* //  Get optimal width and height - based on $option
		$dimensionsArray    = $this->getDimensions($newWidth, $newHeight, $option);
		$optimalWidth       = $dimensionsArray['optimalWidth'];
		$optimalHeight      = $dimensionsArray['optimalHeight'];
		//  Resample - create image canvas of x, y size
		$this->imageResized = imagecreatetruecolor($optimalWidth, $optimalHeight);
		$this->keepTransparancy($optimalWidth, $optimalHeight, $this->imageResized);
		imagecopyresampled($this->imageResized, $this->image, 0, 0, 0, 0, $optimalWidth, $optimalHeight, $this->width, $this->height); */
		
		$imageWidth=$width=$newWidth ;
		$imageHeight=$height=$newHeight;

		list($width_orig, $height_orig) = $this->imageSize;

		$ratio_orig = $width_orig/$height_orig;

		if ($width/$height > $ratio_orig) {
		   $width = $height*$ratio_orig;
		} else {
		   $height = $width/$ratio_orig;
		}
		
		$this->imageResized = imagecreatetruecolor($imageWidth, $imageHeight);
		$white = imagecolorallocate($this->imageResized, 255, 255, 255);
		imagefill($this->imageResized, 0, 0, $white);
		
		$this->keepTransparancy($optimalWidth, $optimalHeight, $this->imageResized);

		imagecopyresampled($this->imageResized, $this->image, ($imageWidth-$width)/2, ($imageHeight-$height)/2, 0 , 0, $width, $height, $width_orig, $height_orig); 

		// *** If '4', then crop too
		if ($option == 4 || $option == 'crop')
		{
			if (($optimalWidth >= $newWidth && $optimalHeight >= $newHeight))
			{
				$this->crop($optimalWidth, $optimalHeight, $newWidth, $newHeight, $cropPos);
			}
		}
		// *** Sharpen image (if jpg and the user wishes to do so)
		if ($sharpen && in_array($this->fileExtension, $this->sharpenArray))
		{
			// *** Sharpen
			$this->sharpen();
		}
	}
	## --------------------------------------------------------
	private function keepTransparancy($width, $height, $im)
	{
		// *** If PNG, perform some transparency retention actions (gif untested)
		if (in_array($this->fileExtension, $this->transparentArray) && $this->keepTransparency)
		{
			$color = imagecolorallocate($im, 255, 255, 255);
			// Fill the background with white
			imagefill($im, 0, 0, $color);
			// Alpha blending must be enabled on the background!
			imagealphablending($im, TRUE);
		
			//imagealphablending($im, false);
			//imagesavealpha($im, true);
			$transparent = imagecolorallocatealpha($im, 255, 255, 255, 127);
			imagefilledrectangle($im, 0, 0, $width, $height, $transparent);
		}
		else
		{
			$color = imagecolorallocate($im, $this->fillColorArray['r'], $this->fillColorArray['g'], $this->fillColorArray['b']);
			imagefilledrectangle($im, 0, 0, $width, $height, $color);
		}
	}
	private function crop($optimalWidth, $optimalHeight, $newWidth, $newHeight, $cropPos)
	{
		// *** Get cropping co-ordinates
		$cropArray  = $this->getCropPlacing($optimalWidth, $optimalHeight, $newWidth, $newHeight, $cropPos);
		$cropStartX = $cropArray['x'];
		$cropStartY = $cropArray['y'];
		// *** Crop this bad boy
		$crop       = imagecreatetruecolor($newWidth, $newHeight);
		$this->keepTransparancy($optimalWidth, $optimalHeight, $crop);
		imagecopyresampled($crop, $this->imageResized, 0, 0, $cropStartX, $cropStartY, $newWidth, $newHeight, $newWidth, $newHeight);
		$this->imageResized = $crop;
		// *** Set new width and height to our variables
		$this->width        = $newWidth;
		$this->height       = $newHeight;
	}
	private function getCropPlacing($optimalWidth, $optimalHeight, $newWidth, $newHeight, $pos = 'm')
	{
		$pos = strtolower($pos);
		// *** If co-ords have been entered
		if (strstr($pos, 'x'))
		{
			$pos     = str_replace(' ', '', $pos);
			$xyArray = explode('x', $pos);
			list($cropStartX, $cropStartY) = $xyArray;
		}
		else
		{
			switch ($pos)
			{
				case 'tl':
					$cropStartX = 0;
					$cropStartY = 0;
					break;
				case 't':
					$cropStartX = ($optimalWidth / 2) - ($newWidth / 2);
					$cropStartY = 0;
					break;
				case 'tr':
					$cropStartX = $optimalWidth - $newWidth;
					$cropStartY = 0;
					break;
				case 'l':
					$cropStartX = 0;
					$cropStartY = ($optimalHeight / 2) - ($newHeight / 2);
					break;
				case 'm':
					$cropStartX = ($optimalWidth / 2) - ($newWidth / 2);
					$cropStartY = ($optimalHeight / 2) - ($newHeight / 2);
					break;
				case 'r':
					$cropStartX = $optimalWidth - $newWidth;
					$cropStartY = ($optimalHeight / 2) - ($newHeight / 2);
					break;
				case 'bl':
					$cropStartX = 0;
					$cropStartY = $optimalHeight - $newHeight;
					break;
				case 'b':
					$cropStartX = ($optimalWidth / 2) - ($newWidth / 2);
					$cropStartY = $optimalHeight - $newHeight;
					break;
				case 'br':
					$cropStartX = $optimalWidth - $newWidth;
					$cropStartY = $optimalHeight - $newHeight;
					break;
				case 'auto':
					// *** If image is a portrait crop from top, not center. v1.5 
					if ($optimalHeight > $optimalWidth)
					{
						$cropStartX = ($optimalWidth / 2) - ($newWidth / 2);
						$cropStartY = ($this->cropFromTopPercent / 100) * $optimalHeight;
					}
					else
					{
						// *** Else crop from the center
						$cropStartX = ($optimalWidth / 2) - ($newWidth / 2);
						$cropStartY = ($optimalHeight / 2) - ($newHeight / 2);
					}
					break;
				default:
					// *** Default to center
					$cropStartX = ($optimalWidth / 2) - ($newWidth / 2);
					$cropStartY = ($optimalHeight / 2) - ($newHeight / 2);
					break;
			}
		}
		return array(
			'x' => $cropStartX,
			'y' => $cropStartY
		);
	}
	private function getDimensions($newWidth, $newHeight, $option)
	{
		switch (strval($option))
		{
			case '0':
			case 'exact':
				$optimalWidth  = $newWidth;
				$optimalHeight = $newHeight;
				break;
			case '1':
			case 'portrait':
				$dimensionsArray = $this->getSizeByFixedHeight($newWidth, $newHeight);
				$optimalWidth    = $dimensionsArray['optimalWidth'];
				$optimalHeight   = $dimensionsArray['optimalHeight'];
				break;
			case '2':
			case 'landscape':
				$dimensionsArray = $this->getSizeByFixedWidth($newWidth, $newHeight);
				$optimalWidth    = $dimensionsArray['optimalWidth'];
				$optimalHeight   = $dimensionsArray['optimalHeight'];
				break;
			case '3':
			case 'auto':
				$dimensionsArray = $this->getSizeByAuto($newWidth, $newHeight);
				$optimalWidth    = $dimensionsArray['optimalWidth'];
				$optimalHeight   = $dimensionsArray['optimalHeight'];
				break;
			case '4':
			case 'crop':
				$dimensionsArray = $this->getOptimalCrop($newWidth, $newHeight);
				$optimalWidth    = $dimensionsArray['optimalWidth'];
				$optimalHeight   = $dimensionsArray['optimalHeight'];
				break;
		}
		return array(
			'optimalWidth' => $optimalWidth,
			'optimalHeight' => $optimalHeight
		);
	}
	private function getSizeByFixedHeight($newWidth, $newHeight)
	{
		// *** If forcing is off...
		if (!$this->forceStretch)
		{
			if ($this->height < $newHeight)
			{
				return array(
					'optimalWidth' => $this->width,
					'optimalHeight' => $this->height
				);
			}
		}
		$ratio    = $this->width / $this->height;
		$newWidth = $newHeight * $ratio;
		return array(
			'optimalWidth' => $newWidth,
			'optimalHeight' => $newHeight
		);
	}
	private function getSizeByFixedWidth($newWidth, $newHeight)
	{
		if (!$this->forceStretch)
		{
			if ($this->width < $newWidth)
			{
				return array(
					'optimalWidth' => $this->width,
					'optimalHeight' => $this->height
				);
			}
		}
		$ratio     = $this->height / $this->width;
		$newHeight = $newWidth * $ratio;
		return array(
			'optimalWidth' => $newWidth,
			'optimalHeight' => $newHeight
		);
	}
	private function getSizeByAuto($newWidth, $newHeight)
	{
		if (!$this->forceStretch)
		{
			if ($this->width < $newWidth && $this->height < $newHeight)
			{
				return array(
					'optimalWidth' => $this->width,
					'optimalHeight' => $this->height
				);
			}
		}
		if ($this->height < $this->width)
		{
			$dimensionsArray = $this->getSizeByFixedWidth($newWidth, $newHeight);
			$optimalWidth    = $dimensionsArray['optimalWidth'];
			$optimalHeight   = $dimensionsArray['optimalHeight'];
		}
		elseif ($this->height > $this->width)
		{
			$dimensionsArray = $this->getSizeByFixedHeight($newWidth, $newHeight);
			$optimalWidth    = $dimensionsArray['optimalWidth'];
			$optimalHeight   = $dimensionsArray['optimalHeight'];
		}
		else
		{
			if ($newHeight < $newWidth)
			{
				$dimensionsArray = $this->getSizeByFixedWidth($newWidth, $newHeight);
				$optimalWidth    = $dimensionsArray['optimalWidth'];
				$optimalHeight   = $dimensionsArray['optimalHeight'];
			}
			else if ($newHeight > $newWidth)
			{
				$dimensionsArray = $this->getSizeByFixedHeight($newWidth, $newHeight);
				$optimalWidth    = $dimensionsArray['optimalWidth'];
				$optimalHeight   = $dimensionsArray['optimalHeight'];
			}
			else
			{
				$optimalWidth  = $newWidth;
				$optimalHeight = $newHeight;
			}
		}
		return array(
			'optimalWidth' => $optimalWidth,
			'optimalHeight' => $optimalHeight
		);
	}
	private function getOptimalCrop($newWidth, $newHeight)
	{
		if (!$this->forceStretch)
		{
			if ($this->width < $newWidth && $this->height < $newHeight)
			{
				return array(
					'optimalWidth' => $this->width,
					'optimalHeight' => $this->height
				);
			}
		}
		$heightRatio = $this->height / $newHeight;
		$widthRatio  = $this->width / $newWidth;
		if ($heightRatio < $widthRatio)
		{
			$optimalRatio = $heightRatio;
		}
		else
		{
			$optimalRatio = $widthRatio;
		}
		$optimalHeight = $this->height / $optimalRatio;
		$optimalWidth  = $this->width / $optimalRatio;
		return array(
			'optimalWidth' => $optimalWidth,
			'optimalHeight' => $optimalHeight
		);
	}
	private function sharpen()
	{
		if (version_compare(PHP_VERSION, '5.1.0') >= 0)
		{
			if ($this->aggresiveSharpening) # A more aggressive sharpening solution
			{
				$sharpenMatrix = array(
					array(
						-1,
						-1,
						-1
					),
					array(
						-1,
						16,
						-1
					),
					array(
						-1,
						-1,
						-1
					)
				);
				$divisor       = 8;
				$offset        = 0;
				imageconvolution($this->imageResized, $sharpenMatrix, $divisor, $offset);
			}
			else
			{
				$sharpness     = $this->findSharp($this->widthOriginal, $this->width);
				$sharpenMatrix = array(
					array(
						-1,
						-2,
						-1
					),
					array(
						-2,
						$sharpness + 12,
						-2
					),
					array(
						-1,
						-2,
						-1
					)
				);
				$divisor       = $sharpness; // adjusts brightness
				$offset        = 0;
				imageconvolution($this->imageResized, $sharpenMatrix, $divisor, $offset);
			}
		}
		else
		{
			if ($this->debug)
			{
				die('Sharpening required PHP 5.1.0 or greater.');
			}
		}
	}
	private function findSharp($orig, $final)
	{
		$final  = $final * (750.0 / $orig);
		$a      = 52;
		$b      = -0.27810650887573124;
		$c      = .00047337278106508946;
		$result = $a + $b * $final + $c * $final * $final;
		return max(round($result), 0);
	}
	private function prepOption($option)
	{
		if (is_array($option))
		{
			if (strtolower($option[0]) == 'crop' && count($option) == 2)
			{
				return 'crop';
			}
			else
			{
				die('Crop resize option array is badly formatted.');
			}
		}
		else if (strpos($option, 'crop') !== false)
		{
			return 'crop';
		}
		if (is_string($option))
		{
			return strtolower($option);
		}
		return $option;
	}
	public function rotate($value = 90, $bgColor = 'transparent')
	{
		if ($this->imageResized)
		{
			if (is_integer($value))
			{
				$degrees = $value;
			}
			$rgbArray = $this->formatColor($bgColor);
			$r        = $rgbArray['r'];
			$g        = $rgbArray['g'];
			$b        = $rgbArray['b'];
			if (isset($rgbArray['a']))
			{
				$a = $rgbArray['a'];
			}
			if (is_string($value))
			{
				$value = strtolower($value);
				switch ($value)
				{
					case 'left':
						$degrees = 90;
						break;
					case 'right':
						$degrees = 270;
						break;
					case 'upside':
						$degrees = 180;
						break;
					default:
						break;
				}
			}
			$degrees = 360 - $degrees;
			$bg      = ImageColorAllocateAlpha($this->imageResized, $r, $g, $b, $a);
			ImageFill($this->imageResized, 0, 0, $bg);
			$this->imageResized = imagerotate($this->imageResized, $degrees, $bg);
			ImageSaveAlpha($this->imageResized, true);
		}
	}
	private function openImage($file)
	{
		if (!file_exists($file) && !$this->checkStringStartsWith('http://', $file))
		{
			if ($this->debug)
			{
				die('Image not found.');
			}
			else
			{
				die();
			}
		}
		;
		$extension = exif_imagetype($file);
		if ($extension == 1)
		{
			$extension = '.gif';
		}
		else if ($extension == 2)
		{
			$extension = '.jpeg';
		}
		else if ($extension == 3)
		{
			$extension = '.png';
		}
		else if ($extension == 6)
		{
			$extension = '.bmp';
		}
		else
		{
			$extension = strrchr($file, '.');
			$extension = strtolower($extension);
		}
		switch ($extension)
		{
			case '.jpg':
			case '.jpeg':
				$img = @imagecreatefromjpeg($file);
				break;
			case '.gif':
				$img = @imagecreatefromgif($file);
				break;
			case '.png':
				$img = @imagecreatefrompng($file);
				break;
			case '.bmp':
				$img = @$this->imagecreatefrombmp($file);
				break;
			// ... etc
			default:
				$img = false;
				break;
		}
		return $img;
	}
	public function reset()
	{
		$this->__construct($this->fileName);
	}
	public function saveImage($savePath, $imageQuality = "100")
	{
		if (!is_resource($this->imageResized))
		{
			if ($this->debug)
			{
				die('saveImage: This is not a resource.');
			}
			else
			{
				die();
			}
		}
		$fileInfoArray = pathInfo($savePath);
		clearstatcache();
		if (!is_writable($fileInfoArray['dirname']))
		{
			if ($this->debug)
			{
				die('The path is not writable. Please check your permissions.');
			}
			else
			{
				die();
			}
		}
		$extension = exif_imagetype($savePath);
		if ($extension == 1)
		{
			$extension = '.gif';
		}
		else if ($extension == 2)
		{
			$extension = '.jpeg';
		}
		else if ($extension == 3)
		{
			$extension = '.png';
		}
		else if ($extension == 6)
		{
			$extension = '.bmp';
		}
		else
		{
			$extension = strrchr($savePath, '.');
			$extension = strtolower($extension);
		}
		$error = '';
		switch ($extension)
		{
			case '.jpg':
			case '.jpeg':
				$this->checkInterlaceImage($this->isInterlace);
				if (imagetypes() & IMG_JPG)
				{
					imagejpeg($this->imageResized, $savePath, $imageQuality);
				}
				else
				{
					$error = 'jpg';
				}
				break;
			case '.gif':
				$this->checkInterlaceImage($this->isInterlace);
				if (imagetypes() & IMG_GIF)
				{
					imagegif($this->imageResized, $savePath);
				}
				else
				{
					$error = 'gif';
				}
				break;
			case '.png':
				$scaleQuality       = round(($imageQuality / 100) * 9);
				$invertScaleQuality = 9 - $scaleQuality;
				$this->checkInterlaceImage($this->isInterlace);
				if (imagetypes() & IMG_PNG)
				{
					imagepng($this->imageResized, $savePath, $invertScaleQuality);
				}
				else
				{
					$error = 'png';
				}
				break;
			case '.bmp':
				file_put_contents($savePath, $this->GD2BMPstring($this->imageResized));
				break;
			default:
				// *** No extension - No save.
				$this->errorArray[] = 'This file type (' . $extension . ') is not supported. File not saved.';
				break;
		}
		if ($error != '')
		{
			$this->errorArray[] = $error . ' support is NOT enabled. File not saved.';
		}
	}
	public function testGDInstalled()
	{
		if (extension_loaded('gd') && function_exists('gd_info'))
		{
			$gdInstalled = true;
		}
		else
		{
			$gdInstalled = false;
		}
		return $gdInstalled;
	}
	public function testIsImage($image)
	{
		if ($image)
		{
			$fileIsImage = true;
		}
		else
		{
			$fileIsImage = false;
		}
		return $fileIsImage;
	}
	public function getFileName()
	{
		return $this->fileName;
	}
	private function checkInterlaceImage($isEnabled)
	{
		if ($isEnabled)
		{
			imageinterlace($this->imageResized, $isEnabled);
		}
	}
	protected function formatColor($value)
	{
		$rgbArray = array();
		if (is_array($value))
		{
			if (key($value) == 0 && count($value) == 3)
			{
				$rgbArray['r'] = $value[0];
				$rgbArray['g'] = $value[1];
				$rgbArray['b'] = $value[2];
			}
			else
			{
				$rgbArray = $value;
			}
		}
		else if (strtolower($value) == 'transparent')
		{
			$rgbArray = array(
				'r' => 255,
				'g' => 255,
				'b' => 255,
				'a' => 127
			);
		}
		else
		{
			$rgbArray = $this->hex2dec($value);
		}
		return $rgbArray;
	}
	function hex2dec($hex)
	{
		$color = str_replace('#', '', $hex);
		if (strlen($color) == 3)
		{
			$color = $color . $color;
		}
		$rgb = array(
			'r' => hexdec(substr($color, 0, 2)),
			'g' => hexdec(substr($color, 2, 2)),
			'b' => hexdec(substr($color, 4, 2)),
			'a' => 0
		);
		return $rgb;
	}
	function checkStringStartsWith($needle, $haystack)
	{
		return (substr($haystack, 0, strlen($needle)) == $needle);
	}
	private function GD2BMPstring(&$gd_image)
	{
		$imageX = ImageSX($gd_image);
		$imageY = ImageSY($gd_image);
		$BMP    = '';
		for ($y = ($imageY - 1); $y >= 0; $y--)
		{
			$thisline = '';
			for ($x = 0; $x < $imageX; $x++)
			{
				$argb = $this->GetPixelColor($gd_image, $x, $y);
				$thisline .= chr($argb['blue']) . chr($argb['green']) . chr($argb['red']);
			}
			while (strlen($thisline) % 4)
			{
				$thisline .= "\x00";
			}
			$BMP .= $thisline;
		}
		$bmpSize          = strlen($BMP) + 14 + 40;
		// BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp
		$BITMAPFILEHEADER = 'BM'; // WORD    bfType;
		$BITMAPFILEHEADER .= $this->LittleEndian2String($bmpSize, 4); // DWORD   bfSize;
		$BITMAPFILEHEADER .= $this->LittleEndian2String(0, 2); // WORD    bfReserved1;
		$BITMAPFILEHEADER .= $this->LittleEndian2String(0, 2); // WORD    bfReserved2;
		$BITMAPFILEHEADER .= $this->LittleEndian2String(54, 4); // DWORD   bfOffBits;
		// BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp
		$BITMAPINFOHEADER = $this->LittleEndian2String(40, 4); // DWORD  biSize;
		$BITMAPINFOHEADER .= $this->LittleEndian2String($imageX, 4); // LONG   biWidth;
		$BITMAPINFOHEADER .= $this->LittleEndian2String($imageY, 4); // LONG   biHeight;
		$BITMAPINFOHEADER .= $this->LittleEndian2String(1, 2); // WORD   biPlanes;
		$BITMAPINFOHEADER .= $this->LittleEndian2String(24, 2); // WORD   biBitCount;
		$BITMAPINFOHEADER .= $this->LittleEndian2String(0, 4); // DWORD  biCompression;
		$BITMAPINFOHEADER .= $this->LittleEndian2String(0, 4); // DWORD  biSizeImage;
		$BITMAPINFOHEADER .= $this->LittleEndian2String(2835, 4); // LONG   biXPelsPerMeter;
		$BITMAPINFOHEADER .= $this->LittleEndian2String(2835, 4); // LONG   biYPelsPerMeter;
		$BITMAPINFOHEADER .= $this->LittleEndian2String(0, 4); // DWORD  biClrUsed;
		$BITMAPINFOHEADER .= $this->LittleEndian2String(0, 4); // DWORD  biClrImportant;
		return $BITMAPFILEHEADER . $BITMAPINFOHEADER . $BMP;
	}
	private function GetPixelColor(&$img, $x, $y)
	{
		if (!is_resource($img))
		{
			return false;
		}
		return @ImageColorsForIndex($img, @ImageColorAt($img, $x, $y));
	}
	private function LittleEndian2String($number, $minbytes = 1)
	{
		$intstring = '';
		while ($number > 0)
		{
			$intstring = $intstring . chr($number & 255);
			$number >>= 8;
		}
		return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT);
	}
	private function ImageCreateFromBMP($filename)
	{
		if (!$f1 = fopen($filename, "rb"))
			return FALSE;
		//1 : Chargement des ent�tes FICHIER
		$FILE = unpack("vfile_type/Vfile_size/Vreserved/Vbitmap_offset", fread($f1, 14));
		if ($FILE['file_type'] != 19778)
			return FALSE;
		//2 : Chargement des ent�tes BMP
		$BMP           = unpack('Vheader_size/Vwidth/Vheight/vplanes/vbits_per_pixel' . '/Vcompression/Vsize_bitmap/Vhoriz_resolution' . '/Vvert_resolution/Vcolors_used/Vcolors_important', fread($f1, 40));
		$BMP['colors'] = pow(2, $BMP['bits_per_pixel']);
		if ($BMP['size_bitmap'] == 0)
			$BMP['size_bitmap'] = $FILE['file_size'] - $FILE['bitmap_offset'];
		$BMP['bytes_per_pixel']  = $BMP['bits_per_pixel'] / 8;
		$BMP['bytes_per_pixel2'] = ceil($BMP['bytes_per_pixel']);
		$BMP['decal']            = ($BMP['width'] * $BMP['bytes_per_pixel'] / 4);
		$BMP['decal'] -= floor($BMP['width'] * $BMP['bytes_per_pixel'] / 4);
		$BMP['decal'] = 4 - (4 * $BMP['decal']);
		if ($BMP['decal'] == 4)
			$BMP['decal'] = 0;
		//3 : Chargement des couleurs de la palette
		$PALETTE = array();
		if ($BMP['colors'] < 16777216)
		{
			$PALETTE = unpack('V' . $BMP['colors'], fread($f1, $BMP['colors'] * 4));
		}
		//4 : Cr�ation de l'image
		$IMG  = fread($f1, $BMP['size_bitmap']);
		$VIDE = chr(0);
		$res  = imagecreatetruecolor($BMP['width'], $BMP['height']);
		$P    = 0;
		$Y    = $BMP['height'] - 1;
		while ($Y >= 0)
		{
			$X = 0;
			while ($X < $BMP['width'])
			{
				if ($BMP['bits_per_pixel'] == 24)
					$COLOR = unpack("V", substr($IMG, $P, 3) . $VIDE);
				elseif ($BMP['bits_per_pixel'] == 16)
				{
					$COLOR    = unpack("v", substr($IMG, $P, 2));
					$blue     = ($COLOR[1] & 0x001f) << 3;
					$green    = ($COLOR[1] & 0x07e0) >> 3;
					$red      = ($COLOR[1] & 0xf800) >> 8;
					$COLOR[1] = $red * 65536 + $green * 256 + $blue;
				}
				elseif ($BMP['bits_per_pixel'] == 8)
				{
					$COLOR    = unpack("n", $VIDE . substr($IMG, $P, 1));
					$COLOR[1] = $PALETTE[$COLOR[1] + 1];
				}
				elseif ($BMP['bits_per_pixel'] == 4)
				{
					$COLOR = unpack("n", $VIDE . substr($IMG, floor($P), 1));
					if (($P * 2) % 2 == 0)
						$COLOR[1] = ($COLOR[1] >> 4);
					else
						$COLOR[1] = ($COLOR[1] & 0x0F);
					$COLOR[1] = $PALETTE[$COLOR[1] + 1];
				}
				elseif ($BMP['bits_per_pixel'] == 1)
				{
					$COLOR = unpack("n", $VIDE . substr($IMG, floor($P), 1));
					if (($P * 8) % 8 == 0)
						$COLOR[1] = $COLOR[1] >> 7;
					elseif (($P * 8) % 8 == 1)
						$COLOR[1] = ($COLOR[1] & 0x40) >> 6;
					elseif (($P * 8) % 8 == 2)
						$COLOR[1] = ($COLOR[1] & 0x20) >> 5;
					elseif (($P * 8) % 8 == 3)
						$COLOR[1] = ($COLOR[1] & 0x10) >> 4;
					elseif (($P * 8) % 8 == 4)
						$COLOR[1] = ($COLOR[1] & 0x8) >> 3;
					elseif (($P * 8) % 8 == 5)
						$COLOR[1] = ($COLOR[1] & 0x4) >> 2;
					elseif (($P * 8) % 8 == 6)
						$COLOR[1] = ($COLOR[1] & 0x2) >> 1;
					elseif (($P * 8) % 8 == 7)
						$COLOR[1] = ($COLOR[1] & 0x1);
					$COLOR[1] = $PALETTE[$COLOR[1] + 1];
				}
				else
					return FALSE;
				imagesetpixel($res, $X, $Y, $COLOR[1]);
				$X++;
				$P += $BMP['bytes_per_pixel'];
			}
			$Y--;
			$P += $BMP['decal'];
		}
		fclose($f1);
		return $res;
	}
	public function __destruct()
	{
		if (is_resource($this->imageResized))
		{
			imagedestroy($this->imageResized);
		}
	}
}
