diff options
Diffstat (limited to 'phpBB/phpbb/captcha/colour_manager.php')
| -rw-r--r-- | phpBB/phpbb/captcha/colour_manager.php | 527 | 
1 files changed, 527 insertions, 0 deletions
diff --git a/phpBB/phpbb/captcha/colour_manager.php b/phpBB/phpbb/captcha/colour_manager.php new file mode 100644 index 0000000000..6ca3c3fd2c --- /dev/null +++ b/phpBB/phpbb/captcha/colour_manager.php @@ -0,0 +1,527 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\captcha; + +class colour_manager +{ +	var $img; +	var $mode; +	var $colours; +	var $named_colours; + +	/** +	* Create the colour manager, link it to the image resource +	*/ +	function __construct($img, $background = false, $mode = 'ahsv') +	{ +		$this->img = $img; +		$this->mode = $mode; +		$this->colours = array(); +		$this->named_colours = array(); + +		if ($background !== false) +		{ +			$bg = $this->allocate_named('background', $background); +			imagefill($this->img, 0, 0, $bg); +		} +	} + +	/** +	* Lookup a named colour resource +	*/ +	function get_resource($named_colour) +	{ +		if (isset($this->named_colours[$named_colour])) +		{ +			return $this->named_colours[$named_colour]; +		} + +		if (isset($this->named_rgb[$named_colour])) +		{ +			return $this->allocate_named($named_colour, $this->named_rgb[$named_colour], 'rgb'); +		} + +		return false; +	} + +	/** +	* Assign a name to a colour resource +	*/ +	function name_colour($name, $resource) +	{ +		$this->named_colours[$name] = $resource; +	} + +	/** +	* names and allocates a colour resource +	*/ +	function allocate_named($name, $colour, $mode = false) +	{ +		$resource = $this->allocate($colour, $mode); + +		if ($resource !== false) +		{ +			$this->name_colour($name, $resource); +		} +		return $resource; +	} + +	/** +	* allocates a specified colour into the image +	*/ +	function allocate($colour, $mode = false) +	{ +		if ($mode === false) +		{ +			$mode = $this->mode; +		} + +		if (!is_array($colour)) +		{ +			if (isset($this->named_rgb[$colour])) +			{ +				return $this->allocate_named($colour, $this->named_rgb[$colour], 'rgb'); +			} + +			if (!is_int($colour)) +			{ +				return false; +			} + +			$mode = 'rgb'; +			$colour = array(255 & ($colour >> 16), 255 & ($colour >>  8), 255 & $colour); +		} + +		if (isset($colour['mode'])) +		{ +			$mode = $colour['mode']; +			unset($colour['mode']); +		} + +		if (isset($colour['random'])) +		{ +			unset($colour['random']); +			// everything else is params +			return $this->random_colour($colour, $mode); +		} + +		$rgb		= $this->model_convert($colour, $mode, 'rgb'); +		$store		= ($this->mode == 'rgb') ? $rgb : $this->model_convert($colour, $mode, $this->mode); +		$resource	= imagecolorallocate($this->img, $rgb[0], $rgb[1], $rgb[2]); +		$this->colours[$resource] = $store; + +		return $resource; +	} + +	/** +	* randomly generates a colour, with optional params +	*/ +	function random_colour($params = array(), $mode = false) +	{ +		if ($mode === false) +		{ +			$mode = $this->mode; +		} + +		switch ($mode) +		{ +			case 'rgb': +				// @TODO random rgb generation. do we intend to do this, or is it just too tedious? +				break; + +			case 'ahsv': +			case 'hsv': +			default: + +				$default_params = array( +					'hue_bias'			=> false,	// degree / 'r'/'g'/'b'/'c'/'m'/'y'   /'o' +					'hue_range'			=> false,	// if hue bias, then difference range +/- from bias +					'min_saturation'	=> 30,		// 0 - 100 +					'max_saturation'	=> 80,		// 0 - 100 +					'min_value'			=> 30,		// 0 - 100 +					'max_value'			=> 80,		// 0 - 100 +				); + +				$alt = ($mode == 'ahsv') ? true : false; +				$params = array_merge($default_params, $params); + +				$min_hue		= 0; +				$max_hue		= 359; +				$min_saturation	= max(0, $params['min_saturation']); +				$max_saturation	= min(100, $params['max_saturation']); +				$min_value		= max(0, $params['min_value']); +				$max_value		= min(100, $params['max_value']); + +				if ($params['hue_bias'] !== false) +				{ +					if (is_numeric($params['hue_bias'])) +					{ +						$h = intval($params['hue_bias']) % 360; +					} +					else +					{ +						switch ($params['hue_bias']) +						{ +							case 'o': +								$h = $alt ?  60 :  30; +								break; + +							case 'y': +								$h = $alt ? 120 :  60; +								break; + +							case 'g': +								$h = $alt ? 180 : 120; +								break; + +							case 'c': +								$h = $alt ? 210 : 180; +								break; + +							case 'b': +								$h = 240; +								break; + +							case 'm': +								$h = 300; +								break; + +							case 'r': +							default: +								$h = 0; +								break; +						} +					} + +					$min_hue = $h + 360; +					$max_hue = $h + 360; + +					if ($params['hue_range']) +					{ +						$min_hue -= min(180, $params['hue_range']); +						$max_hue += min(180, $params['hue_range']); +					} +				} + +				$h = mt_rand($min_hue, $max_hue); +				$s = mt_rand($min_saturation, $max_saturation); +				$v = mt_rand($min_value, $max_value); + +				return $this->allocate(array($h, $s, $v), $mode); + +				break; +		} +	} + +	/** +	*/ +	function colour_scheme($resource, $include_original = true) +	{ +		$mode = 'hsv'; + +		if (($pre = $this->get_resource($resource)) !== false) +		{ +			$resource = $pre; +		} + +		$colour = $this->model_convert($this->colours[$resource], $this->mode, $mode); +		$results = ($include_original) ? array($resource) : array(); +		$colour2 = $colour3 = $colour4 = $colour; +		$colour2[0] += 150; +		$colour3[0] += 180; +		$colour4[0] += 210; + +		$results[] = $this->allocate($colour2, $mode); +		$results[] = $this->allocate($colour3, $mode); +		$results[] = $this->allocate($colour4, $mode); + +		return $results; +	} + +	/** +	*/ +	function mono_range($resource, $count = 5, $include_original = true) +	{ +		if (is_array($resource)) +		{ +			$results = array(); +			for ($i = 0, $size = sizeof($resource); $i < $size; ++$i) +			{ +				$results = array_merge($results, $this->mono_range($resource[$i], $count, $include_original)); +			} +			return $results; +		} + +		$mode = (in_array($this->mode, array('hsv', 'ahsv'), true) ? $this->mode : 'ahsv'); +		if (($pre = $this->get_resource($resource)) !== false) +		{ +			$resource = $pre; +		} + +		$colour = $this->model_convert($this->colours[$resource], $this->mode, $mode); + +		$results = array(); +		if ($include_original) +		{ +			$results[] = $resource; +			$count--; +		} + +		// This is a hard problem. I chicken out and try to maintain readability at the cost of less randomness. + +		while ($count > 0) +		{ +			$colour[1] = ($colour[1] + mt_rand(40,60)) % 99; +			$colour[2] = ($colour[2] + mt_rand(40,60)); +			$results[] = $this->allocate($colour, $mode); +			$count--; +		} +		return $results; +	} + +	/** +	* Convert from one colour model to another +	*/ +	function model_convert($colour, $from_model, $to_model) +	{ +		if ($from_model == $to_model) +		{ +			return $colour; +		} + +		switch ($to_model) +		{ +			case 'hsv': + +				switch ($from_model) +				{ +					case 'ahsv': +						return $this->ah2h($colour); +						break; + +					case 'rgb': +						return $this->rgb2hsv($colour); +						break; +				} +				break; + +			case 'ahsv': + +				switch ($from_model) +				{ +					case 'hsv': +						return $this->h2ah($colour); +						break; + +					case 'rgb': +						return $this->h2ah($this->rgb2hsv($colour)); +						break; +				} +				break; + +			case 'rgb': +				switch ($from_model) +				{ +					case 'hsv': +						return $this->hsv2rgb($colour); +						break; + +					case 'ahsv': +						return $this->hsv2rgb($this->ah2h($colour)); +						break; +				} +				break; +		} +		return false; +	} + +	/** +	* Slightly altered from wikipedia's algorithm +	*/ +	function hsv2rgb($hsv) +	{ +		$this->normalize_hue($hsv[0]); + +		$h = $hsv[0]; +		$s = min(1, max(0, $hsv[1] / 100)); +		$v = min(1, max(0, $hsv[2] / 100)); + +		// calculate hue sector +		$hi = floor($hsv[0] / 60); + +		// calculate opposite colour +		$p = $v * (1 - $s); + +		// calculate distance between hex vertices +		$f = ($h / 60) - $hi; + +		// coming in or going out? +		if (!($hi & 1)) +		{ +			$f = 1 - $f; +		} + +		// calculate adjacent colour +		$q = $v * (1 - ($f * $s)); + +		switch ($hi) +		{ +			case 0: +				$rgb = array($v, $q, $p); +				break; + +			case 1: +				$rgb = array($q, $v, $p); +				break; + +			case 2: +				$rgb = array($p, $v, $q); +				break; + +			case 3: +				$rgb = array($p, $q, $v); +				break; + +			case 4: +				$rgb = array($q, $p, $v); +				break; + +			case 5: +				$rgb = array($v, $p, $q); +				break; + +			default: +				return array(0, 0, 0); +				break; +		} + +		return array(255 * $rgb[0], 255 * $rgb[1], 255 * $rgb[2]); +	} + +	/** +	* (more than) Slightly altered from wikipedia's algorithm +	*/ +	function rgb2hsv($rgb) +	{ +		$r = min(255, max(0, $rgb[0])); +		$g = min(255, max(0, $rgb[1])); +		$b = min(255, max(0, $rgb[2])); +		$max = max($r, $g, $b); +		$min = min($r, $g, $b); + +		$v = $max / 255; +		$s = (!$max) ? 0 : 1 - ($min / $max); + +		// if max - min is 0, we want hue to be 0 anyway. +		$h = $max - $min; + +		if ($h) +		{ +			switch ($max) +			{ +				case $g: +					$h = 120 + (60 * ($b - $r) / $h); +					break; + +				case $b: +					$h = 240 + (60 * ($r - $g) / $h); +					break; + +				case $r: +					$h = 360 + (60 * ($g - $b) / $h); +					break; +			} +		} +		$this->normalize_hue($h); + +		return array($h, $s * 100, $v * 100); +	} + +	/** +	*/ +	function normalize_hue(&$hue) +	{ +		$hue %= 360; + +		if ($hue < 0) +		{ +			$hue += 360; +		} +	} + +	/** +	* Alternate hue to hue +	*/ +	function ah2h($ahue) +	{ +		if (is_array($ahue)) +		{ +			$ahue[0] = $this->ah2h($ahue[0]); +			return $ahue; +		} +		$this->normalize_hue($ahue); + +		// blue through red is already ok +		if ($ahue >= 240) +		{ +			return $ahue; +		} + +		// ahue green is at 180 +		if ($ahue >= 180) +		{ +			// return (240 - (2 * (240 - $ahue))); +			return (2 * $ahue) - 240; // equivalent +		} + +		// ahue yellow is at 120   (RYB rather than RGB) +		if ($ahue >= 120) +		{ +			return $ahue - 60; +		} + +		return $ahue / 2; +	} + +	/** +	* hue to Alternate hue +	*/ +	function h2ah($hue) +	{ +		if (is_array($hue)) +		{ +			$hue[0] = $this->h2ah($hue[0]); +			return $hue; +		} +		$this->normalize_hue($hue); + +		// blue through red is already ok +		if ($hue >= 240) +		{ +			return $hue; +		} +		else if ($hue <= 60) +		{ +			return $hue * 2; +		} +		else if ($hue <= 120) +		{ +			return $hue + 60; +		} +		else +		{ +			return ($hue + 240) / 2; +		} +	} +}  | 
