PageRenderTime 105ms CodeModel.GetById 2ms app.highlight 93ms RepoModel.GetById 1ms app.codeStats 0ms

/includes/captcha/captcha_gd.php

http://seo-phpbb.googlecode.com/
PHP | 1567 lines | 1280 code | 159 blank | 128 comment | 71 complexity | d0c255131e409d86bb11f70358df08b4 MD5 | raw file
   1<?php
   2/**
   3*
   4* @package VC
   5* @version $Id: captcha_gd.php 8479 2008-03-29 00:22:48Z naderman $
   6* @copyright (c) 2006 phpBB Group
   7* @license http://opensource.org/licenses/gpl-license.php GNU Public License
   8*
   9*/
  10
  11/**
  12* @ignore
  13*/
  14if (!defined('IN_PHPBB'))
  15{
  16	exit;
  17}
  18
  19/**
  20* Original Author - Xore (Robert Hetzler)
  21* With contributions from Neothermic
  22*
  23* @package VC
  24*/
  25class captcha
  26{
  27	var $width = 360;
  28	var $height = 96;
  29
  30	/**
  31	* Create the image containing $code with a seed of $seed
  32	*/
  33	function execute($code, $seed)
  34	{
  35		global $config;
  36		srand($seed);
  37		mt_srand($seed);
  38
  39		// Create image
  40		$img = imagecreatetruecolor($this->width, $this->height);
  41
  42		// Generate colours
  43		$colour = new colour_manager($img, array(
  44			'random'	=> true,
  45			'min_value'	=> 60,
  46		), 'hsv');
  47
  48		$scheme = $colour->colour_scheme('background', false);
  49		$scheme = $colour->mono_range($scheme, 10, false);
  50		shuffle($scheme);
  51
  52		$bg_colours = array_splice($scheme, mt_rand(6, 12));
  53
  54		// Generate code characters
  55		$characters = $sizes = $bounding_boxes = array();
  56		$width_avail = $this->width - 15;
  57		$code_len = strlen($code);
  58
  59		$captcha_bitmaps = $this->captcha_bitmaps();
  60		for ($i = 0; $i < $code_len; ++$i)
  61		{
  62			$characters[$i] = new char_cube3d($captcha_bitmaps, $code[$i]);
  63
  64			list($min, $max) = $characters[$i]->range();
  65			$sizes[$i] = mt_rand($min, $max);
  66
  67			$box = $characters[$i]->dimensions($sizes[$i]);
  68			$width_avail -= ($box[2] - $box[0]);
  69			$bounding_boxes[$i] = $box;
  70		}
  71
  72		// Redistribute leftover x-space
  73		$offset = array();
  74		for ($i = 0; $i < $code_len; ++$i)
  75		{
  76			$denom = ($code_len - $i);
  77			$denom = max(1.3, $denom);
  78			$offset[$i] = mt_rand(0, (1.5 * $width_avail) / $denom);
  79			$width_avail -= $offset[$i];
  80		}
  81
  82		if ($config['captcha_gd_x_grid'])
  83		{
  84			$grid = (int) $config['captcha_gd_x_grid'];
  85			for ($y = 0; $y < $this->height; $y += mt_rand($grid - 2, $grid + 2))
  86			{
  87				$current_colour = $scheme[array_rand($scheme)];
  88				imageline($img, mt_rand(0,4), mt_rand($y - 3, $y), mt_rand($this->width - 5, $this->width), mt_rand($y - 3, $y), $current_colour);
  89			}
  90		}
  91
  92		if ($config['captcha_gd_y_grid'])
  93		{
  94			$grid = (int) $config['captcha_gd_y_grid'];
  95			for ($x = 0; $x < $this->width; $x += mt_rand($grid - 2, $grid + 2))
  96			{
  97				$current_colour = $scheme[array_rand($scheme)];
  98				imagedashedline($img, mt_rand($x -3, $x + 3), mt_rand(0, 4), mt_rand($x -3, $x + 3), mt_rand($this->height - 5, $this->height), $current_colour);
  99			}
 100		}
 101
 102		$xoffset = 5;
 103		for ($i = 0; $i < $code_len; ++$i)
 104		{
 105			$dimm = $bounding_boxes[$i];
 106			$xoffset += ($offset[$i] - $dimm[0]);
 107			$yoffset = mt_rand(-$dimm[1], $this->height - $dimm[3]);
 108
 109			$characters[$i]->drawchar($sizes[$i], $xoffset, $yoffset, $img, $colour->get_resource('background'), $scheme);
 110			$xoffset += $dimm[2];
 111		}
 112		
 113		if ($config['captcha_gd_foreground_noise'])
 114		{
 115			$this->noise_line($img, 0, 0, $this->width, $this->height, $colour->get_resource('background'), $scheme, $bg_colours);
 116		}
 117
 118		// Send image
 119		header('Content-Type: image/png');
 120		header('Cache-control: no-cache, no-store');
 121		imagepng($img);
 122		imagedestroy($img);
 123	}
 124
 125	/**
 126	* Noise line
 127	*/
 128	function noise_line($img, $min_x, $min_y, $max_x, $max_y, $bg, $font, $non_font)
 129	{
 130		imagesetthickness($img, 2);
 131
 132		$x1 = $min_x;
 133		$x2 = $max_x;
 134		$y1 = $min_y;
 135		$y2 = $min_y;
 136
 137		do
 138		{
 139			$line = array_merge(
 140				array_fill(0, mt_rand(30, 60), $non_font[array_rand($non_font)]),
 141				array_fill(0, mt_rand(30, 60), $bg)
 142			);
 143
 144			imagesetstyle($img, $line);
 145			imageline($img, $x1, $y1, $x2, $y2, IMG_COLOR_STYLED);
 146
 147			$y1 += mt_rand(12, 35);
 148			$y2 += mt_rand(12, 35);
 149		}
 150		while ($y1 < $max_y && $y2 < $max_y);
 151
 152		$x1 = $min_x;
 153		$x2 = $min_x;
 154		$y1 = $min_y;
 155		$y2 = $max_y;
 156
 157		do
 158		{
 159			$line = array_merge(
 160				array_fill(0, mt_rand(30, 60), $non_font[array_rand($non_font)]),
 161				array_fill(0, mt_rand(30, 60), $bg)
 162			);
 163
 164			imagesetstyle($img, $line);
 165			imageline($img, $x1, $y1, $x2, $y2, IMG_COLOR_STYLED);
 166
 167			$x1 += mt_rand(20, 35);
 168			$x2 += mt_rand(20, 35);
 169		}
 170		while ($x1 < $max_x && $x2 < $max_x);
 171		imagesetthickness($img, 1);
 172	}
 173
 174	/**
 175	* Return bitmaps
 176	*/
 177	function captcha_bitmaps()
 178	{
 179		return array(
 180			'width'		=> 9,
 181			'height'	=> 15,
 182			'data'		=> array(
 183
 184			'A' => array(
 185				array(0,0,0,0,1,0,0,0,0),
 186				array(0,0,0,1,0,1,0,0,0),
 187				array(0,0,0,1,0,1,0,0,0),
 188				array(0,0,0,1,0,1,0,0,0),
 189				array(0,0,1,0,0,0,1,0,0),
 190				array(0,0,1,0,0,0,1,0,0),
 191				array(0,0,1,0,0,0,1,0,0),
 192				array(0,1,0,0,0,0,0,1,0),
 193				array(0,1,0,0,0,0,0,1,0),
 194				array(0,1,1,1,1,1,1,1,0),
 195				array(0,1,0,0,0,0,0,1,0),
 196				array(1,0,0,0,0,0,0,0,1),
 197				array(1,0,0,0,0,0,0,0,1),
 198				array(1,0,0,0,0,0,0,0,1),
 199				array(1,0,0,0,0,0,0,0,1),
 200			),
 201			'B' => array(
 202				array(1,1,1,1,1,1,1,0,0),
 203				array(1,0,0,0,0,0,0,1,0),
 204				array(1,0,0,0,0,0,0,0,1),
 205				array(1,0,0,0,0,0,0,0,1),
 206				array(1,0,0,0,0,0,0,0,1),
 207				array(1,0,0,0,0,0,0,0,1),
 208				array(1,0,0,0,0,0,0,1,0),
 209				array(1,1,1,1,1,1,1,0,0),
 210				array(1,0,0,0,0,0,0,1,0),
 211				array(1,0,0,0,0,0,0,0,1),
 212				array(1,0,0,0,0,0,0,0,1),
 213				array(1,0,0,0,0,0,0,0,1),
 214				array(1,0,0,0,0,0,0,0,1),
 215				array(1,0,0,0,0,0,0,1,0),
 216				array(1,1,1,1,1,1,1,0,0),
 217			),
 218			'C' => array(
 219				array(0,0,1,1,1,1,1,0,0),
 220				array(0,1,0,0,0,0,0,1,0),
 221				array(1,0,0,0,0,0,0,0,1),
 222				array(1,0,0,0,0,0,0,0,1),
 223				array(1,0,0,0,0,0,0,0,0),
 224				array(1,0,0,0,0,0,0,0,0),
 225				array(1,0,0,0,0,0,0,0,0),
 226				array(1,0,0,0,0,0,0,0,0),
 227				array(1,0,0,0,0,0,0,0,0),
 228				array(1,0,0,0,0,0,0,0,0),
 229				array(1,0,0,0,0,0,0,0,0),
 230				array(1,0,0,0,0,0,0,0,1),
 231				array(1,0,0,0,0,0,0,0,1),
 232				array(0,1,0,0,0,0,0,1,0),
 233				array(0,0,1,1,1,1,1,0,0),
 234			),
 235			'D' => array(
 236				array(1,1,1,1,1,1,1,0,0),
 237				array(1,0,0,0,0,0,0,1,0),
 238				array(1,0,0,0,0,0,0,0,1),
 239				array(1,0,0,0,0,0,0,0,1),
 240				array(1,0,0,0,0,0,0,0,1),
 241				array(1,0,0,0,0,0,0,0,1),
 242				array(1,0,0,0,0,0,0,0,1),
 243				array(1,0,0,0,0,0,0,0,1),
 244				array(1,0,0,0,0,0,0,0,1),
 245				array(1,0,0,0,0,0,0,0,1),
 246				array(1,0,0,0,0,0,0,0,1),
 247				array(1,0,0,0,0,0,0,0,1),
 248				array(1,0,0,0,0,0,0,0,1),
 249				array(1,0,0,0,0,0,0,1,0),
 250				array(1,1,1,1,1,1,1,0,0),
 251			),
 252			'E' => array(
 253				array(1,1,1,1,1,1,1,1,1),
 254				array(1,0,0,0,0,0,0,0,0),
 255				array(1,0,0,0,0,0,0,0,0),
 256				array(1,0,0,0,0,0,0,0,0),
 257				array(1,0,0,0,0,0,0,0,0),
 258				array(1,0,0,0,0,0,0,0,0),
 259				array(1,0,0,0,0,0,0,0,0),
 260				array(1,1,1,1,1,1,1,1,0),
 261				array(1,0,0,0,0,0,0,0,0),
 262				array(1,0,0,0,0,0,0,0,0),
 263				array(1,0,0,0,0,0,0,0,0),
 264				array(1,0,0,0,0,0,0,0,0),
 265				array(1,0,0,0,0,0,0,0,0),
 266				array(1,0,0,0,0,0,0,0,0),
 267				array(1,1,1,1,1,1,1,1,1),
 268			),
 269			'F' => array(
 270				array(1,1,1,1,1,1,1,1,1),
 271				array(1,0,0,0,0,0,0,0,0),
 272				array(1,0,0,0,0,0,0,0,0),
 273				array(1,0,0,0,0,0,0,0,0),
 274				array(1,0,0,0,0,0,0,0,0),
 275				array(1,0,0,0,0,0,0,0,0),
 276				array(1,0,0,0,0,0,0,0,0),
 277				array(1,1,1,1,1,1,1,0,0),
 278				array(1,0,0,0,0,0,0,0,0),
 279				array(1,0,0,0,0,0,0,0,0),
 280				array(1,0,0,0,0,0,0,0,0),
 281				array(1,0,0,0,0,0,0,0,0),
 282				array(1,0,0,0,0,0,0,0,0),
 283				array(1,0,0,0,0,0,0,0,0),
 284				array(1,0,0,0,0,0,0,0,0),
 285			),
 286			'G' => array(
 287				array(0,0,1,1,1,1,1,0,0),
 288				array(0,1,0,0,0,0,0,1,0),
 289				array(1,0,0,0,0,0,0,0,1),
 290				array(1,0,0,0,0,0,0,0,0),
 291				array(1,0,0,0,0,0,0,0,0),
 292				array(1,0,0,0,0,0,0,0,0),
 293				array(1,0,0,0,0,0,0,0,0),
 294				array(1,0,0,0,0,0,0,0,0),
 295				array(1,0,0,0,0,0,1,1,1),
 296				array(1,0,0,0,0,0,0,0,1),
 297				array(1,0,0,0,0,0,0,0,1),
 298				array(1,0,0,0,0,0,0,0,1),
 299				array(1,0,0,0,0,0,0,0,1),
 300				array(0,1,0,0,0,0,0,1,0),
 301				array(0,0,1,1,1,1,1,0,0),
 302			),
 303			'H' => array(
 304				array(1,0,0,0,0,0,0,0,1),
 305				array(1,0,0,0,0,0,0,0,1),
 306				array(1,0,0,0,0,0,0,0,1),
 307				array(1,0,0,0,0,0,0,0,1),
 308				array(1,0,0,0,0,0,0,0,1),
 309				array(1,0,0,0,0,0,0,0,1),
 310				array(1,0,0,0,0,0,0,0,1),
 311				array(1,1,1,1,1,1,1,1,1),
 312				array(1,0,0,0,0,0,0,0,1),
 313				array(1,0,0,0,0,0,0,0,1),
 314				array(1,0,0,0,0,0,0,0,1),
 315				array(1,0,0,0,0,0,0,0,1),
 316				array(1,0,0,0,0,0,0,0,1),
 317				array(1,0,0,0,0,0,0,0,1),
 318				array(1,0,0,0,0,0,0,0,1),
 319			),
 320			'I' => array(
 321				array(1,1,1,1,1,1,1,1,1),
 322				array(0,0,0,0,1,0,0,0,0),
 323				array(0,0,0,0,1,0,0,0,0),
 324				array(0,0,0,0,1,0,0,0,0),
 325				array(0,0,0,0,1,0,0,0,0),
 326				array(0,0,0,0,1,0,0,0,0),
 327				array(0,0,0,0,1,0,0,0,0),
 328				array(0,0,0,0,1,0,0,0,0),
 329				array(0,0,0,0,1,0,0,0,0),
 330				array(0,0,0,0,1,0,0,0,0),
 331				array(0,0,0,0,1,0,0,0,0),
 332				array(0,0,0,0,1,0,0,0,0),
 333				array(0,0,0,0,1,0,0,0,0),
 334				array(0,0,0,0,1,0,0,0,0),
 335				array(1,1,1,1,1,1,1,1,1),
 336			),
 337			'J' => array(
 338				array(1,1,1,1,1,1,1,1,1),
 339				array(0,0,0,0,0,1,0,0,0),
 340				array(0,0,0,0,0,1,0,0,0),
 341				array(0,0,0,0,0,1,0,0,0),
 342				array(0,0,0,0,0,1,0,0,0),
 343				array(0,0,0,0,0,1,0,0,0),
 344				array(0,0,0,0,0,1,0,0,0),
 345				array(0,0,0,0,0,1,0,0,0),
 346				array(0,0,0,0,0,1,0,0,0),
 347				array(0,0,0,0,0,1,0,0,0),
 348				array(0,0,0,0,0,1,0,0,0),
 349				array(1,0,0,0,0,1,0,0,0),
 350				array(1,0,0,0,0,1,0,0,0),
 351				array(0,1,0,0,1,0,0,0,0),
 352				array(0,0,1,1,0,0,0,0,0),
 353			),
 354			'K' => array(    // New 'K', supplied by NeoThermic
 355				array(1,0,0,0,0,0,0,0,1),
 356				array(1,0,0,0,0,0,0,1,0),
 357				array(1,0,0,0,0,0,1,0,0),
 358				array(1,0,0,0,0,1,0,0,0),
 359				array(1,0,0,0,1,0,0,0,0),
 360				array(1,0,0,1,0,0,0,0,0),
 361				array(1,0,1,0,0,0,0,0,0),
 362				array(1,1,0,0,0,0,0,0,0),
 363				array(1,0,1,0,0,0,0,0,0),
 364				array(1,0,0,1,0,0,0,0,0),
 365				array(1,0,0,0,1,0,0,0,0),
 366				array(1,0,0,0,0,1,0,0,0),
 367				array(1,0,0,0,0,0,1,0,0),
 368				array(1,0,0,0,0,0,0,1,0),
 369				array(1,0,0,0,0,0,0,0,1),
 370			),
 371			'L' => array(
 372				array(0,0,0,0,0,0,0,0,0),
 373				array(1,0,0,0,0,0,0,0,0),
 374				array(1,0,0,0,0,0,0,0,0),
 375				array(1,0,0,0,0,0,0,0,0),
 376				array(1,0,0,0,0,0,0,0,0),
 377				array(1,0,0,0,0,0,0,0,0),
 378				array(1,0,0,0,0,0,0,0,0),
 379				array(1,0,0,0,0,0,0,0,0),
 380				array(1,0,0,0,0,0,0,0,0),
 381				array(1,0,0,0,0,0,0,0,0),
 382				array(1,0,0,0,0,0,0,0,0),
 383				array(1,0,0,0,0,0,0,0,0),
 384				array(1,0,0,0,0,0,0,0,0),
 385				array(1,0,0,0,0,0,0,0,0),
 386				array(1,1,1,1,1,1,1,1,1),
 387			),
 388			'M' => array(
 389				array(1,1,0,0,0,0,0,1,1),
 390				array(1,1,0,0,0,0,0,1,1),
 391				array(1,0,1,0,0,0,1,0,1),
 392				array(1,0,1,0,0,0,1,0,1),
 393				array(1,0,1,0,0,0,1,0,1),
 394				array(1,0,0,1,0,1,0,0,1),
 395				array(1,0,0,1,0,1,0,0,1),
 396				array(1,0,0,1,0,1,0,0,1),
 397				array(1,0,0,0,1,0,0,0,1),
 398				array(1,0,0,0,1,0,0,0,1),
 399				array(1,0,0,0,0,0,0,0,1),
 400				array(1,0,0,0,0,0,0,0,1),
 401				array(1,0,0,0,0,0,0,0,1),
 402				array(1,0,0,0,0,0,0,0,1),
 403				array(1,0,0,0,0,0,0,0,1),
 404			),
 405			'N' => array(
 406				array(1,1,0,0,0,0,0,0,1),
 407				array(1,1,0,0,0,0,0,0,1),
 408				array(1,0,1,0,0,0,0,0,1),
 409				array(1,0,1,0,0,0,0,0,1),
 410				array(1,0,0,1,0,0,0,0,1),
 411				array(1,0,0,1,0,0,0,0,1),
 412				array(1,0,0,0,1,0,0,0,1),
 413				array(1,0,0,0,1,0,0,0,1),
 414				array(1,0,0,0,1,0,0,0,1),
 415				array(1,0,0,0,0,1,0,0,1),
 416				array(1,0,0,0,0,1,0,0,1),
 417				array(1,0,0,0,0,0,1,0,1),
 418				array(1,0,0,0,0,0,1,0,1),
 419				array(1,0,0,0,0,0,0,1,1),
 420				array(1,0,0,0,0,0,0,1,1),
 421			),
 422			'O' => array(
 423				array(0,0,1,1,1,1,1,0,0),
 424				array(0,1,0,0,0,0,0,1,0),
 425				array(1,0,0,0,0,0,0,0,1),
 426				array(1,0,0,0,0,0,0,0,1),
 427				array(1,0,0,0,0,0,0,0,1),
 428				array(1,0,0,0,0,0,0,0,1),
 429				array(1,0,0,0,0,0,0,0,1),
 430				array(1,0,0,0,0,0,0,0,1),
 431				array(1,0,0,0,0,0,0,0,1),
 432				array(1,0,0,0,0,0,0,0,1),
 433				array(1,0,0,0,0,0,0,0,1),
 434				array(1,0,0,0,0,0,0,0,1),
 435				array(1,0,0,0,0,0,0,0,1),
 436				array(0,1,0,0,0,0,0,1,0),
 437				array(0,0,1,1,1,1,1,0,0),
 438			),
 439			'P' => array(
 440				array(1,1,1,1,1,1,1,0,0),
 441				array(1,0,0,0,0,0,0,1,0),
 442				array(1,0,0,0,0,0,0,0,1),
 443				array(1,0,0,0,0,0,0,0,1),
 444				array(1,0,0,0,0,0,0,0,1),
 445				array(1,0,0,0,0,0,0,0,1),
 446				array(1,0,0,0,0,0,0,1,0),
 447				array(1,1,1,1,1,1,1,0,0),
 448				array(1,0,0,0,0,0,0,0,0),
 449				array(1,0,0,0,0,0,0,0,0),
 450				array(1,0,0,0,0,0,0,0,0),
 451				array(1,0,0,0,0,0,0,0,0),
 452				array(1,0,0,0,0,0,0,0,0),
 453				array(1,0,0,0,0,0,0,0,0),
 454				array(1,0,0,0,0,0,0,0,0),
 455			),
 456			'Q' => array(
 457				array(0,0,1,1,1,1,1,0,0),
 458				array(0,1,0,0,0,0,0,1,0),
 459				array(1,0,0,0,0,0,0,0,1),
 460				array(1,0,0,0,0,0,0,0,1),
 461				array(1,0,0,0,0,0,0,0,1),
 462				array(1,0,0,0,0,0,0,0,1),
 463				array(1,0,0,0,0,0,0,0,1),
 464				array(1,0,0,0,0,0,0,0,1),
 465				array(1,0,0,0,0,0,0,0,1),
 466				array(1,0,0,0,0,0,0,0,1),
 467				array(1,0,0,0,0,0,0,0,1),
 468				array(1,0,0,0,0,1,0,0,1),
 469				array(1,0,0,0,0,0,1,0,1),
 470				array(0,1,0,0,0,0,0,1,0),
 471				array(0,0,1,1,1,1,1,0,1),
 472			),
 473			'R' => array(
 474				array(1,1,1,1,1,1,1,0,0),
 475				array(1,0,0,0,0,0,0,1,0),
 476				array(1,0,0,0,0,0,0,0,1),
 477				array(1,0,0,0,0,0,0,0,1),
 478				array(1,0,0,0,0,0,0,0,1),
 479				array(1,0,0,0,0,0,0,0,1),
 480				array(1,0,0,0,0,0,0,1,0),
 481				array(1,1,1,1,1,1,1,0,0),
 482				array(1,1,1,0,0,0,0,0,0),
 483				array(1,0,0,1,0,0,0,0,0),
 484				array(1,0,0,0,1,0,0,0,0),
 485				array(1,0,0,0,0,1,0,0,0),
 486				array(1,0,0,0,0,0,1,0,0),
 487				array(1,0,0,0,0,0,0,1,0),
 488				array(1,0,0,0,0,0,0,0,1),
 489			),
 490			'S' => array(
 491				array(0,0,1,1,1,1,1,0,0),
 492				array(0,1,0,0,0,0,0,1,0),
 493				array(1,0,0,0,0,0,0,0,1),
 494				array(1,0,0,0,0,0,0,0,0),
 495				array(1,0,0,0,0,0,0,0,0),
 496				array(1,0,0,0,0,0,0,0,0),
 497				array(0,1,0,0,0,0,0,0,0),
 498				array(0,0,1,1,1,1,1,0,0),
 499				array(0,0,0,0,0,0,0,1,0),
 500				array(0,0,0,0,0,0,0,0,1),
 501				array(0,0,0,0,0,0,0,0,1),
 502				array(0,0,0,0,0,0,0,0,1),
 503				array(1,0,0,0,0,0,0,0,1),
 504				array(0,1,0,0,0,0,0,1,0),
 505				array(0,0,1,1,1,1,1,0,0),
 506			),
 507			'T' => array(
 508				array(1,1,1,1,1,1,1,1,1),
 509				array(0,0,0,0,1,0,0,0,0),
 510				array(0,0,0,0,1,0,0,0,0),
 511				array(0,0,0,0,1,0,0,0,0),
 512				array(0,0,0,0,1,0,0,0,0),
 513				array(0,0,0,0,1,0,0,0,0),
 514				array(0,0,0,0,1,0,0,0,0),
 515				array(0,0,0,0,1,0,0,0,0),
 516				array(0,0,0,0,1,0,0,0,0),
 517				array(0,0,0,0,1,0,0,0,0),
 518				array(0,0,0,0,1,0,0,0,0),
 519				array(0,0,0,0,1,0,0,0,0),
 520				array(0,0,0,0,1,0,0,0,0),
 521				array(0,0,0,0,1,0,0,0,0),
 522				array(0,0,0,0,1,0,0,0,0),
 523			),
 524			'U' => array(
 525				array(1,0,0,0,0,0,0,0,1),
 526				array(1,0,0,0,0,0,0,0,1),
 527				array(1,0,0,0,0,0,0,0,1),
 528				array(1,0,0,0,0,0,0,0,1),
 529				array(1,0,0,0,0,0,0,0,1),
 530				array(1,0,0,0,0,0,0,0,1),
 531				array(1,0,0,0,0,0,0,0,1),
 532				array(1,0,0,0,0,0,0,0,1),
 533				array(1,0,0,0,0,0,0,0,1),
 534				array(1,0,0,0,0,0,0,0,1),
 535				array(1,0,0,0,0,0,0,0,1),
 536				array(1,0,0,0,0,0,0,0,1),
 537				array(1,0,0,0,0,0,0,0,1),
 538				array(0,1,0,0,0,0,0,1,0),
 539				array(0,0,1,1,1,1,1,0,0),
 540			),
 541			'V' => array(
 542				array(1,0,0,0,0,0,0,0,1),
 543				array(1,0,0,0,0,0,0,0,1),
 544				array(1,0,0,0,0,0,0,0,1),
 545				array(0,1,0,0,0,0,0,1,0),
 546				array(0,1,0,0,0,0,0,1,0),
 547				array(0,1,0,0,0,0,0,1,0),
 548				array(0,0,1,0,0,0,1,0,0),
 549				array(0,0,1,0,0,0,1,0,0),
 550				array(0,0,1,0,0,0,1,0,0),
 551				array(0,0,1,0,0,0,1,0,0),
 552				array(0,0,0,1,0,1,0,0,0),
 553				array(0,0,0,1,0,1,0,0,0),
 554				array(0,0,0,1,0,1,0,0,0),
 555				array(0,0,0,0,1,0,0,0,0),
 556				array(0,0,0,0,1,0,0,0,0),
 557			),
 558			'W' => array(    // New 'W', supplied by MHobbit
 559				array(1,0,0,0,0,0,0,0,1),
 560				array(1,0,0,0,0,0,0,0,1),
 561				array(1,0,0,0,0,0,0,0,1),
 562				array(1,0,0,0,0,0,0,0,1),
 563				array(1,0,0,0,0,0,0,0,1),
 564				array(1,0,0,0,1,0,0,0,1),
 565				array(1,0,0,0,1,0,0,0,1),
 566				array(1,0,0,1,0,1,0,0,1),
 567				array(1,0,0,1,0,1,0,0,1),
 568				array(1,0,0,1,0,1,0,0,1),
 569				array(1,0,1,0,0,0,1,0,1),
 570				array(1,0,1,0,0,0,1,0,1),
 571				array(1,0,1,0,0,0,1,0,1),
 572				array(1,1,0,0,0,0,0,1,1),
 573				array(1,1,0,0,0,0,0,1,1),
 574			),
 575			'X' => array(
 576				array(1,0,0,0,0,0,0,0,1),
 577				array(1,0,0,0,0,0,0,0,1),
 578				array(0,1,0,0,0,0,0,1,0),
 579				array(0,1,0,0,0,0,0,1,0),
 580				array(0,0,1,0,0,0,1,0,0),
 581				array(0,0,0,1,0,1,0,0,0),
 582				array(0,0,0,1,0,1,0,0,0),
 583				array(0,0,0,0,1,0,0,0,0),
 584				array(0,0,0,1,0,1,0,0,0),
 585				array(0,0,0,1,0,1,0,0,0),
 586				array(0,0,1,0,0,0,1,0,0),
 587				array(0,1,0,0,0,0,1,0,0),
 588				array(0,1,0,0,0,0,0,1,0),
 589				array(1,0,0,0,0,0,0,0,1),
 590				array(1,0,0,0,0,0,0,0,1),
 591			),
 592			'Y' => array(
 593				array(1,0,0,0,0,0,0,0,1),
 594				array(1,0,0,0,0,0,0,0,1),
 595				array(0,1,0,0,0,0,0,1,0),
 596				array(0,1,0,0,0,0,0,1,0),
 597				array(0,0,1,0,0,0,1,0,0),
 598				array(0,0,1,0,0,0,1,0,0),
 599				array(0,0,0,1,0,1,0,0,0),
 600				array(0,0,0,0,1,0,0,0,0),
 601				array(0,0,0,0,1,0,0,0,0),
 602				array(0,0,0,0,1,0,0,0,0),
 603				array(0,0,0,0,1,0,0,0,0),
 604				array(0,0,0,0,1,0,0,0,0),
 605				array(0,0,0,0,1,0,0,0,0),
 606				array(0,0,0,0,1,0,0,0,0),
 607				array(0,0,0,0,1,0,0,0,0),
 608			),
 609			'Z' => array(    // New 'Z' supplied by Anon
 610				array(1,1,1,1,1,1,1,1,1),
 611				array(1,0,0,0,0,0,0,0,1),
 612				array(0,0,0,0,0,0,0,0,1),
 613				array(0,0,0,0,0,0,0,1,0),
 614				array(0,0,0,0,0,0,1,0,0),
 615				array(0,0,0,0,0,1,0,0,0),
 616				array(0,0,0,0,0,1,0,0,0),
 617				array(0,0,0,0,1,0,0,0,0),
 618				array(0,0,0,1,0,0,0,0,0),
 619				array(0,0,0,1,0,0,0,0,0),
 620				array(0,0,1,0,0,0,0,0,0),
 621				array(0,1,0,0,0,0,0,0,0),
 622				array(1,0,0,0,0,0,0,0,0),
 623				array(1,0,0,0,0,0,0,0,1),
 624				array(1,1,1,1,1,1,1,1,1),
 625			),
 626			'1' => array(
 627				array(0,0,0,1,1,0,0,0,0),
 628				array(0,0,1,0,1,0,0,0,0),
 629				array(0,1,0,0,1,0,0,0,0),
 630				array(0,0,0,0,1,0,0,0,0),
 631				array(0,0,0,0,1,0,0,0,0),
 632				array(0,0,0,0,1,0,0,0,0),
 633				array(0,0,0,0,1,0,0,0,0),
 634				array(0,0,0,0,1,0,0,0,0),
 635				array(0,0,0,0,1,0,0,0,0),
 636				array(0,0,0,0,1,0,0,0,0),
 637				array(0,0,0,0,1,0,0,0,0),
 638				array(0,0,0,0,1,0,0,0,0),
 639				array(0,0,0,0,1,0,0,0,0),
 640				array(0,0,0,0,1,0,0,0,0),
 641				array(0,1,1,1,1,1,1,1,0),
 642			),
 643			'2' => array(    // New '2' supplied by Anon
 644				array(0,0,0,1,1,1,0,0,0),
 645				array(0,0,1,0,0,0,1,0,0),
 646				array(0,1,0,0,0,0,1,1,0),
 647				array(0,0,0,0,0,0,0,0,1),
 648				array(0,0,0,0,0,0,0,0,1),
 649				array(0,0,0,0,0,0,0,1,1),
 650				array(0,0,0,0,0,0,0,1,0),
 651				array(0,0,0,0,0,0,1,0,0),
 652				array(0,0,0,0,0,1,0,0,0),
 653				array(0,0,0,0,1,0,0,0,0),
 654				array(0,0,0,1,0,0,0,0,0),
 655				array(0,0,1,0,0,0,0,0,0),
 656				array(0,1,0,0,0,0,0,0,0),
 657				array(1,1,1,1,1,1,1,1,1),
 658				array(0,0,0,0,0,0,0,0,0),
 659			),
 660			'3' => array(
 661				array(0,0,1,1,1,1,1,0,0),
 662				array(0,1,0,0,0,0,0,1,0),
 663				array(1,0,0,0,0,0,0,0,1),
 664				array(0,0,0,0,0,0,0,0,1),
 665				array(0,0,0,0,0,0,0,0,1),
 666				array(0,0,0,0,0,0,0,0,1),
 667				array(0,0,0,0,0,0,0,1,0),
 668				array(0,0,0,0,0,1,1,0,0),
 669				array(0,0,0,0,0,0,0,1,0),
 670				array(0,0,0,0,0,0,0,0,1),
 671				array(0,0,0,0,0,0,0,0,1),
 672				array(0,0,0,0,0,0,0,0,1),
 673				array(1,0,0,0,0,0,0,0,1),
 674				array(0,1,0,0,0,0,0,1,0),
 675				array(0,0,1,1,1,1,1,0,0),
 676			),
 677			'4' => array(
 678				array(0,0,0,0,0,0,1,1,0),
 679				array(0,0,0,0,0,1,0,1,0),
 680				array(0,0,0,0,1,0,0,1,0),
 681				array(0,0,0,1,0,0,0,1,0),
 682				array(0,0,1,0,0,0,0,1,0),
 683				array(0,1,0,0,0,0,0,1,0),
 684				array(1,0,0,0,0,0,0,1,0),
 685				array(1,0,0,0,0,0,0,1,0),
 686				array(1,1,1,1,1,1,1,1,1),
 687				array(0,0,0,0,0,0,0,1,0),
 688				array(0,0,0,0,0,0,0,1,0),
 689				array(0,0,0,0,0,0,0,1,0),
 690				array(0,0,0,0,0,0,0,1,0),
 691				array(0,0,0,0,0,0,0,1,0),
 692				array(0,0,0,0,0,0,0,1,0),
 693			),
 694			'5' => array(
 695				array(1,1,1,1,1,1,1,1,1),
 696				array(1,0,0,0,0,0,0,0,0),
 697				array(1,0,0,0,0,0,0,0,0),
 698				array(1,0,0,0,0,0,0,0,0),
 699				array(1,0,0,0,0,0,0,0,0),
 700				array(0,1,0,0,0,0,0,0,0),
 701				array(0,0,1,1,1,1,1,0,0),
 702				array(0,0,0,0,0,0,0,1,0),
 703				array(0,0,0,0,0,0,0,0,1),
 704				array(0,0,0,0,0,0,0,0,1),
 705				array(0,0,0,0,0,0,0,0,1),
 706				array(0,0,0,0,0,0,0,0,1),
 707				array(1,0,0,0,0,0,0,0,1),
 708				array(0,1,0,0,0,0,0,1,0),
 709				array(0,0,1,1,1,1,1,0,0),
 710			),
 711			'6' => array(
 712				array(0,0,1,1,1,1,1,0,0),
 713				array(0,1,0,0,0,0,0,1,0),
 714				array(1,0,0,0,0,0,0,0,1),
 715				array(1,0,0,0,0,0,0,0,1),
 716				array(1,0,0,0,0,0,0,0,0),
 717				array(1,0,0,0,0,0,0,0,0),
 718				array(1,0,0,1,1,1,1,0,0),
 719				array(1,0,1,0,0,0,0,1,0),
 720				array(1,1,0,0,0,0,0,0,1),
 721				array(1,0,0,0,0,0,0,0,1),
 722				array(1,0,0,0,0,0,0,0,1),
 723				array(1,0,0,0,0,0,0,0,1),
 724				array(1,0,0,0,0,0,0,0,1),
 725				array(0,1,0,0,0,0,0,1,0),
 726				array(0,0,1,1,1,1,1,0,0),
 727			),
 728			'7' => array(
 729				array(1,1,1,1,1,1,1,1,1),
 730				array(0,0,0,0,0,0,0,0,1),
 731				array(0,0,0,0,0,0,0,1,0),
 732				array(0,0,0,0,0,0,0,1,0),
 733				array(0,0,0,0,0,0,1,0,0),
 734				array(0,0,0,0,0,1,0,0,0),
 735				array(0,0,0,0,0,1,0,0,0),
 736				array(0,0,0,0,1,0,0,0,0),
 737				array(0,0,0,1,0,0,0,0,0),
 738				array(0,0,0,1,0,0,0,0,0),
 739				array(0,0,1,0,0,0,0,0,0),
 740				array(0,1,0,0,0,0,0,0,0),
 741				array(0,1,0,0,0,0,0,0,0),
 742				array(1,0,0,0,0,0,0,0,0),
 743				array(1,0,0,0,0,0,0,0,0),
 744			),
 745			'8' => array(
 746				array(0,0,1,1,1,1,1,0,0),
 747				array(0,1,0,0,0,0,0,1,0),
 748				array(1,0,0,0,0,0,0,0,1),
 749				array(1,0,0,0,0,0,0,0,1),
 750				array(1,0,0,0,0,0,0,0,1),
 751				array(1,0,0,0,0,0,0,0,1),
 752				array(0,1,0,0,0,0,0,1,0),
 753				array(0,0,1,1,1,1,1,0,0),
 754				array(0,1,0,0,0,0,0,1,0),
 755				array(1,0,0,0,0,0,0,0,1),
 756				array(1,0,0,0,0,0,0,0,1),
 757				array(1,0,0,0,0,0,0,0,1),
 758				array(1,0,0,0,0,0,0,0,1),
 759				array(0,1,0,0,0,0,0,1,0),
 760				array(0,0,1,1,1,1,1,0,0),
 761			),
 762			'9' => array(
 763				array(0,0,1,1,1,1,1,0,0),
 764				array(0,1,0,0,0,0,0,1,0),
 765				array(1,0,0,0,0,0,0,0,1),
 766				array(1,0,0,0,0,0,0,0,1),
 767				array(1,0,0,0,0,0,0,0,1),
 768				array(1,0,0,0,0,0,0,0,1),
 769				array(1,0,0,0,0,0,0,1,1),
 770				array(0,1,0,0,0,0,1,0,1),
 771				array(0,0,1,1,1,1,0,0,1),
 772				array(0,0,0,0,0,0,0,0,1),
 773				array(0,0,0,0,0,0,0,0,1),
 774				array(1,0,0,0,0,0,0,0,1),
 775				array(1,0,0,0,0,0,0,0,1),
 776				array(0,1,0,0,0,0,0,1,0),
 777				array(0,0,1,1,1,1,1,0,0),
 778			),
 779			)
 780		);
 781	}
 782}
 783
 784/**
 785* @package VC
 786*/
 787class char_cube3d
 788{
 789	var $bitmap;
 790	var $bitmap_width;
 791	var $bitmap_height;
 792
 793	var $basis_matrix = array(array(1, 0, 0), array(0, 1, 0), array(0, 0, 1));
 794	var $abs_x = array(1, 0);
 795	var $abs_y = array(0, 1);
 796	var $x = 0;
 797	var $y = 1;
 798	var $z = 2;
 799	var $letter = '';
 800
 801	/**
 802	*/
 803	function char_cube3d(&$bitmaps, $letter)
 804	{
 805		$this->bitmap			= $bitmaps['data'][$letter];
 806		$this->bitmap_width		= $bitmaps['width'];
 807		$this->bitmap_height	= $bitmaps['height'];
 808
 809		$this->basis_matrix[0][0] = mt_rand(-600, 600);
 810		$this->basis_matrix[0][1] = mt_rand(-600, 600);
 811		$this->basis_matrix[0][2] = (mt_rand(0, 1) * 2000) - 1000;
 812		$this->basis_matrix[1][0] = mt_rand(-1000, 1000);
 813		$this->basis_matrix[1][1] = mt_rand(-1000, 1000);
 814		$this->basis_matrix[1][2] = mt_rand(-1000, 1000);
 815
 816		$this->normalize($this->basis_matrix[0]);
 817		$this->normalize($this->basis_matrix[1]);
 818		$this->basis_matrix[2] = $this->cross_product($this->basis_matrix[0], $this->basis_matrix[1]);
 819		$this->normalize($this->basis_matrix[2]);
 820
 821		// $this->basis_matrix[1] might not be (probably isn't) orthogonal to $basis_matrix[0]
 822		$this->basis_matrix[1] = $this->cross_product($this->basis_matrix[0], $this->basis_matrix[2]);
 823		$this->normalize($this->basis_matrix[1]);
 824
 825		// Make sure our cube is facing into the canvas (assuming +z == in)
 826		for ($i = 0; $i < 3; ++$i)
 827		{
 828			if ($this->basis_matrix[$i][2] < 0)
 829			{
 830				$this->basis_matrix[$i][0] *= -1;
 831				$this->basis_matrix[$i][1] *= -1;
 832				$this->basis_matrix[$i][2] *= -1;
 833			}
 834		}
 835
 836		// Force our "z" basis vector to be the one with greatest absolute z value
 837		$this->x = 0;
 838		$this->y = 1;
 839		$this->z = 2;
 840
 841		// Swap "y" with "z"
 842		if ($this->basis_matrix[1][2] > $this->basis_matrix[2][2])
 843		{
 844			$this->z = 1;
 845			$this->y = 2;
 846		}
 847
 848		// Swap "x" with "z"
 849		if ($this->basis_matrix[0][2] > $this->basis_matrix[$this->z][2])
 850		{
 851			$this->x = $this->z;
 852			$this->z = 0;
 853		}
 854
 855		// Still need to determine which of $x,$y are which.
 856		// wrong orientation if y's y-component is less than it's x-component
 857		// likewise if x's x-component is less than it's y-component
 858		// if they disagree, go with the one with the greater weight difference.
 859		// rotate if positive
 860		$weight = (abs($this->basis_matrix[$this->x][1]) - abs($this->basis_matrix[$this->x][0])) + (abs($this->basis_matrix[$this->y][0]) - abs($this->basis_matrix[$this->y][1]));
 861
 862		// Swap "x" with "y"
 863		if ($weight > 0)
 864		{
 865			list($this->x, $this->y) = array($this->y, $this->x);
 866		}
 867
 868		$this->abs_x = array($this->basis_matrix[$this->x][0], $this->basis_matrix[$this->x][1]);
 869		$this->abs_y = array($this->basis_matrix[$this->y][0], $this->basis_matrix[$this->y][1]);
 870
 871		if ($this->abs_x[0] < 0)
 872		{
 873			$this->abs_x[0] *= -1;
 874			$this->abs_x[1] *= -1;
 875		}
 876
 877		if ($this->abs_y[1] > 0)
 878		{
 879			$this->abs_y[0] *= -1;
 880			$this->abs_y[1] *= -1;
 881		}
 882
 883		$this->letter = $letter;
 884	}
 885
 886	/**
 887	* Draw a character
 888	*/
 889	function drawchar($scale, $xoff, $yoff, $img, $background, $colours)
 890	{
 891		$width	= $this->bitmap_width;
 892		$height	= $this->bitmap_height;
 893		$bitmap	= $this->bitmap;
 894
 895		$colour1 = $colours[array_rand($colours)];
 896		$colour2 = $colours[array_rand($colours)];
 897
 898		$swapx = ($this->basis_matrix[$this->x][0] > 0);
 899		$swapy = ($this->basis_matrix[$this->y][1] < 0);
 900
 901		for ($y = 0; $y < $height; ++$y)
 902		{
 903			for ($x = 0; $x < $width; ++$x)
 904			{
 905				$xp = ($swapx) ? ($width - $x - 1) : $x;
 906				$yp = ($swapy) ? ($height - $y - 1) : $y;
 907
 908				if ($bitmap[$height - $yp - 1][$xp])
 909				{
 910					$dx = $this->scale($this->abs_x, ($xp - ($swapx ? ($width / 2) : ($width / 2) - 1)) * $scale);
 911					$dy = $this->scale($this->abs_y, ($yp - ($swapy ? ($height / 2) : ($height / 2) - 1)) * $scale);
 912					$xo = $xoff + $dx[0] + $dy[0];
 913					$yo = $yoff + $dx[1] + $dy[1];
 914
 915					$origin = array(0, 0, 0);
 916					$xvec = $this->scale($this->basis_matrix[$this->x], $scale);
 917					$yvec = $this->scale($this->basis_matrix[$this->y], $scale);
 918					$face_corner = $this->sum2($xvec, $yvec);
 919
 920					$zvec = $this->scale($this->basis_matrix[$this->z], $scale);
 921					$x_corner = $this->sum2($xvec, $zvec);
 922					$y_corner = $this->sum2($yvec, $zvec);
 923
 924					imagefilledpolygon($img, $this->gen_poly($xo, $yo, $origin, $xvec, $x_corner,$zvec), 4, $colour1);
 925					imagefilledpolygon($img, $this->gen_poly($xo, $yo, $origin, $yvec, $y_corner,$zvec), 4, $colour2);
 926
 927					$face = $this->gen_poly($xo, $yo, $origin, $xvec, $face_corner, $yvec);
 928
 929					imagefilledpolygon($img, $face, 4, $background);
 930					imagepolygon($img, $face, 4, $colour1);
 931				}
 932			}
 933		}
 934	}
 935
 936	/*
 937	* return a roughly acceptable range of sizes for rendering with this texttype
 938	*/
 939	function range()
 940	{
 941		return array(3, 4);
 942	}
 943
 944	/**
 945	* Vector length
 946	*/
 947	function vectorlen($vector)
 948	{
 949		return sqrt(pow($vector[0], 2) + pow($vector[1], 2) + pow($vector[2], 2));
 950	}
 951
 952	/**
 953	* Normalize
 954	*/
 955	function normalize(&$vector, $length = 1)
 956	{
 957		$length = (( $length < 1) ? 1 : $length);
 958		$length /= $this->vectorlen($vector);
 959		$vector[0] *= $length;
 960		$vector[1] *= $length;
 961		$vector[2] *= $length;
 962	}
 963
 964	/**
 965	*/
 966	function cross_product($vector1, $vector2)
 967	{
 968		$retval = array(0, 0, 0);
 969		$retval[0] =  (($vector1[1] * $vector2[2]) - ($vector1[2] * $vector2[1]));
 970		$retval[1] = -(($vector1[0] * $vector2[2]) - ($vector1[2] * $vector2[0]));
 971		$retval[2] =  (($vector1[0] * $vector2[1]) - ($vector1[1] * $vector2[0]));
 972
 973		return $retval;
 974	}
 975
 976	/**
 977	*/
 978	function sum($vector1, $vector2)
 979	{
 980		return array($vector1[0] + $vector2[0], $vector1[1] + $vector2[1], $vector1[2] + $vector2[2]);
 981	}
 982
 983	/**
 984	*/
 985	function sum2($vector1, $vector2)
 986	{
 987		return array($vector1[0] + $vector2[0], $vector1[1] + $vector2[1]);
 988	}
 989
 990	/**
 991	*/
 992	function scale($vector, $length)
 993	{
 994		if (sizeof($vector) == 2)
 995		{
 996			return array($vector[0] * $length, $vector[1] * $length);
 997		}
 998
 999		return array($vector[0] * $length, $vector[1] * $length, $vector[2] * $length);
1000	}
1001
1002	/**
1003	*/
1004	function gen_poly($xoff, $yoff, &$vec1, &$vec2, &$vec3, &$vec4)
1005	{
1006		$poly = array();
1007		$poly[0] = $xoff + $vec1[0];
1008		$poly[1] = $yoff + $vec1[1];
1009		$poly[2] = $xoff + $vec2[0];
1010		$poly[3] = $yoff + $vec2[1];
1011		$poly[4] = $xoff + $vec3[0];
1012		$poly[5] = $yoff + $vec3[1];
1013		$poly[6] = $xoff + $vec4[0];
1014		$poly[7] = $yoff + $vec4[1];
1015
1016		return $poly;
1017	}
1018
1019	/**
1020	* dimensions
1021	*/
1022	function dimensions($size)
1023	{
1024		$xn = $this->scale($this->basis_matrix[$this->x], -($this->bitmap_width / 2) * $size);
1025		$xp = $this->scale($this->basis_matrix[$this->x], ($this->bitmap_width / 2) * $size);
1026		$yn = $this->scale($this->basis_matrix[$this->y], -($this->bitmap_height / 2) * $size);
1027		$yp = $this->scale($this->basis_matrix[$this->y], ($this->bitmap_height / 2) * $size);
1028
1029		$p = array();
1030		$p[0] = $this->sum2($xn, $yn);
1031		$p[1] = $this->sum2($xp, $yn);
1032		$p[2] = $this->sum2($xp, $yp);
1033		$p[3] = $this->sum2($xn, $yp);
1034
1035		$min_x = $max_x = $p[0][0];
1036		$min_y = $max_y = $p[0][1];
1037
1038		for ($i = 1; $i < 4; ++$i)
1039		{
1040			$min_x = ($min_x > $p[$i][0]) ? $p[$i][0] : $min_x;
1041			$min_y = ($min_y > $p[$i][1]) ? $p[$i][1] : $min_y;
1042			$max_x = ($max_x < $p[$i][0]) ? $p[$i][0] : $max_x;
1043			$max_y = ($max_y < $p[$i][1]) ? $p[$i][1] : $max_y;
1044		}
1045
1046		return array($min_x, $min_y, $max_x, $max_y);
1047	}
1048}
1049
1050/**
1051* @package VC
1052*/
1053class colour_manager
1054{
1055	var $img;
1056	var $mode;
1057	var $colours;
1058	var $named_colours;
1059
1060	/**
1061	* Create the colour manager, link it to the image resource
1062	*/
1063	function colour_manager($img, $background = false, $mode = 'ahsv')
1064	{
1065		$this->img = $img;
1066		$this->mode = $mode;
1067		$this->colours = array();
1068		$this->named_colours = array();
1069
1070		if ($background !== false)
1071		{
1072			$bg = $this->allocate_named('background', $background);
1073			imagefill($this->img, 0, 0, $bg);
1074		}
1075	}
1076
1077	/**
1078	* Lookup a named colour resource
1079	*/
1080	function get_resource($named_colour)
1081	{
1082		if (isset($this->named_colours[$named_colour]))
1083		{
1084			return $this->named_colours[$named_colour];
1085		}
1086
1087		if (isset($this->named_rgb[$named_colour]))
1088		{
1089			return $this->allocate_named($named_colour, $this->named_rgb[$named_colour], 'rgb');
1090		}
1091
1092		return false;
1093	}
1094
1095	/**
1096	* Assign a name to a colour resource
1097	*/
1098	function name_colour($name, $resource)
1099	{
1100		$this->named_colours[$name] = $resource;
1101	}
1102
1103	/**
1104	* names and allocates a colour resource
1105	*/
1106	function allocate_named($name, $colour, $mode = false)
1107	{
1108		$resource = $this->allocate($colour, $mode);
1109
1110		if ($resource !== false)
1111		{
1112			$this->name_colour($name, $resource);
1113		}
1114		return $resource;
1115	}
1116
1117	/**
1118	* allocates a specified colour into the image
1119	*/
1120	function allocate($colour, $mode = false)
1121	{
1122		if ($mode === false)
1123		{
1124			$mode = $this->mode;
1125		}
1126		
1127		if (!is_array($colour))
1128		{
1129			if (isset($this->named_rgb[$colour]))
1130			{
1131				return $this->allocate_named($colour, $this->named_rgb[$colour], 'rgb');
1132			}
1133
1134			if (!is_int($colour))
1135			{
1136				return false;
1137			}
1138
1139			$mode = 'rgb';
1140			$colour = array(255 & ($colour >> 16), 255 & ($colour >>  8), 255 & $colour);
1141		}
1142
1143		if (isset($colour['mode']))
1144		{
1145			$mode = $colour['mode'];
1146			unset($colour['mode']);
1147		}
1148
1149		if (isset($colour['random']))
1150		{
1151			unset($colour['random']);
1152			// everything else is params
1153			return $this->random_colour($colour, $mode);
1154		}
1155
1156		$rgb		= colour_manager::model_convert($colour, $mode, 'rgb');
1157		$store		= ($this->mode == 'rgb') ? $rgb : colour_manager::model_convert($colour, $mode, $this->mode);
1158		$resource	= imagecolorallocate($this->img, $rgb[0], $rgb[1], $rgb[2]);
1159		$this->colours[$resource] = $store;
1160
1161		return $resource;
1162	}
1163
1164	/**
1165	* randomly generates a colour, with optional params
1166	*/
1167	function random_colour($params = array(), $mode = false)
1168	{
1169		if ($mode === false)
1170		{
1171			$mode = $this->mode;
1172		}
1173
1174		switch ($mode)
1175		{
1176			case 'rgb':
1177				// @TODO random rgb generation. do we intend to do this, or is it just too tedious?
1178			break;
1179
1180			case 'ahsv':
1181			case 'hsv':
1182			default:
1183
1184				$default_params = array(
1185					'hue_bias'			=> false,	// degree / 'r'/'g'/'b'/'c'/'m'/'y'   /'o'
1186					'hue_range'			=> false,	// if hue bias, then difference range +/- from bias
1187					'min_saturation'	=> 30,		// 0 - 100
1188					'max_saturation'	=> 80,		// 0 - 100
1189					'min_value'			=> 30,		// 0 - 100
1190					'max_value'			=> 80,		// 0 - 100
1191				);
1192
1193				$alt = ($mode == 'ahsv') ? true : false;
1194				$params = array_merge($default_params, $params);
1195
1196				$min_hue		= 0;
1197				$max_hue		= 359;
1198				$min_saturation	= max(0, $params['min_saturation']);
1199				$max_saturation	= min(100, $params['max_saturation']);
1200				$min_value		= max(0, $params['min_value']);
1201				$max_value		= min(100, $params['max_value']);
1202
1203				if ($params['hue_bias'] !== false)
1204				{
1205					if (is_numeric($params['hue_bias']))
1206					{
1207						$h = intval($params['hue_bias']) % 360;
1208					}
1209					else
1210					{
1211						switch ($params['hue_bias'])
1212						{
1213							case 'o':
1214								$h = $alt ?  60 :  30;
1215							break;
1216
1217							case 'y':
1218								$h = $alt ? 120 :  60;
1219							break;
1220
1221							case 'g':
1222								$h = $alt ? 180 : 120;
1223							break;
1224
1225							case 'c':
1226								$h = $alt ? 210 : 180;
1227							break;
1228
1229							case 'b':
1230								$h = 240;
1231							break;
1232
1233							case 'm':
1234								$h = 300;
1235							break;
1236
1237							case 'r':
1238							default:
1239								$h = 0;
1240							break;
1241						}
1242					}
1243
1244					$min_hue = $h + 360;
1245					$max_hue = $h + 360;
1246
1247					if ($params['hue_range'])
1248					{
1249						$min_hue -= min(180, $params['hue_range']);
1250						$max_hue += min(180, $params['hue_range']);
1251					}
1252				}
1253
1254				$h = mt_rand($min_hue, $max_hue);
1255				$s = mt_rand($min_saturation, $max_saturation);
1256				$v = mt_rand($min_value, $max_value);
1257
1258				return $this->allocate(array($h, $s, $v), $mode);
1259
1260			break;
1261		}
1262	}
1263
1264	/**
1265	*/
1266	function colour_scheme($resource, $include_original = true)
1267	{
1268		$mode = 'hsv';
1269
1270		if (($pre = $this->get_resource($resource)) !== false)
1271		{
1272			$resource = $pre;
1273		}
1274
1275		$colour = colour_manager::model_convert($this->colours[$resource], $this->mode, $mode);
1276		$results = ($include_original) ? array($resource) : array();
1277		$colour2 = $colour3 = $colour4 = $colour;
1278		$colour2[0] += 150;
1279		$colour3[0] += 180;
1280		$colour4[0] += 210;
1281
1282
1283		$results[] = $this->allocate($colour2, $mode);
1284		$results[] = $this->allocate($colour3, $mode);
1285		$results[] = $this->allocate($colour4, $mode);
1286
1287		return $results;
1288	}
1289
1290	/**
1291	*/
1292	function mono_range($resource, $count = 5, $include_original = true)
1293	{
1294		if (is_array($resource))
1295		{
1296			$results = array();
1297			for ($i = 0, $size = sizeof($resource); $i < $size; ++$i)
1298			{
1299				$results = array_merge($results, $this->mono_range($resource[$i], $count, $include_original));
1300			}
1301			return $results;
1302		}
1303
1304		$mode = (in_array($this->mode, array('hsv', 'ahsv'), true) ? $this->mode : 'ahsv');
1305		if (($pre = $this->get_resource($resource)) !== false)
1306		{
1307			$resource = $pre;
1308		}
1309
1310		$colour = colour_manager::model_convert($this->colours[$resource], $this->mode, $mode);
1311
1312		$results = array();
1313		if ($include_original)
1314		{
1315			$results[] = $resource;
1316			$count--;
1317		}
1318
1319		// This is a hard problem. I chicken out and try to maintain readability at the cost of less randomness.
1320		
1321		while ($count > 0)
1322		{
1323			$colour[1] = ($colour[1] + mt_rand(40,60)) % 99;
1324			$colour[2] = ($colour[2] + mt_rand(40,60));
1325			$results[] = $this->allocate($colour, $mode);
1326			$count--;
1327		}
1328		return $results;
1329	}
1330
1331	/**
1332	* Convert from one colour model to another
1333	*/
1334	function model_convert($colour, $from_model, $to_model)
1335	{
1336		if ($from_model == $to_model)
1337		{
1338			return $colour;
1339		}
1340
1341		switch ($to_model)
1342		{
1343			case 'hsv':
1344
1345				switch ($from_model)
1346				{
1347					case 'ahsv':
1348						return colour_manager::ah2h($colour);
1349					break;
1350
1351					case 'rgb':
1352						return colour_manager::rgb2hsv($colour);
1353					break;
1354				}
1355			break;
1356
1357			case 'ahsv':
1358
1359				switch ($from_model)
1360				{
1361					case 'hsv':
1362						return colour_manager::h2ah($colour);
1363					break;
1364
1365					case 'rgb':
1366						return colour_manager::h2ah(colour_manager::rgb2hsv($colour));
1367					break;
1368				}
1369			break;
1370
1371			case 'rgb':
1372				switch ($from_model)
1373				{
1374					case 'hsv':
1375						return colour_manager::hsv2rgb($colour);
1376					break;
1377
1378					case 'ahsv':
1379						return colour_manager::hsv2rgb(colour_manager::ah2h($colour));
1380					break;
1381				}
1382			break;
1383		}
1384		return false;
1385	}
1386
1387	/**
1388	* Slightly altered from wikipedia's algorithm
1389	*/
1390	function hsv2rgb($hsv)
1391	{
1392		colour_manager::normalize_hue($hsv[0]);
1393
1394		$h = $hsv[0];
1395		$s = min(1, max(0, $hsv[1] / 100));
1396		$v = min(1, max(0, $hsv[2] / 100));
1397
1398		// calculate hue sector
1399		$hi = floor($hsv[0] / 60);
1400
1401		// calculate opposite colour
1402		$p = $v * (1 - $s);
1403
1404		// calculate distance between hex vertices
1405		$f = ($h / 60) - $hi;
1406
1407		// coming in or going out?
1408		if (!($hi & 1))
1409		{
1410			$f = 1 - $f;
1411		}
1412
1413		// calculate adjacent colour
1414		$q = $v * (1 - ($f * $s));
1415
1416		switch ($hi)
1417		{
1418			case 0:
1419				$rgb = array($v, $q, $p);
1420			break;
1421
1422			case 1:
1423				$rgb = array($q, $v, $p);
1424			break;
1425
1426			case 2:
1427				$rgb = array($p, $v, $q);
1428			break;
1429
1430			case 3:
1431				$rgb = array($p, $q, $v);
1432			break;
1433
1434			case 4:
1435				$rgb = array($q, $p, $v);
1436			break;
1437
1438			case 5:
1439				$rgb = array($v, $p, $q);
1440			break;
1441
1442			default:
1443				return array(0, 0, 0);
1444			break;
1445		}
1446
1447		return array(255 * $rgb[0], 255 * $rgb[1], 255 * $rgb[2]);
1448	}
1449
1450	/**
1451	* (more than) Slightly altered from wikipedia's algorithm
1452	*/
1453	function rgb2hsv($rgb)
1454	{
1455		$r = min(255, max(0, $rgb[0]));
1456		$g = min(255, max(0, $rgb[1]));
1457		$b = min(255, max(0, $rgb[2]));
1458		$max = max($r, $g, $b);
1459		$min = min($r, $g, $b);
1460
1461		$v = $max / 255;
1462		$s = (!$max) ? 0 : 1 - ($min / $max);
1463
1464		// if max - min is 0, we want hue to be 0 anyway.
1465		$h = $max - $min;
1466
1467		if ($h)
1468		{
1469			switch ($max)
1470			{
1471				case $g:
1472					$h = 120 + (60 * ($b - $r) / $h);
1473				break;
1474
1475				case $b:
1476					$h = 240 + (60 * ($r - $g) / $h);
1477				break;
1478
1479				case $r:
1480					$h = 360 + (60 * ($g - $b) / $h);
1481				break;
1482			}
1483		}
1484		colour_manager::normalize_hue($h);
1485
1486		return array($h, $s * 100, $v * 100);
1487	}
1488
1489	/**
1490	*/
1491	function normalize_hue(&$hue)
1492	{
1493		$hue %= 360;
1494
1495		if ($hue < 0)
1496		{
1497			$hue += 360;
1498		}
1499	}
1500
1501	/**
1502	* Alternate hue to hue
1503	*/
1504	function ah2h($ahue)
1505	{
1506		if (is_array($ahue))
1507		{
1508			$ahue[0] = colour_manager::ah2h($ahue[0]);
1509			return $ahue;
1510		}
1511		colour_manager::normalize_hue($ahue);
1512
1513		// blue through red is already ok
1514		if ($ahue >= 240)
1515		{
1516			return $ahue;
1517		}
1518
1519		// ahue green is at 180
1520		if ($ahue >= 180)
1521		{
1522			// return (240 - (2 * (240 - $ahue)));
1523			return (2 * $ahue) - 240; // equivalent
1524		}
1525
1526		// ahue yellow is at 120   (RYB rather than RGB)
1527		if ($ahue >= 120)
1528		{
1529			return $ahue - 60;
1530		}
1531
1532		return $ahue / 2;
1533	}
1534
1535	/**
1536	* hue to Alternate hue
1537	*/
1538	function h2ah($hue)
1539	{
1540		if (is_array($hue))
1541		{
1542			$hue[0] = colour_manager::h2ah($hue[0]);
1543			return $hue;
1544		}
1545		colour_manager::normalize_hue($hue);
1546
1547		// blue through red is already ok
1548		if ($hue >= 240)
1549		{
1550			return $hue;
1551		}
1552		else if ($hue <= 60)
1553		{
1554			return $hue * 2;
1555		}
1556		else if ($hue <= 120)
1557		{
1558			return $hue + 60;
1559		}
1560		else
1561		{
1562			return ($hue + 240) / 2;
1563		}
1564	}
1565}
1566
1567?>