aboutsummaryrefslogtreecommitdiffstats
path: root/phpBB/includes/captcha/captcha_gd.php
diff options
context:
space:
mode:
authorDavid M <davidmj@users.sourceforge.net>2006-08-12 02:09:41 +0000
committerDavid M <davidmj@users.sourceforge.net>2006-08-12 02:09:41 +0000
commitca2a672d20f65d44cf9eb6114e89d273ba5deaef (patch)
tree33bc4d13d8cff529d86b6b9251d0a1cecbda4a3b /phpBB/includes/captcha/captcha_gd.php
parentb1ef984526f462a2c2132ab34a039490bceeb836 (diff)
downloadforums-ca2a672d20f65d44cf9eb6114e89d273ba5deaef.tar
forums-ca2a672d20f65d44cf9eb6114e89d273ba5deaef.tar.gz
forums-ca2a672d20f65d44cf9eb6114e89d273ba5deaef.tar.bz2
forums-ca2a672d20f65d44cf9eb6114e89d273ba5deaef.tar.xz
forums-ca2a672d20f65d44cf9eb6114e89d273ba5deaef.zip
- New color generation system, please report any images that have issues.
git-svn-id: file:///svn/phpbb/trunk@6267 89ea8834-ac86-4346-8a33-228a782c2dd0
Diffstat (limited to 'phpBB/includes/captcha/captcha_gd.php')
-rw-r--r--phpBB/includes/captcha/captcha_gd.php1433
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