diff options
Diffstat (limited to 'phpBB/includes/captcha/captcha_gd.php')
| -rw-r--r-- | phpBB/includes/captcha/captcha_gd.php | 1433 | 
1 files changed, 1088 insertions, 345 deletions
diff --git a/phpBB/includes/captcha/captcha_gd.php b/phpBB/includes/captcha/captcha_gd.php index ac5ffcbb20..f6eb5c9c50 100644 --- a/phpBB/includes/captcha/captcha_gd.php +++ b/phpBB/includes/captcha/captcha_gd.php @@ -94,7 +94,7 @@ class captcha  			{  				if ($map & (1 << $x))  				{ -					$char = hexdec(substr($seed, ($y * 4) + $x, 1)); +					$char = hexdec(substr($seed, ($y << 2) + $x, 1));  					if (!($char >> 2))  					{  						switch ($char & 3) @@ -124,7 +124,7 @@ class captcha  		$cells = array();  		for ($i = 0; $i < 6; ++$i)  		{ -			$cells = hexdec(substr($seed, 20 + ($i * 2), 2)); +			$cells = hexdec(substr($seed, 20 + ($i << 1), 2));  			$x1 = $cells & 3;  			$cells = $cells >> 2;  			$y1 = $cells & 3; @@ -176,27 +176,52 @@ class captcha  		// Generate image  		$img_x = 800;  		$img_y = 250; -		$img = imagecreate($img_x, $img_y); +		$img = imagecreatetruecolor($img_x, $img_y);  		// Generate colors -		$background = imagecolorallocate($img, mt_rand(155, 255), mt_rand(155, 255), mt_rand(155, 255)); -		imagefill($img, 0, 0, $background); +		$c = new color_manager($img, array( +			'random'			=> true, +			'min_saturation'	=> 70, +			'min_value'			=> 65, +		)); +		 +		$primaries = $c->color_scheme('background', 'tetradic', false); +		 +		$noise = array_shift($primaries); +		$noise = $c->mono_range($noise, 'value', 5, false); +		$primaries = $c->mono_range($primaries, 'value', 5, false); -		$random = array(); -		$fontcolors = array(); -		for ($i = 0; $i < 15; $i++) +		// Generate code characters +		$characters = array(); +		$sizes = array(); +		$bounding_boxes = array(); +		$width_avail = $img_x; +		$code_num = sizeof($code); +		$char_class = $this->captcha_char('char_ttf'); +		for ( $i = 0; $i < $code_num; ++$i )  		{ -			$random[$i] = imagecolorallocate($img, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255)); -			$fontcolors[$i] = imagecolorallocate($img, mt_rand(0, 120), mt_rand(0, 120), mt_rand(0, 120)); +			$characters[$i] = new $char_class($code[$i]); +			list($min, $max) = $characters[$i]->range(); +			$sizes[$i] = mt_rand($min, $max / 2); +			$box = $characters[$i]->dimensions($sizes[$i]); +			$width_avail -= ($box[2] - $box[0]); +			$bounding_boxes[$i] = $box;  		} -		// Generate code characters -		$code_num = sizeof($code); +		// Redistribute leftover x-space +		$offset = array(); +		for ( $i = 0; $i < $code_num; ++$i ) +		{ +			$denom = ($code_num - $i); +			$denom = max(1.5, $denom); +			$offset[$i] = mt_rand(0, (1.5 * $width_avail) / $denom); +			$width_avail -= $offset[$i]; +		}  		// Add some line noise  		if ($config['policy_shape_noise_line'])  		{ -			$this->noise_line($img, 0, 0, $img_x, $img_y, $background, $fontcolors, $random); +			$this->noise_line($img, 0, 0, $img_x, $img_y, $c->r('background'), $primaries, $noise);  		}  		$real = mt_rand(0, 3); @@ -206,23 +231,22 @@ class captcha  			$patterns[$i & 3] .= str_pad(dechex(mt_rand(0, 65535)), 4, '0', STR_PAD_LEFT);  		} -		$char_class = $this->captcha_char('char_ttf');  		for ($i = 0; $i < 4; ++$i)  		{ -			if ($i) +			/*if ($i)  			{  				$y = 5 + ($i * 60);  				imageline($img, 550, $y, 650, $y, $fontcolors[0]); -			} -			$this->draw_pattern($patterns[$i], $img, 525, 10 + ($i * 60), 575, ($i + 1) * 60, $fontcolors); +			}*/ +			$this->draw_pattern($patterns[$i], $img, 525, 10 + ($i * 60), 575, ($i + 1) * 60, $primaries);  			if ($i == $real)  			{ -				$this->draw_pattern($patterns[$i], $img, 25, 25, 225, 225, $fontcolors, 3); +				$this->draw_pattern($patterns[$i], $img, 25, 25, 225, 225, $primaries, 3);  				for ($j = 0; $j < $code_num; ++$j)  				{  					$character = new $char_class($code[$j]); -					$character->drawchar(25, 600 + ($j * 25), 35 + ($i * 60), $img, $background, $fontcolors); +					$character->drawchar(25, 600 + ($j * 25), 35 + ($i * 60), $img, $c->r('background'), $primaries);  				}  			}  			else @@ -231,20 +255,29 @@ class captcha  				for ($j = strlen($word) - 1; $j >= 0; --$j)  				{  					$character = new $char_class(substr($word, $j, 1)); -					$character->drawchar(25, 600 + ($j * 25), 35 + ($i * 60), $img, $background, $fontcolors); +					$character->drawchar(25, 600 + ($j * 25), 35 + ($i * 60), $img, $c->r('background'), $primaries);  				}  			}  		} -		imagestring($img, 6, 250,  50, $user->lang['CAPTCHA_LINE_1'], $fontcolors[0]); -		imagestring($img, 6, 250, 100, $user->lang['CAPTCHA_LINE_2'], $fontcolors[0]); -		imagestring($img, 6, 250, 150, $user->lang['CAPTCHA_LINE_3'], $fontcolors[0]); -		imagestring($img, 6, 250, 200, $user->lang['CAPTCHA_LINE_4'], $fontcolors[0]); + +		$count = sizeof($user->lang['CAPTCHA']['shape']); +		$line_height = $img_y / ($count + 1); +		for ($i = 0; $i < $count; ++$i) +		{ +			$text = $user->lang['CAPTCHA']['shape'][$i]; +			$line_width = strlen($text) * 4.5; //  ( / 2, * 9 ) +			imagestring($img, 6, ($img_x / 2) - $line_width - 1, $line_height * ($i + 1) - 1, $text, $c->r('black')); +			imagestring($img, 6, ($img_x / 2) - $line_width - 1, $line_height * ($i + 1) + 1, $text, $c->r('black')); +			imagestring($img, 6, ($img_x / 2) - $line_width + 1, $line_height * ($i + 1) + 1, $text, $c->r('black')); +			imagestring($img, 6, ($img_x / 2) - $line_width + 1, $line_height * ($i + 1) - 1, $text, $c->r('black')); +			imagestring($img, 6, ($img_x / 2) - $line_width, $line_height * ($i + 1), $text, $c->r('white')); +		}  		// Add some pixel noise  		if ($config['policy_shape_noise_pixel'])  		{ -			$this->noise_pixel($img, 0, 0, $img_x, $img_y, $background, $fontcolors, $random, $config['policy_shape_noise_pixel']); +			$this->noise_pixel($img, 0, 0, $img_x, $img_y, $c->r('background'), $primaries, $noise, $config['policy_shape_noise_pixel']);  		}  		// Send image @@ -262,23 +295,23 @@ class captcha  		$fonts = captcha_load_ttf_fonts();  		// Generate basic colors -		$background = imagecolorallocate($img, mt_rand(155, 255), mt_rand(155, 255), mt_rand(155, 255)); -		imagefill($img, 0, 0, $background); -		$black = imagecolorallocate($img, 0, 0, 0); -		$random = array(); -		$fontcolors = array(); -		for ($i = 0; $i < 15; ++$i) -		{ -			$random[$i] = imagecolorallocate($img, mt_rand(120, 255), mt_rand(120, 255), mt_rand(120, 255)); -		} -		$fontcolors[0] = imagecolorallocate($img, mt_rand(0, 120), mt_rand(0, 120), mt_rand(0, 120)); - +		$c = new color_manager($img, 'white'); +		$c->allocate_named('primary', array( +			'random'			=> true, +			'min_saturation'	=> 50, +			'min_value'			=> 75, +		)); +		$bg_colors		= $c->color_scheme('primary', 'triadic', false); +		$text_colors	= $c->mono_range('primary', 'saturation', 6); +		$bg_colors		= $c->mono_range($bg_colors, 'saturation', 6); +		  		// Specificy image portion dimensions.  		$count = sizeof($code);  		$cellsize = $img_x / $count;  		$y_range = min($cellsize, $img_y);  		$y_max = $img_y - $y_range;  		$y_off = array(); // consecutive vertical offset of characters +		$color = array(); // color of characters  		$y_off[0] = mt_rand(0, $y_max);  		for ($i = 1; $i < $count; ++$i)  		{ @@ -293,108 +326,33 @@ class captcha  				$y_off[$i] = $y_off[$i - 1] * ((100 + $diff) / 100);  			}  		} +		 +		$range = 0.075; - -		$range = 0.1; - -		$chars = array_merge(range('A', 'Z'), range('0', '9')); +		$chars = array_merge(range('A', 'Z'), range('1', '9'));  		// draw some characters. if they're within the vector spec of the code character, color them differently -		for ($i = 0; $i < 5000; ++$i) +		for ($i = 0; $i < 8000; ++$i)  		{  			$degree = mt_rand(-30, 30);  			$x = mt_rand(0, $img_x - 1);  			$y = mt_rand(0, $img_y); -			$c = $chars[array_rand($chars)]; +			$text = $chars[array_rand($chars)];  			$char = $x / $cellsize;  			$meta_x = ((($x % $cellsize) / $cellsize) * 1.5) - 0.25;  			$meta_y = (($img_y - $y) - $y_off[$char]) / $y_range;  			$font = $fonts[array_rand($fonts)]; +			 +			$distance = vector_distance($map[$code[$char]], $meta_x, $meta_y, $range); -			// distance check -			$distance = 2; -			$show = false; -			foreach ($map[$code[$char]] AS $vector) -			{ -				switch ($vector[0]) -				{ -					case 'line': - -						$c_theta = cos($vector[5]); -						$s_theta = sin($vector[5]); -						$bx = $meta_x - $vector[1];  -						$by = $meta_y - $vector[2];  -						$r = ($by * $c_theta) - ($bx * $s_theta); -						if ($r < $range && $r > -$range) -						{ -							if (abs($c_theta) > abs($s_theta)) -							{ -								$s = (($bx + ($s_theta * $r)) / $c_theta);								 -							} -							else -							{ -								$s = (($by + ($c_theta * $r)) / $s_theta); -							} -							if ($s > -$range) -							{ -								if ($s < 0) -								{ -									$distance = min($distance, sqrt(pow($s, 2) + pow($r, 2))); -								} -								else if ($s < $vector[6]) -								{ -									$distance = min($distance, $r); -								} -								else if ($s < $vector[6] + $range) -								{ -									$distance = min($distance, sqrt(pow($s - $vector[6], 2) + pow($r, 2))); -								} -							} -						} - -					break; - -					case 'arc': - -						$dx = $meta_x - $vector[1]; -						$dy = -($meta_y - $vector[2]); // because our arcs are upside-down -						if (abs($dx) > abs($dy)) -						{ -							$phi = rad2deg(atan(($dy * $vector[3]) / ($dx * $vector[4]))); -							$phi += ($dx < 0) ? 180 : 360; -							$phi %= 360; -						} -						else -						{ -							$phi = 90 - rad2deg(atan(($dx * $vector[4]) / ($dy * $vector[3]))); -							$phi += ($dy < 0) ? 180 : 360; -							$phi %= 360; -						} - -						$internal = $vector[6] > $vector[5]; // external wraps over the 360 point -						$low = $phi >= $vector[5]; // phi is above our low range -						$high = $phi <= $vector[6]; // phi is below our high range. -						if ($internal ? ($low && $high) : ($low || $high)) // if it wraps, it can only be one or the other -						{ -							$radphi = deg2rad($phi); -							$px = cos($radphi) * 0.5 * $vector[3]; -							$py = sin($radphi) * 0.5 * $vector[4]; -							$distance = min($distance, sqrt(pow($px - $dx, 2) + pow($py - $dy, 2))); -						} - -					break; -				} -			} - -			if ($distance <= $range) -			{ -				imagettftext($img, 10, $degree, $x, $y, $black, $font, $c); -			} -			else -			{ -				imagettftext($img, 10, $degree, $x, $y, $random[$char], $font, $c); -			} - +			$switch = !(rand() % 100); +			 +			imagettftext($img, 10, $degree, $x, $y, +				(($distance <= $range) xor $switch) ? +					$c->r_rand($text_colors) : +					$c->r_rand($bg_colors), +				$font, $text); +			  		}  		// Send image @@ -406,39 +364,44 @@ class captcha  		// Generate image  		$img_x = 800;  		$img_y = 250; -		$img = imagecreate($img_x, $img_y); -		$stencil = imagecreate($img_x, $img_y); +		$img		= imagecreatetruecolor($img_x, $img_y); +		$stencil	= imagecreatetruecolor($img_x, $img_y);  		$map = captcha_vectors();  		$fonts = captcha_load_ttf_fonts();  		// Generate colors -		$white1 = imagecolorallocate($img, 255, 255, 255); -		$black2 = imagecolorallocate($stencil, 0, 0, 0); -		$black1 = imagecolorallocate($img, 0, 0, 0); -		$white2 = imagecolorallocate($stencil, 255, 255, 255); -		$channel = mt_rand(0,2); -		$c1 = (!($channel % 3)) ? 255 : 0; -		$c2 = (!(($channel + 1) % 3)) ? 255 : 0; -		$c3 = (!(($channel + 2) % 3)) ? 255 : 0; -		$primary = imagecolorallocate($img, $c1, $c2, $c3); -		$second = imagecolorallocate($img, $c2, $c3, $c1); -		$third = imagecolorallocate($img, $c3, $c1, $c2); - -		imagefill($stencil, 0, 0, $black2); -		imagefill($img, 0, 0, $white1); +		$c = new color_manager($img, 'black'); +		$cs = new color_manager($stencil, 'gray'); +		 +		$c->allocate_named('primary', array( +			'random'			=> true, +			'min_saturation'	=> 75, +			'min_value'			=> 80, +		)); +		 +		$secondary = $c->color_scheme('primary', 'triadic', false); + +		//imagefill($stencil, 0, 0, $black2); +		//imagefill($img, 0, 0, $white1);  		$chars = array_merge(range('A', 'Z'), range('1', '9')); - -		for ($i = 0; $i < 1000; ++$i) +		$step = 20; +		$density = 4; +		for ($i = 0; $i < $img_x; $i += $step)  		{ -			$degree = mt_rand(-30, 30); -			$x = mt_rand(0, $img_x - 1); -			$y = mt_rand(0, $img_y); -			$c = $chars[array_rand($chars)]; -			$font = $fonts[array_rand($fonts)]; - -			imagettftext($stencil, mt_rand(20, 30), $degree, $x, $y, $white2, $font, $c); +			for ($j = 0; $j < $img_y; $j += $step) +			{ +				for ($k = 0; $k < $density; ++$k) +				{ +					$degree = mt_rand(-30, 30); +					$x = mt_rand($i, $i + $step); +					$y = mt_rand($j, $j + $step); +					$char = $chars[array_rand($chars)]; +					$font = $fonts[array_rand($fonts)]; +					imagettftext($stencil, mt_rand(20, 30), $degree, $x, $y, $cs->r('black'), $font, $char); +				} +			}  		}  		for ($i = 0; $i < 3; ++$i) @@ -449,27 +412,62 @@ class captcha  			$x2 = mt_rand(0, $img_x - 1);  			$y1 = mt_rand(0, $img_y);  			$y2 = mt_rand(0, $img_y); -			$c1 = $chars[array_rand($chars)]; -			$c2 = $chars[array_rand($chars)]; +			$char1 = $chars[array_rand($chars)]; +			$char2 = $chars[array_rand($chars)];  			$font1 = $fonts[array_rand($fonts)];  			$font2 = $fonts[array_rand($fonts)]; -			imagettftext($img, 150, $degree1, $x1, $y1, $second, $font1, $c1); -			imagettftext($img, 150, $degree2, $x2, $y2, $third, $font2, $c2); +			imagettftext($img, mt_rand(75, 100), $degree1, $x1, $y1, $secondary[0], $font1, $char1); +			imagettftext($img, mt_rand(75, 100), $degree2, $x2, $y2, $secondary[1], $font2, $char2);  		} -		$text = implode('', $code); -		$font = $fonts[array_rand($fonts)]; -		imagettftext($img, 150, mt_rand(-5, 5), mt_rand(0, $img_x / 4), mt_rand(150, $img_y), $primary, $font, $text); +		$characters = array(); +		$sizes = array(); +		$bounding_boxes = array(); +		$width_avail = $img_x; +		$code_num = sizeof($code); +		$char_class = $this->captcha_char('char_ttf'); +		for ($i = 0; $i < $code_num; ++$i) +		{ +			$characters[$i] = new $char_class($code[$i]); +			$sizes[$i] = mt_rand(75, 100); +			$box = $characters[$i]->dimensions($sizes[$i]); +			$width_avail -= ($box[2] - $box[0]); +			$bounding_boxes[$i] = $box; +		} +		 +		// +		// Redistribute leftover x-space +		// +		$offset = array(); +		for ($i = 0; $i < $code_num; ++$i) +		{ +			$denom = ($code_num - $i); +			$denom = max(1.5, $denom); +			$offset[$i] = mt_rand(0, (1.5 * $width_avail) / $denom); +			$width_avail -= $offset[$i]; +		} + +		// Draw the text +		$xoffset = 0; +		for ($i = 0; $i < $code_num; ++$i) +		{ +			$characters[$i] = new $char_class($code[$i]); +			$dimm = $bounding_boxes[$i]; +			$xoffset += ($offset[$i] - $dimm[0]); +			$yoffset = mt_rand(-$dimm[1], $img_y - $dimm[3]); +			$characters[$i]->drawchar($sizes[$i], $xoffset, $yoffset, $img, $c->r('background'), array($c->r('primary'))); +			$xoffset += $dimm[2]; +		}  		for ($i = 0; $i < $img_x; ++$i)  		{  			for ($j = 0; $j < $img_y; ++$j)  			{ -				// if the stencil is black, set the pixel in the image black -				if (!imagecolorat($stencil, $i, $j)) +				// if the stencil is not black, set the pixel in the image to gray +				if (imagecolorat($stencil, $i, $j))  				{ -					imagesetpixel($img, $i, $j, $black1); +					imagesetpixel($img, $i, $j, $c->r('gray'));  				}  			}  		} @@ -480,6 +478,7 @@ class captcha  	function policy_cells($code)  	{ +		global $user;  		// Generate image  		$img_x = 800;  		$img_y = 250; @@ -492,120 +491,137 @@ class captcha  		//  		// Generate colors  		// -		$white = imagecolorallocate($img, 255, 255, 255); -		$black = imagecolorallocate($img, 0, 0, 0); -		$colors = array( -			imagecolorallocate($img, 128, 0, 0), -			imagecolorallocate($img, 0, 128, 0), -			imagecolorallocate($img, 0, 0, 128), -			imagecolorallocate($img, 90, 90, 90) -		); -						 -		$red = mt_rand(0,3); - -		imagefill($img, 10, 10, $white); - -		$offset1 = 50; -		$offset2 = 550; - -		// draw the cell grid -		for ($i = 0; $i < 4; ++$i) +		$c = new color_manager($img, 'white'); +		 +		$c->allocate_named('primary', array( +			'random'			=> true, +			'min_saturation'	=> 30, +			'min_value'			=> 65, +		)); +		$primaries = $c->color_scheme('primary', 'tetradic'); +		$bg_colors = $c->mono_range($primaries, 'value', 4, false); +		shuffle($primaries); +		shuffle($bg_colors); +		 +		// Randomize the characters on the right and the left +		$left_characters	= array();  +		$right_characters	= array(); +		$chars				= array_merge(range('A', 'Z'), range('1', '9')); +		$chars_size			= sizeof($chars) - 1; +		$alpha				= range('A', 'Z'); +		$alpha_size			= sizeof($alpha) - 1; +		for ($i = 0; $i < 25; ++$i)  		{ -			imageline($img, $offset1 + 40 + ($i * 40), 25, $offset1 + 40 + ($i * 40), 225, $black); -			imageline($img, $offset1, 65 + ($i * 40), $offset1 + 200, 65 + ($i * 40), $black); -			imageline($img, $offset2 + 40 + ($i * 40), 25, $offset2 + 40 + ($i * 40), 225, $black); -			imageline($img, $offset2, 65 + ($i * 40), $offset2 + 200, 65 + ($i * 40), $black); +			$left_characters[$i]	= $alpha[mt_rand(0, $alpha_size)]; +			$right_characters[$i]	= $chars[mt_rand(0, $chars_size)];  		} - +		 +		// Pick locations for our code, shuffle the rest into 3 separate queues  		$code_count = sizeof($code); - -		$characters = array(); -		$bitmap = array_fill(0, 25, 0); -		$cellorder = array(); -		$xs = array(); -		$ys = array(); - -		$chars = array_merge(range('A', 'Z'), range('1', '9')); - -		for ($i = 0, $count = sizeof($chars) - 1; $i < 25; ++$i) +		$code_order = range(0, 24); +		shuffle($code_order); +		$remaining = array_splice($code_order, $code_count); +		$lineups = array($code_order, array(), array(), array()); +		for ($i = sizeof($remaining) - 1; $i >= 0; --$i)  		{ -			// Put a character in this cell -			$characters[$i] = $chars[array_rand($chars)]; -			$xs[$i] = $offset1 + 20 + (($i % 5) * 40) + mt_rand(-13, 13); -			$ys[$i] = 45 + (intval($i / 5) * 40) + mt_rand(-13, 13); -			 -			// Generate a pastel color -			$color = array(255, 255, 255); -			for ($j = 0; $j < 3; ++$j) -			{ -				$color[array_rand($color)] -= 25; -			} -			$bg = imagecolorallocate($img, $color[0], $color[1], $color[2]); -			 -			// fill the cells with the background colors -			imagefilledrectangle($img, $offset1 + 1 + (($i % 5) * 40), 26 + (intval($i / 5) * 40), $offset1 + 39 + (($i % 5) * 40), 64 + (intval($i / 5) * 40), $bg); -			imagefilledrectangle($img, $offset2 + 1 + (($i % 5) * 40), 26 + (intval($i / 5) * 40), $offset2 + 39 + (($i % 5) * 40), 64 + (intval($i / 5) * 40), $bg); +			$lineups[mt_rand(1, 3)][] = $remaining[$i];  		} - -		// assign the code to some cells -		$cellorder = range(0, 24); -		shuffle($cellorder); -		array_splice($cellorder, $code_count); +		 +		// overwrite the randomized left and right values with our code, where applicable  		for ($i = 0; $i < $code_count; ++$i)  		{ -			$bitmap[$cellorder[$i]] = $i + 1; -			$characters[$cellorder[$i]] = $code[$i]; +			$left_characters[$code_order[$i]]	= $i + 1; +			$right_characters[$code_order[$i]]	= $code[$i];  		} +		 +		 +		$offset1 = 50; +		$offset2 = 550; -		// assign the remaning cells to three separate colors -		$spares = array(array(), array(), array()); +		// Draw the cells and right hand characters +		$xs = $ys = array();  		for ($i = 0; $i < 25; ++$i)  		{ -			if (!$bitmap[$i]) -			{ -				$spares[array_rand($spares)][] = $i; -			} -		} -		shuffle($spares[0]); -		shuffle($spares[1]); -		shuffle($spares[2]); +			$xs[$i] = $offset1 + 20 + (($i % 5) * 40) + mt_rand(-13, 13); +			$ys[$i] = 45 + (intval($i / 5) * 40) + mt_rand(-13, 13); -		// draw circles and lines for our fake entries -		for ($k = 0; $k < 3; ++$k ) +			$bg = $c->r_rand($bg_colors); +			 +			// fill the cells with the background colors +			imagefilledrectangle($img, +				$offset1 + 1 + (($i % 5) * 40),		26 + (intval($i / 5) * 40), +				$offset1 + 39 + (($i % 5) * 40),	64 + (intval($i / 5) * 40), +				$bg); +			imagefilledrectangle($img, +				$offset2 + 1 + (($i % 5) * 40),		26 + (intval($i / 5) * 40), +				$offset2 + 39 + (($i % 5) * 40),	64 + (intval($i / 5) * 40), +				$bg); +			 +			$level = intval($i / 5); +			$pos = $i % 5; +			imagettftext($img, 12, 0, +				$offset2 + 15 + ($pos * 40),		50 + ($level * 40), +				$c->is_dark($bg) ? $c->r('white'): $c->r('black'), $fonts['genr102.ttf'], $right_characters[$i]); +		} +		 +		// draw the lines that appear between nodes (visual hint) +		for ($k = 0; $k < 4; ++$k )  		{ -			for ($i = 0, $size = sizeof($spares[$k]); $i < $size; ++$i ) +			$lineup = $lineups[$k]; +			for ($i = 1, $size = sizeof($lineup); $i < $size; ++$i )  			{ -				imagefilledellipse($img, $xs[$spares[$k][$i]], $ys[$spares[$k][$i]], 20, 20, $colors[($red + $k + 1) & 3]); -				if ($i) -				{ -					imageline($img, $xs[$spares[$k][$i - 1]], $ys[$spares[$k][$i - 1]], $xs[$spares[$k][$i]], $ys[$spares[$k][$i]], $colors[($red + $k + 1) & 3]); -				} +				imageline($img, +					$xs[$lineup[$i - 1]],	$ys[$lineup[$i - 1]], +					$xs[$lineup[$i]],		$ys[$lineup[$i]], +					$primaries[$k]);  			}  		} - -		// draw lines for our real entries -		for ($i = 1; $i < $code_count; ++$i ) -		{ -			imageline($img, $xs[$cellorder[$i - 1]], $ys[$cellorder[$i - 1]], $xs[$cellorder[$i]], $ys[$cellorder[$i]], $colors[$red]); -		} - -		// write characters into the text cells & put white dots (change this?) on our fake entry circles -		for ($i = 0; $i < 25; ++$i) +		 +		// draw the actual nodes +		$textcolor = $c->is_dark($primaries[0]) ? $c->r('white') : $c->r('black'); +		for ($k = 0; $k < 4; ++$k )  		{ -			if (!$bitmap[$i]) +			for ($j = 0, $size = sizeof($lineups[$k]); $j < $size; ++$j )  			{ -				imagefilledellipse($img, $xs[$i], $ys[$i], 9, 9, $white); +				$i = $lineups[$k][$j]; +				imagefilledellipse($img, +					$xs[$i],			$ys[$i], +					20,					20, +					$primaries[$k]); +				imagettftext($img, 12, 0, +					$xs[$i] - 5,		$ys[$i] + 5, +					$textcolor, $fonts['genr102.ttf'], $left_characters[$i]);  			} -			$level = intval($i / 5); -			$pos = $i % 5; -			imagettftext($img, 12, 0, $offset2 + 15 + ($pos * 40), 50 + ($level * 40), $black, $fonts[array_rand($fonts)], $characters[$i]);  		} - -		// draw our real entries in -		for ($i = 0; $i < $code_count; ++$i ) +		 +		// Draw poly behind explain text +		$points = mt_rand(3, 6); +		$arc = 360 / $points; +		$vertices = array(); +		$c_x = $img_x / 2; +		$c_y = $img_y / 2; +		$radius = $img_y / 2.5; +		$start = deg2rad(mt_rand(0, 360)); +		for ($i = 0; $i < $points; ++$i) +		{ +			$rad = $start + deg2rad(($arc * $i) + mt_rand(-10, 10)); +			$vertices[] = $c_x + (cos($rad) * $radius); +			$vertices[] = $c_y + (sin($rad) * $radius); +		} +		imagefilledpolygon($img, $vertices, $points, $primaries[mt_rand(0,3)]); +		 +		// draw explain text +		$count = sizeof($user->lang['CAPTCHA']['cells']); +		$line_height = $img_y / ($count + 1); +		for ($i = 0; $i < $count; ++$i)  		{ -			imagefilledellipse($img, $xs[$cellorder[$i]], $ys[$cellorder[$i]], 20, 20, $colors[$red]); -			imagettftext($img, 12, 0, $xs[$cellorder[$i]] - 5, $ys[$cellorder[$i]] + 5, $white, $fonts[array_rand($fonts)], ($i + 1)); +			$text = $user->lang['CAPTCHA']['cells'][$i]; +			$line_width = strlen($text) * 4.5; //  ( / 2, * 9 ) +			imagestring($img, 6, ($img_x / 2) - $line_width - 1, $line_height * ($i + 1) - 1, $text, $c->r('black')); +			imagestring($img, 6, ($img_x / 2) - $line_width - 1, $line_height * ($i + 1) + 1, $text, $c->r('black')); +			imagestring($img, 6, ($img_x / 2) - $line_width + 1, $line_height * ($i + 1) + 1, $text, $c->r('black')); +			imagestring($img, 6, ($img_x / 2) - $line_width + 1, $line_height * ($i + 1) - 1, $text, $c->r('black')); +			imagestring($img, 6, ($img_x / 2) - $line_width, $line_height * ($i + 1), $text, $c->r('white'));  		}  		// Send image @@ -621,19 +637,18 @@ class captcha  		// Generate image  		$img_x = 800;  		$img_y = 250; -		$img = imagecreate($img_x, $img_y); +		$img = imagecreatetruecolor($img_x, $img_y);  		// Generate colors -		$background = imagecolorallocate($img, mt_rand(155, 255), mt_rand(155, 255), mt_rand(155, 255)); -		imagefill($img, 0, 0, $background); - -		$random = $fontcolors = array(); +		$c = new color_manager($img, array( +			'random'			=> true, +			'min_value'			=> 60, +		), 'hsv'); -		for ($i = 0; $i < 15; $i++) -		{ -			$random[$i] = imagecolorallocate($img, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255)); -			$fontcolors[$i] = imagecolorallocate($img, mt_rand(0, 120), mt_rand(0, 120), mt_rand(0, 120)); -		} +		$scheme = $c->color_scheme('background', 'triadic', false); +		$scheme = $c->mono_range($scheme, 'both', 10, false); +		shuffle($scheme); +		$bg_colors = array_splice($scheme, mt_rand(6, 12));  		// Generate code characters  		$characters = $sizes = $bounding_boxes = array(); @@ -665,7 +680,7 @@ class captcha  		// Add some line noise  		if ($config['policy_entropy_noise_line'])  		{ -			$this->noise_line($img, 0, 0, $img_x, $img_y, $background, $fontcolors, $random); +			$this->noise_line($img, 0, 0, $img_x, $img_y, $c->r('background'), $scheme, $bg_colors);  		}  		// Draw the text @@ -675,14 +690,14 @@ class captcha  			$dimm = $bounding_boxes[$i];  			$xoffset += ($offset[$i] - $dimm[0]);  			$yoffset = mt_rand(-$dimm[1], $img_y - $dimm[3]); -			$characters[$i]->drawchar($sizes[$i], $xoffset, $yoffset, $img, $background, $fontcolors); +			$characters[$i]->drawchar($sizes[$i], $xoffset, $yoffset, $img, $c->r('background'), $scheme);  			$xoffset += $dimm[2];  		}  		// Add some pixel noise  		if ($config['policy_entropy_noise_pixel'])  		{ -			$this->noise_pixel($img, 0, 0, $img_x, $img_y, $background, $fontcolors, $random, $config['policy_entropy_noise_pixel']); +			$this->noise_pixel($img, 0, 0, $img_x, $img_y, $c->r('background'), $scheme, $bg_colors, $config['policy_entropy_noise_pixel']);  		}  		// Send image @@ -697,7 +712,7 @@ class captcha  		// Generate image  		$img_x	= 700;  		$img_y	= 225; -		$img	= imagecreate($img_x, $img_y); +		$img	= imagecreatetruecolor($img_x, $img_y);  		$x_grid = mt_rand(6, 10);  		$y_grid = mt_rand(6, 10); @@ -738,26 +753,36 @@ class captcha  		// we'll calculate the 4th point so that it forms a proper trapezoid  		$box[3][0] = $box[2][0] + $box[0][0] - $box[1][0];  		$box[3][1] = $box[2][1] + $box[0][1] - $box[1][1]; - -		// Generate colors (When we get a chance, come up with *better* colors. these ones suck) -		$background = imagecolorallocate($img, mt_rand(155, 255), mt_rand(155, 255), mt_rand(155, 255)); -		imagefill($img, 0, 0, $background); - +		$c = new color_manager($img, array( +			'random'			=> true, +			'min_saturation'	=> 50, +			'min_value'			=> 65, +		)); +		 +		$r1 = $c->random_color(array( +			'min_value'		=> 20, +			'max_value'		=> 50, +		)); +		$r2 = $c->random_color(array( +			'min_value'		=> 70, +			'max_value'		=> 100, +		)); +		$rdata = mt_rand(0,1) ? array( +			$c->colors[$r1], +			$c->colors[$r2], +		) : array( +			$c->colors[$r2], +			$c->colors[$r1], +		); +		  		$colors = array(); - -		$minr = mt_rand(0, 8); -		$ming = mt_rand(0, 8); -		$minb = mt_rand(0, 8); - -		$maxr = mt_rand(128, 220); -		$maxg = mt_rand(128, 220); -		$maxb = mt_rand(128, 220); - -		for ($i = -30; $i <= 30; ++$i) +		for ($i = 0; $i < 60; ++$i)  		{ -			$coeff1 = ($i + 30) / 60; -			$coeff2 = 1 - $coeff1; -			$colors[$i] = imagecolorallocate($img, ($coeff2 * $maxr) + ($coeff1 * $minr), ($coeff2 * $maxg) + ($coeff1 * $ming), ($coeff2 * $maxb) + ($coeff1 * $minb)); +			$colors[$i - 30] = $c->allocate(array( +				$rdata[0][0], +				(($i * $rdata[0][1]) + ((60 - $i) * $rdata[1][1])) / 60, +				(($i * $rdata[0][2]) + ((60 - $i) * $rdata[1][2])) / 60, +			));  		}  		// $img_buffer is the last row of 3-space positions (converted to img-space), cached @@ -817,8 +842,9 @@ class captcha  		{  			$cur_height		= $this->wave_height($x, 0, $subdivision_factor);  			$offset			= $cur_height - $prev_height;  -			$img_pos_cur	= array($img_pos_prev[0] + $dxx, $img_pos_prev[1] + $dxy + $offset); - +			$img_pos_cur	= array($img_pos_prev[0] + $dxx, +									$img_pos_prev[1] + $dxy + $offset); +									  			$img_buffer[0][$x]	= $img_pos_cur;  			$img_pos_prev		= $img_pos_cur;  			$prev_height		= $cur_height; @@ -829,23 +855,25 @@ class captcha  			// swap buffers  			$buffer_cur		= $y & 1;  			$buffer_prev	= 1 - $buffer_cur; - +			  			$prev_height	= $this->wave_height(0, $y, $subdivision_factor);  			$offset			= $prev_height - $this->wave_height(0, $y - 1, $subdivision_factor); -			$img_pos_cur	= array($img_buffer[$buffer_prev][0][0] + $dyx, $img_buffer[$buffer_prev][0][1] + $dyy + $offset); +			$img_pos_cur	= array($img_buffer[$buffer_prev][0][0] + $dyx, +									$img_buffer[$buffer_prev][0][1] + $dyy + $offset);  			$img_pos_prev	= $img_pos_cur;  			$img_buffer[$buffer_cur][0]	= $img_pos_cur; - +			  			for ($x = 1; $x <= $full_x; ++$x)  			{  				$cur_height		= $this->wave_height($x, $y, $subdivision_factor) + $this->grid_height($x, $y, 1, $x_grid, $y_grid); -				// height is a z-factor, not a y-factor +				//height is a z-factor, not a y-factor  				$offset			= $cur_height - $prev_height; -				$img_pos_cur	= array($img_pos_prev[0] + $dxx, $img_pos_prev[1] + $dxy + $offset); -				 -				// (height is float, index it to an int, get closest color) +				$img_pos_cur	= array($img_pos_prev[0] + $dxx, +										$img_pos_prev[1] + $dxy + $offset); + +				//(height is float, index it to an int, get closest color)  				$color			= $colors[intval($cur_height)];  				$img_pos_prev	= $img_pos_cur;  				$prev_height	= $cur_height; @@ -915,20 +943,19 @@ class captcha  		// Generate image  		$img_x = 250;  		$img_y = 120; -		$img = imagecreate($img_x, $img_y); +		$img = imagecreatetruecolor($img_x, $img_y);  		// Generate colors -		$background = imagecolorallocate($img, mt_rand(155, 255), mt_rand(155, 255), mt_rand(155, 255)); -		imagefill($img, 0, 0, $background); - -		$random = $fontcolors = array(); - -		for ($i = 0; $i < 15; $i++) -		{ -			$random[$i] = imagecolorallocate($img, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255)); -		} - -		$fontcolors[0] = imagecolorallocate($img, mt_rand(0, 120), mt_rand(0, 120), mt_rand(0, 120)); +		$c = new color_manager($img, array( +			'random'			=> true, +			'min_saturation'	=> 70, +			'min_value'			=> 65, +		)); +		 +		$primaries = $c->color_scheme('background', 'triadic', false); +		$text = mt_rand(0, 1); +		$c->name_color('text', $primaries[$text]); +		$noise = $c->mono_range($primaries[1 - $text], 'both', 6, false);  		// Generate code characters  		$characters = $bounding_boxes = array(); @@ -952,30 +979,44 @@ class captcha  		// Add some line noise  		if ($config['policy_overlap_noise_line'])  		{ -			$this->noise_line($img, 0, 0, $img_x, $img_y, $background, $fontcolors, $random); +			$this->noise_line($img, 0, 0, $img_x, $img_y, $c->r('background'), array($c->r('text')), $noise);  		}  		// Draw the text -		$min = -$bounding_boxes[0][1]; -		$max = $img_y - $bounding_boxes[0][3]; +		$min = 10 - $bounding_boxes[0][1]; +		$max = ($img_y - 10) - $bounding_boxes[0][3];  		$med = ($max + $min) / 2; - +		  		$yoffset = mt_rand($med, $max); - -		for ($i = 0; $i < $code_num; ++$i) +		$char_num = sizeof($characters); +		 +		imagesetthickness($img, 3); +		for ($i = 0; $i < $char_num; ++$i)  		{ +			if ($i) +			{ +				imageline($img, $old_x + mt_rand(-3, 3), $old_y - 70 + mt_rand(-3, 3), $offset + mt_rand(-3, 3), $yoffset - 70 + mt_rand(-3, 3), $c->r('text')); +				imageline($img, $old_x + mt_rand(-3, 3), $old_y + 30 + mt_rand(-3, 3), $offset + mt_rand(-3, 3), $yoffset + 30 + mt_rand(-3, 3), $c->r('text'));  +			} +			  			$dimm = $bounding_boxes[$i];  			$offset -= $dimm[0]; -			$characters[$i]->drawchar($char_size, $offset, $yoffset, $img, $background, $fontcolors); +			$characters[$i]->drawchar($char_size, $offset, $yoffset, $img, $c->r('background'), array($c->r('text'))); +			 +			$old_x = $offset; +			$old_y = $yoffset; +			  			$offset += $dimm[2];  			$offset -= (($dimm[2] - $dimm[0]) * $overlap_factor);  			$yoffset += ($i & 1) ? ((1 - $overlap_factor) * ($dimm[3] - $dimm[1])) : ((1 - $overlap_factor) * ($dimm[1] - $dimm[3]));  		} +		 +		imagesetthickness($img, 1);  		// Add some medium pixel noise  		if ($config['policy_overlap_noise_pixel'])  		{ -			$this->noise_pixel($img, 0, 0, $img_x, $img_y, $background, $fontcolors, $random, $config['policy_overlap_noise_pixel']); +			$this->noise_pixel($img, 0, 0, $img_x, $img_y, $c->r('background'), array($c->r('text')), $noise, $config['policy_overlap_noise_pixel']);  		}  		// Send image @@ -1180,6 +1221,7 @@ class char_dots  		$this->space = 10;  		$this->radius = 1; +		$this->density = 3;  		$this->letter = $letter;  	} @@ -1211,18 +1253,14 @@ class char_dots  						for ($i = 0; $i < $len; ++$i)  						{ -							$shift1 = mt_rand(-$this->radius, $this->radius); -							$shift2 = mt_rand(-$this->radius, $this->radius); - -							imagesetpixel($img, -										$xoff + ($veclist[1] * $width) + (($i * $dx) / $len) + ($inv_dx * $shift1), -										$yoff + ((1 - $veclist[2]) * $height) + (($i * $dy) / $len) + ($inv_dy * $shift1), -										$color); -							 -							imagesetpixel($img, -										$xoff + ($veclist[1] * $width) + (($i * $dx) / $len) + ($inv_dx * $shift2), -										$yoff + ((1 - $veclist[2]) * $height) + (($i * $dy) / $len) + ($inv_dy * $shift2), -										$color); +							for ($k = 0; $k <= $this->density; ++$k) +							{ +								$shift = mt_rand(-$this->radius, $this->radius); +								imagesetpixel($img, +											$xoff + ($veclist[1] * $width) + (($i * $dx) / $len) + ($inv_dx * $shift), +											$yoff + ((1 - $veclist[2]) * $height) + (($i * $dy) / $len) + ($inv_dy * $shift), +											$color); +							}  						}  					break; @@ -1243,22 +1281,20 @@ class char_dots  						for ($i = 0; $i < $arclengthdeg; $i += $increment)  						{  							$theta = deg2rad(($i + $veclist[5]) % 360); -							$shift1 = mt_rand(-$this->radius, $this->radius); -							$shift2 = mt_rand(-$this->radius, $this->radius); -							$x_o1 = cos($theta) * (($veclist[3] * 0.5 * $width) + $shift1); -							$y_o1 = sin($theta) * (($veclist[4] * 0.5 * $height) + $shift1); -							$x_o2 = cos($theta) * (($veclist[3] * 0.5 * $width) + $shift2); -							$y_o2 = sin($theta) * (($veclist[4] * 0.5 * $height) + $shift2); - -							imagesetpixel($img, -										$xoff + $x_c + $x_o1, -										$yoff + $y_c + $y_o1, -										$color); - -							imagesetpixel($img, -										$xoff + $x_c + $x_o2, -										$yoff + $y_c + $y_o2, -										$color); +							$x_o = cos($theta); +							$y_o = sin($theta); +							$pre_width = ($veclist[3] * 0.5 * $width); +							$pre_height = ($veclist[4] * 0.5 * $height); +							for ($k = 0; $k <= $this->density; ++$k) +							{ +								$shift = mt_rand(-$this->radius, $this->radius); +								$x_o1 = $x_o * ($pre_width + $shift); +								$y_o1 = $y_o * ($pre_height + $shift); +								imagesetpixel($img, +											$xoff + $x_c + $x_o1, +											$yoff + $y_c + $y_o1, +											$color); +							}  						}  					break; @@ -1499,8 +1535,7 @@ class char_hatches  						for ($p = 0; $p <= $hatches; ++$p)  						{ -							// toss some crap at the hough transform, if people even get to that stage here -							if (!mt_rand(0, 9) && ($hatches > 3) && !$p) +							if (!$p && !mt_rand(0, 9) && ($hatches > 3))  							{  								continue;  							} @@ -2699,4 +2734,712 @@ function captcha_vectors()  	);  } +class color_manager +{ +	var $img; +	var $mode; +	var $colors; +	var $named_colors; +	var $named_rgb = array( +		'red'		=> array(0xff, 0x00, 0x00), +		'maroon'	=> array(0x80, 0x00, 0x00), +		'yellow'	=> array(0xff, 0xff, 0x00), +		'olive'		=> array(0x80, 0x80, 0x00), +		'lime'		=> array(0x00, 0xff, 0x00), +		'green'		=> array(0x00, 0x80, 0x00), +		'aqua'		=> array(0x00, 0xff, 0xff), +		'teal'		=> array(0x00, 0x80, 0x80), +		'blue'		=> array(0x00, 0x00, 0xff), +		'navy'		=> array(0x00, 0x00, 0x80), +		'fuchsia'	=> array(0xff, 0x00, 0xff), +		'purple'	=> array(0x80, 0x00, 0x80), +		'white'		=> array(0xff, 0xff, 0xff), +		'silver'	=> array(0xc0, 0xc0, 0xc0), +		'gray'		=> array(0x80, 0x80, 0x80), +		'black'		=> array(0x00, 0x00, 0x00), +	); +	 +	/** +	* Create the color manager, link it to +	* the image resource +	*/ +	function color_manager($img, $background = false, $mode = 'ahsv') +	{ +		$this->img = $img; +		$this->mode = $mode; +		$this->colors = array(); +		$this->named_colors = array(); +		if ($background !== false) +		{ +			$bg = $this->allocate_named('background', $background); +			imagefill($this->img, 0, 0, $bg); +		} +	} +	 +	/** +	* Lookup a named color resource +	*/ +	function r($named_color) +	{ +		if (isset($this->named_colors[$named_color])) +		{ +			return $this->named_colors[$named_color]; +		} +		if (isset($this->named_rgb[$named_color])) +		{ +			return $this->allocate_named($named_color, $this->named_rgb[$named_color], 'rgb'); +		} +		return false; +	} +	 +	/** +	* Assign a name to a color resource +	*/ +	function name_color($name, $resource) +	{ +		$this->named_colors[$name] = $resource; +	} +	 +	/** +	* random color resource +	*/ +	function r_rand($colors) +	{ +		return $colors[array_rand($colors)]; +	} +	 +	/** +	* names and allocates a color resource +	*/ +	function allocate_named($name, $color, $mode = false) +	{ +		$resource = $this->allocate($color, $mode); +		if ($resource !== false) +		{ +			$this->name_color($name, $resource); +		} +		return $resource; +	} +	 +	/** +	* allocates a specified color into the image +	*/ +	function allocate($color, $mode = false) +	{ +		if ($mode === false) +		{ +			$mode = $this->mode; +		} +		if (!is_array($color)) +		{ +			if (isset($this->named_rgb[$color])) +			{ +				return $this->allocate_named($color, $this->named_rgb[$color], 'rgb'); +			} +			if (!is_int($color)) +			{ +				return false; +			} +			$mode = 'rgb'; +			$color = array( +				255 & ($color >> 16), +				255 & ($color >>  8), +				255 & $color, +			); +		} +		 +		if (isset($color['mode'])) +		{ +			$mode = $color['mode']; +			unset($color['mode']); +		} +		if (isset($color['random'])) +		{ +			unset($color['random']); +			// everything else is params +			return $this->random_color($color, $mode); +		} +		 +		$rgb		= color_manager::model_convert($color, $mode, 'rgb'); +		$store		= ($this->mode == 'rgb') ? $rgb : color_manager::model_convert($color, $mode, $this->mode); +		$resource	= imagecolorallocate($this->img, $rgb[0], $rgb[1], $rgb[2]); +		 +		$this->colors[$resource] = $store; +		 +		return $resource; +	} +	 +	/** +	* randomly generates a color, with optional params +	*/ +	function random_color($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'	=> 100,		// 0 - 100 +					'min_value'			=> 30,		// 0 - 100 +					'max_value'			=> 100,		// 0 - 100 +				); +				 +				$alt = ($mode == 'ahsv'); +				 +				$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 color_scheme($resource, $scheme, $include_original = true) +	{ +		$mode = (in_array($this->mode, array('hsv', 'ahsv'), true) ? $this->mode : 'hsv'); +		if (($pre = $this->r($resource)) !== false) +		{ +			$resource = $pre; +		} +		$color = color_manager::model_convert($this->colors[$resource], $this->mode, $mode); +		$results = $include_original ? array($resource) : array(); +		 +		switch ($scheme) +		{ +			case 'complement': +				 +				$color2 = $color; +				$color2[0] += 180; +				$results[] = $this->allocate($color2, $mode); +				 +			break; +			 +			case 'triadic': +				 +				$color2 = $color3 = $color; +				$color2[0] += 120; +				$color3[0] += 240; +				$results[] = $this->allocate($color2, $mode); +				$results[] = $this->allocate($color3, $mode); +				 +			break; +			 +			case 'tetradic': +				 +				$color2 = $color3 = $color4 = $color; +				$color2[0] += 30; +				$color3[0] += 180; +				$color4[0] += 210; +				$results[] = $this->allocate($color2, $mode); +				$results[] = $this->allocate($color3, $mode); +				$results[] = $this->allocate($color4, $mode); +				 +			break; +			 +			case 'analogous': +				 +				$color2 = $color3 = $color; +				$color2[0] += 30; +				$color3[0] += 330; +				$results[] = $this->allocate($color2, $mode); +				$results[] = $this->allocate($color3, $mode); +				 +			break; +		} +		return $results; +	} +	 +	function mono_range($resource, $type = 'both', $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], $type, $count, $include_original)); +			} +			return $results; +		} +		$mode = (in_array($this->mode, array('hsv', 'ahsv'), true) ? $this->mode : 'ahsv'); +		if (($pre = $this->r($resource)) !== false) +		{ +			$resource = $pre; +		} +		$color = color_manager::model_convert($this->colors[$resource], $this->mode, $mode); +		 +		$results = array(); +		if ($include_original) +		{ +			$results[] = $resource; +			$count--; +		} +		 +		switch ($type) +		{ +			case 'saturation': +				 +				$pivot		= $color[1]; +				$num_below	= intval(($pivot * $count) / 100); +				$num_above	= $count - $num_below; +				 +				for ($i = $num_above; $i > 0; --$i) +				{ +					$color[1] = (($i * 100) + (($num_above - $i) * $pivot)) / $num_above; +					$results[] = $this->allocate($color, $mode); +				} +				 +				++$num_below; +				 +				for ($i = $num_below - 1; $i > 0; --$i) +				{ +					$color[1] = ($i * $pivot) / $num_below;; +					$results[] = $this->allocate($color, $mode); +				} +				 +				return $results; +				 +			break; +			 +			case 'value': +				 +				$pivot		= $color[2]; +				$num_below	= intval(($pivot * $count) / 100); +				$num_above	= $count - $num_below; +				 +				for ($i = $num_above; $i > 0; --$i) +				{ +					$color[2] = (($i * 100) + (($num_above - $i) * $pivot)) / $num_above; +					$results[] = $this->allocate($color, $mode); +				} +				 +				++$num_below; +				 +				for ($i = $num_below - 1; $i > 0; --$i) +				{ +					$color[2] = ($i * $pivot) / $num_below;; +					$results[] = $this->allocate($color, $mode); +				} +				 +				return $results; +				 +			break; +			 +			case 'both': +				 +				// This is a hard problem. I chicken out and do an even triangle +				// the problem is that it disregards the original saturation and value, +				//		and as such a generated result might come arbitrarily close to our original value. +				$length = ceil(sqrt($count * 2)); +				for ($i = $length; $i > 0; --$i) +				{ +					for ($j = $i; $j > 0; --$j) +					{ +						$color[1] = ($i * 100) / $length; +						$color[2] = ($j * 100) / $i; +						$results[] = $this->allocate($color, $mode); +						--$count; +						if (!$count) +						{ +							return $results; +						} +					} +				} +				 +				return $results; +				 +			break; +		} +		 +		return false; +	} +	 +	function is_dark($resource) +	{ +		$color = (($pre = $this->r($resource)) !== false) ? $this->colors[$pre] : $this->colors[$resource]; +		switch($this->mode) +		{ +			case 'ahsv': +			case 'hsv': +				 +				return ($color[2] <= 50); +				 +			break; + +			case 'rgb': +				 +				return (max($color[0], $color[1], $color[2]) <= 128); +				 +			break; +		} +		return false; +	} +	 +	/** +	* Convert from one color model to another +	* +	* note: properly following coding standards here yields unweildly amounts of whitespace, rendering this less than easily readable +	*  +	*/ +	function model_convert($color, $from_model, $to_model) +	{ +		if ($from_model == $to_model) +		{ +			return $color; +		} +		switch ($to_model) +		{ +			case 'hsv': +				switch($from_model) +				{ +					case 'ahsv': +						return color_manager::ah2h($color); +					break; + +					case 'rgb': +						return color_manager::rgb2hsv($color); +					break; +				} +			break; +			 +			case 'ahsv': +				switch($from_model) +				{ +					case 'hsv': +						return color_manager::h2ah($color); +					break; + +					case 'rgb': +						return color_manager::h2ah(color_manager::rgb2hsv($color)); +					break; +				} +			break; +			 +			case 'rgb': +				switch($from_model) +				{ +					case 'hsv': +						return color_manager::hsv2rgb($color); +					break; + +					case 'ahsv': +						return color_manager::hsv2rgb(color_manager::ah2h($color)); +					break; +				} +			break; +		} +		return false; +	} +	 +	/** +	* Slightly altered from wikipedia's algorithm +	*/ +	function hsv2rgb($hsv) +	{ +		color_manager::normalize_hue($hsv[0]); +		$h = $hsv[0]; +		$s = min(1, max(0, $hsv[1] / 100)); +		$v = min(1, max(0, $hsv[2] / 100)); +		 +		$hi = floor($hsv[0] / 60);		// calculate hue sector + +		$p = $v * (1 - $s);				// calculate opposite color +		$f = ($h / 60) - $hi;			// calculate distance between hex vertices +		if (!($hi & 1))					// coming in or going out? +		{ +			$f = 1 - $f; +		} +		$q = $v * (1 - ($f * $s));		// calculate adjacent color +		 +		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); +		$h = $max - $min;	// if max - min is 0, we want hue to be 0 anyway. +		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; +			} +		} +		color_manager::normalize_hue($h); +		return array($h, $s * 100, $v * 100); +	} +	 +	/** +	* Bleh +	*/ +	function normalize_hue(&$hue) +	{ +		$hue %= 360; +		if ($hue < 0) +		{ +			$hue += 360; +		} +	} +	 +	/** +	* Alternate hue to hue +	*/ +	function ah2h($ahue) +	{ +		if (is_array($ahue)) +		{ +			$ahue[0] = color_manager::ah2h($ahue[0]); +			return $ahue; +		} +		color_manager::normalize_hue($ahue); +		if ($ahue >= 240) // blue through red is already ok +		{ +			return $ahue; +		} +		if ($ahue >= 180) // ahue green is at 180 +		{ +			// return (240 - (2 * (240 - $ahue))); +			return (2 * $ahue) - 240; // equivalent +		} +		if ($ahue >= 120) // ahue yellow is at 120   (RYB rather than RGB) +		{ +			return $ahue - 60; +		} +		return $ahue / 2; +	} + +	/** +	* hue to Alternate hue +	*/ +	function h2ah($hue) +	{ +		if (is_array($hue)) +		{ +			$hue[0] = color_manager::h2ah($hue[0]); +			return $hue; +		} +		color_manager::normalize_hue($hue); +		if ($hue >= 240) // blue through red is already ok +		{ +			return $hue; +		} +		else if ($hue <= 60) +		{ +			return $hue * 2; +		} +		else if ($hue <= 120) +		{ +			return $hue + 60; +		} +		else +		{ +			return ($hue + 240) / 2; +		} +	} +} + +function vector_distance(&$char, $x, $y, $range = 0.1) +{ +	$distance = $range + 1; +	foreach ($char AS $vector) +	{ +		$d = $range + 1; +		switch ($vector[0]) +		{ +			case 'arc': +				 +				$dx = $x - $vector[1]; +				$dy = -($y - $vector[2]);				//because our arcs are upside-down.... +				if (abs($dx) > abs($dy)) +				{ +					$phi = rad2deg(atan(($dy * $vector[3])/($dx * $vector[4]))); +					$phi += ($dx < 0) ? 180 : 360; +					$phi %= 360; +				} +				else +				{ +					$phi = 90 - rad2deg(atan(($dx * $vector[4])/($dy * $vector[3]))); +					$phi += ($dy < 0) ? 180 : 360; +					$phi %= 360; +				} +								 +				$internal = $vector[6] > $vector[5];	//external wraps over the 360 point +				$low = $phi >= $vector[5]; 					//phi is above our low range +				$high = $phi <= $vector[6];					//phi is below our high range. +				if ($internal ? ($low && $high) : ($low || $high))	//if it wraps, it can only be one or the other +				{ +					$radphi = deg2rad($phi);						// i'm awesome. or not. +					$px = cos($radphi) * 0.5 * $vector[3]; +					$py = sin($radphi) * 0.5 * $vector[4]; +					$d = sqrt(pow($px - $dx, 2) + pow($py - $dy, 2)); +				} +				 +			break; +							 +			case 'line': +				 +				$bx = $x - $vector[1]; +				$by = $y - $vector[2]; +				$dx = cos($vector[5]); +				$dy = sin($vector[5]); +				$r = ($by * $dx) - ($bx * $dy); +				if ($r < $range && $r > -$range) +				{ +					if (abs($dx) > abs($dy)) +					{ +						$s = (($bx + ($dy * $r)) / $dx);								 +					} +					else +					{ +						$s = (($by + ($dx * $r)) / $dy); +					} +					if ($s > -$range) +					{ +						if ($s < 0) +						{ +							$d = sqrt(pow($s, 2) + pow($r, 2)); +						} +						elseif ($s < $vector[6]) +						{ +							$d = $r; +						} +						elseif ($s < $vector[6] + $range) +						{ +							$d = sqrt(pow($s - $vector[6], 2) + pow($r, 2)); +						} +					} +				} +				 +			break; +		} +		$distance = min($distance, $d); +	} +	return $distance; +}  ?>
\ No newline at end of file  | 
