diff options
Diffstat (limited to 'phpBB/phpbb/captcha')
-rw-r--r-- | phpBB/phpbb/captcha/char_cube3d.php | 277 | ||||
-rw-r--r-- | phpBB/phpbb/captcha/colour_manager.php | 527 |
2 files changed, 804 insertions, 0 deletions
diff --git a/phpBB/phpbb/captcha/char_cube3d.php b/phpBB/phpbb/captcha/char_cube3d.php new file mode 100644 index 0000000000..a712b16dce --- /dev/null +++ b/phpBB/phpbb/captcha/char_cube3d.php @@ -0,0 +1,277 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\captcha; + +class char_cube3d +{ + var $bitmap; + var $bitmap_width; + var $bitmap_height; + + var $basis_matrix = array(array(1, 0, 0), array(0, 1, 0), array(0, 0, 1)); + var $abs_x = array(1, 0); + var $abs_y = array(0, 1); + var $x = 0; + var $y = 1; + var $z = 2; + var $letter = ''; + + /** + */ + function __construct(&$bitmaps, $letter) + { + $this->bitmap = $bitmaps['data'][$letter]; + $this->bitmap_width = $bitmaps['width']; + $this->bitmap_height = $bitmaps['height']; + + $this->basis_matrix[0][0] = mt_rand(-600, 600); + $this->basis_matrix[0][1] = mt_rand(-600, 600); + $this->basis_matrix[0][2] = (mt_rand(0, 1) * 2000) - 1000; + $this->basis_matrix[1][0] = mt_rand(-1000, 1000); + $this->basis_matrix[1][1] = mt_rand(-1000, 1000); + $this->basis_matrix[1][2] = mt_rand(-1000, 1000); + + $this->normalize($this->basis_matrix[0]); + $this->normalize($this->basis_matrix[1]); + $this->basis_matrix[2] = $this->cross_product($this->basis_matrix[0], $this->basis_matrix[1]); + $this->normalize($this->basis_matrix[2]); + + // $this->basis_matrix[1] might not be (probably isn't) orthogonal to $basis_matrix[0] + $this->basis_matrix[1] = $this->cross_product($this->basis_matrix[0], $this->basis_matrix[2]); + $this->normalize($this->basis_matrix[1]); + + // Make sure our cube is facing into the canvas (assuming +z == in) + for ($i = 0; $i < 3; ++$i) + { + if ($this->basis_matrix[$i][2] < 0) + { + $this->basis_matrix[$i][0] *= -1; + $this->basis_matrix[$i][1] *= -1; + $this->basis_matrix[$i][2] *= -1; + } + } + + // Force our "z" basis vector to be the one with greatest absolute z value + $this->x = 0; + $this->y = 1; + $this->z = 2; + + // Swap "y" with "z" + if ($this->basis_matrix[1][2] > $this->basis_matrix[2][2]) + { + $this->z = 1; + $this->y = 2; + } + + // Swap "x" with "z" + if ($this->basis_matrix[0][2] > $this->basis_matrix[$this->z][2]) + { + $this->x = $this->z; + $this->z = 0; + } + + // Still need to determine which of $x,$y are which. + // wrong orientation if y's y-component is less than it's x-component + // likewise if x's x-component is less than it's y-component + // if they disagree, go with the one with the greater weight difference. + // rotate if positive + $weight = (abs($this->basis_matrix[$this->x][1]) - abs($this->basis_matrix[$this->x][0])) + (abs($this->basis_matrix[$this->y][0]) - abs($this->basis_matrix[$this->y][1])); + + // Swap "x" with "y" + if ($weight > 0) + { + list($this->x, $this->y) = array($this->y, $this->x); + } + + $this->abs_x = array($this->basis_matrix[$this->x][0], $this->basis_matrix[$this->x][1]); + $this->abs_y = array($this->basis_matrix[$this->y][0], $this->basis_matrix[$this->y][1]); + + if ($this->abs_x[0] < 0) + { + $this->abs_x[0] *= -1; + $this->abs_x[1] *= -1; + } + + if ($this->abs_y[1] > 0) + { + $this->abs_y[0] *= -1; + $this->abs_y[1] *= -1; + } + + $this->letter = $letter; + } + + /** + * Draw a character + */ + function drawchar($scale, $xoff, $yoff, $img, $background, $colours) + { + $width = $this->bitmap_width; + $height = $this->bitmap_height; + $bitmap = $this->bitmap; + + $colour1 = $colours[array_rand($colours)]; + $colour2 = $colours[array_rand($colours)]; + + $swapx = ($this->basis_matrix[$this->x][0] > 0); + $swapy = ($this->basis_matrix[$this->y][1] < 0); + + for ($y = 0; $y < $height; ++$y) + { + for ($x = 0; $x < $width; ++$x) + { + $xp = ($swapx) ? ($width - $x - 1) : $x; + $yp = ($swapy) ? ($height - $y - 1) : $y; + + if ($bitmap[$height - $yp - 1][$xp]) + { + $dx = $this->scale($this->abs_x, ($xp - ($swapx ? ($width / 2) : ($width / 2) - 1)) * $scale); + $dy = $this->scale($this->abs_y, ($yp - ($swapy ? ($height / 2) : ($height / 2) - 1)) * $scale); + $xo = $xoff + $dx[0] + $dy[0]; + $yo = $yoff + $dx[1] + $dy[1]; + + $origin = array(0, 0, 0); + $xvec = $this->scale($this->basis_matrix[$this->x], $scale); + $yvec = $this->scale($this->basis_matrix[$this->y], $scale); + $face_corner = $this->sum2($xvec, $yvec); + + $zvec = $this->scale($this->basis_matrix[$this->z], $scale); + $x_corner = $this->sum2($xvec, $zvec); + $y_corner = $this->sum2($yvec, $zvec); + + imagefilledpolygon($img, $this->gen_poly($xo, $yo, $origin, $xvec, $x_corner,$zvec), 4, $colour1); + imagefilledpolygon($img, $this->gen_poly($xo, $yo, $origin, $yvec, $y_corner,$zvec), 4, $colour2); + + $face = $this->gen_poly($xo, $yo, $origin, $xvec, $face_corner, $yvec); + + imagefilledpolygon($img, $face, 4, $background); + imagepolygon($img, $face, 4, $colour1); + } + } + } + } + + /* + * return a roughly acceptable range of sizes for rendering with this texttype + */ + function range() + { + return array(3, 4); + } + + /** + * Vector length + */ + function vectorlen($vector) + { + return sqrt(pow($vector[0], 2) + pow($vector[1], 2) + pow($vector[2], 2)); + } + + /** + * Normalize + */ + function normalize(&$vector, $length = 1) + { + $length = (( $length < 1) ? 1 : $length); + $length /= $this->vectorlen($vector); + $vector[0] *= $length; + $vector[1] *= $length; + $vector[2] *= $length; + } + + /** + */ + function cross_product($vector1, $vector2) + { + $retval = array(0, 0, 0); + $retval[0] = (($vector1[1] * $vector2[2]) - ($vector1[2] * $vector2[1])); + $retval[1] = -(($vector1[0] * $vector2[2]) - ($vector1[2] * $vector2[0])); + $retval[2] = (($vector1[0] * $vector2[1]) - ($vector1[1] * $vector2[0])); + + return $retval; + } + + /** + */ + function sum($vector1, $vector2) + { + return array($vector1[0] + $vector2[0], $vector1[1] + $vector2[1], $vector1[2] + $vector2[2]); + } + + /** + */ + function sum2($vector1, $vector2) + { + return array($vector1[0] + $vector2[0], $vector1[1] + $vector2[1]); + } + + /** + */ + function scale($vector, $length) + { + if (sizeof($vector) == 2) + { + return array($vector[0] * $length, $vector[1] * $length); + } + + return array($vector[0] * $length, $vector[1] * $length, $vector[2] * $length); + } + + /** + */ + function gen_poly($xoff, $yoff, &$vec1, &$vec2, &$vec3, &$vec4) + { + $poly = array(); + $poly[0] = $xoff + $vec1[0]; + $poly[1] = $yoff + $vec1[1]; + $poly[2] = $xoff + $vec2[0]; + $poly[3] = $yoff + $vec2[1]; + $poly[4] = $xoff + $vec3[0]; + $poly[5] = $yoff + $vec3[1]; + $poly[6] = $xoff + $vec4[0]; + $poly[7] = $yoff + $vec4[1]; + + return $poly; + } + + /** + * dimensions + */ + function dimensions($size) + { + $xn = $this->scale($this->basis_matrix[$this->x], -($this->bitmap_width / 2) * $size); + $xp = $this->scale($this->basis_matrix[$this->x], ($this->bitmap_width / 2) * $size); + $yn = $this->scale($this->basis_matrix[$this->y], -($this->bitmap_height / 2) * $size); + $yp = $this->scale($this->basis_matrix[$this->y], ($this->bitmap_height / 2) * $size); + + $p = array(); + $p[0] = $this->sum2($xn, $yn); + $p[1] = $this->sum2($xp, $yn); + $p[2] = $this->sum2($xp, $yp); + $p[3] = $this->sum2($xn, $yp); + + $min_x = $max_x = $p[0][0]; + $min_y = $max_y = $p[0][1]; + + for ($i = 1; $i < 4; ++$i) + { + $min_x = ($min_x > $p[$i][0]) ? $p[$i][0] : $min_x; + $min_y = ($min_y > $p[$i][1]) ? $p[$i][1] : $min_y; + $max_x = ($max_x < $p[$i][0]) ? $p[$i][0] : $max_x; + $max_y = ($max_y < $p[$i][1]) ? $p[$i][1] : $max_y; + } + + return array($min_x, $min_y, $max_x, $max_y); + } +} diff --git a/phpBB/phpbb/captcha/colour_manager.php b/phpBB/phpbb/captcha/colour_manager.php new file mode 100644 index 0000000000..6ca3c3fd2c --- /dev/null +++ b/phpBB/phpbb/captcha/colour_manager.php @@ -0,0 +1,527 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\captcha; + +class colour_manager +{ + var $img; + var $mode; + var $colours; + var $named_colours; + + /** + * Create the colour manager, link it to the image resource + */ + function __construct($img, $background = false, $mode = 'ahsv') + { + $this->img = $img; + $this->mode = $mode; + $this->colours = array(); + $this->named_colours = array(); + + if ($background !== false) + { + $bg = $this->allocate_named('background', $background); + imagefill($this->img, 0, 0, $bg); + } + } + + /** + * Lookup a named colour resource + */ + function get_resource($named_colour) + { + if (isset($this->named_colours[$named_colour])) + { + return $this->named_colours[$named_colour]; + } + + if (isset($this->named_rgb[$named_colour])) + { + return $this->allocate_named($named_colour, $this->named_rgb[$named_colour], 'rgb'); + } + + return false; + } + + /** + * Assign a name to a colour resource + */ + function name_colour($name, $resource) + { + $this->named_colours[$name] = $resource; + } + + /** + * names and allocates a colour resource + */ + function allocate_named($name, $colour, $mode = false) + { + $resource = $this->allocate($colour, $mode); + + if ($resource !== false) + { + $this->name_colour($name, $resource); + } + return $resource; + } + + /** + * allocates a specified colour into the image + */ + function allocate($colour, $mode = false) + { + if ($mode === false) + { + $mode = $this->mode; + } + + if (!is_array($colour)) + { + if (isset($this->named_rgb[$colour])) + { + return $this->allocate_named($colour, $this->named_rgb[$colour], 'rgb'); + } + + if (!is_int($colour)) + { + return false; + } + + $mode = 'rgb'; + $colour = array(255 & ($colour >> 16), 255 & ($colour >> 8), 255 & $colour); + } + + if (isset($colour['mode'])) + { + $mode = $colour['mode']; + unset($colour['mode']); + } + + if (isset($colour['random'])) + { + unset($colour['random']); + // everything else is params + return $this->random_colour($colour, $mode); + } + + $rgb = $this->model_convert($colour, $mode, 'rgb'); + $store = ($this->mode == 'rgb') ? $rgb : $this->model_convert($colour, $mode, $this->mode); + $resource = imagecolorallocate($this->img, $rgb[0], $rgb[1], $rgb[2]); + $this->colours[$resource] = $store; + + return $resource; + } + + /** + * randomly generates a colour, with optional params + */ + function random_colour($params = array(), $mode = false) + { + if ($mode === false) + { + $mode = $this->mode; + } + + switch ($mode) + { + case 'rgb': + // @TODO random rgb generation. do we intend to do this, or is it just too tedious? + break; + + case 'ahsv': + case 'hsv': + default: + + $default_params = array( + 'hue_bias' => false, // degree / 'r'/'g'/'b'/'c'/'m'/'y' /'o' + 'hue_range' => false, // if hue bias, then difference range +/- from bias + 'min_saturation' => 30, // 0 - 100 + 'max_saturation' => 80, // 0 - 100 + 'min_value' => 30, // 0 - 100 + 'max_value' => 80, // 0 - 100 + ); + + $alt = ($mode == 'ahsv') ? true : false; + $params = array_merge($default_params, $params); + + $min_hue = 0; + $max_hue = 359; + $min_saturation = max(0, $params['min_saturation']); + $max_saturation = min(100, $params['max_saturation']); + $min_value = max(0, $params['min_value']); + $max_value = min(100, $params['max_value']); + + if ($params['hue_bias'] !== false) + { + if (is_numeric($params['hue_bias'])) + { + $h = intval($params['hue_bias']) % 360; + } + else + { + switch ($params['hue_bias']) + { + case 'o': + $h = $alt ? 60 : 30; + break; + + case 'y': + $h = $alt ? 120 : 60; + break; + + case 'g': + $h = $alt ? 180 : 120; + break; + + case 'c': + $h = $alt ? 210 : 180; + break; + + case 'b': + $h = 240; + break; + + case 'm': + $h = 300; + break; + + case 'r': + default: + $h = 0; + break; + } + } + + $min_hue = $h + 360; + $max_hue = $h + 360; + + if ($params['hue_range']) + { + $min_hue -= min(180, $params['hue_range']); + $max_hue += min(180, $params['hue_range']); + } + } + + $h = mt_rand($min_hue, $max_hue); + $s = mt_rand($min_saturation, $max_saturation); + $v = mt_rand($min_value, $max_value); + + return $this->allocate(array($h, $s, $v), $mode); + + break; + } + } + + /** + */ + function colour_scheme($resource, $include_original = true) + { + $mode = 'hsv'; + + if (($pre = $this->get_resource($resource)) !== false) + { + $resource = $pre; + } + + $colour = $this->model_convert($this->colours[$resource], $this->mode, $mode); + $results = ($include_original) ? array($resource) : array(); + $colour2 = $colour3 = $colour4 = $colour; + $colour2[0] += 150; + $colour3[0] += 180; + $colour4[0] += 210; + + $results[] = $this->allocate($colour2, $mode); + $results[] = $this->allocate($colour3, $mode); + $results[] = $this->allocate($colour4, $mode); + + return $results; + } + + /** + */ + function mono_range($resource, $count = 5, $include_original = true) + { + if (is_array($resource)) + { + $results = array(); + for ($i = 0, $size = sizeof($resource); $i < $size; ++$i) + { + $results = array_merge($results, $this->mono_range($resource[$i], $count, $include_original)); + } + return $results; + } + + $mode = (in_array($this->mode, array('hsv', 'ahsv'), true) ? $this->mode : 'ahsv'); + if (($pre = $this->get_resource($resource)) !== false) + { + $resource = $pre; + } + + $colour = $this->model_convert($this->colours[$resource], $this->mode, $mode); + + $results = array(); + if ($include_original) + { + $results[] = $resource; + $count--; + } + + // This is a hard problem. I chicken out and try to maintain readability at the cost of less randomness. + + while ($count > 0) + { + $colour[1] = ($colour[1] + mt_rand(40,60)) % 99; + $colour[2] = ($colour[2] + mt_rand(40,60)); + $results[] = $this->allocate($colour, $mode); + $count--; + } + return $results; + } + + /** + * Convert from one colour model to another + */ + function model_convert($colour, $from_model, $to_model) + { + if ($from_model == $to_model) + { + return $colour; + } + + switch ($to_model) + { + case 'hsv': + + switch ($from_model) + { + case 'ahsv': + return $this->ah2h($colour); + break; + + case 'rgb': + return $this->rgb2hsv($colour); + break; + } + break; + + case 'ahsv': + + switch ($from_model) + { + case 'hsv': + return $this->h2ah($colour); + break; + + case 'rgb': + return $this->h2ah($this->rgb2hsv($colour)); + break; + } + break; + + case 'rgb': + switch ($from_model) + { + case 'hsv': + return $this->hsv2rgb($colour); + break; + + case 'ahsv': + return $this->hsv2rgb($this->ah2h($colour)); + break; + } + break; + } + return false; + } + + /** + * Slightly altered from wikipedia's algorithm + */ + function hsv2rgb($hsv) + { + $this->normalize_hue($hsv[0]); + + $h = $hsv[0]; + $s = min(1, max(0, $hsv[1] / 100)); + $v = min(1, max(0, $hsv[2] / 100)); + + // calculate hue sector + $hi = floor($hsv[0] / 60); + + // calculate opposite colour + $p = $v * (1 - $s); + + // calculate distance between hex vertices + $f = ($h / 60) - $hi; + + // coming in or going out? + if (!($hi & 1)) + { + $f = 1 - $f; + } + + // calculate adjacent colour + $q = $v * (1 - ($f * $s)); + + switch ($hi) + { + case 0: + $rgb = array($v, $q, $p); + break; + + case 1: + $rgb = array($q, $v, $p); + break; + + case 2: + $rgb = array($p, $v, $q); + break; + + case 3: + $rgb = array($p, $q, $v); + break; + + case 4: + $rgb = array($q, $p, $v); + break; + + case 5: + $rgb = array($v, $p, $q); + break; + + default: + return array(0, 0, 0); + break; + } + + return array(255 * $rgb[0], 255 * $rgb[1], 255 * $rgb[2]); + } + + /** + * (more than) Slightly altered from wikipedia's algorithm + */ + function rgb2hsv($rgb) + { + $r = min(255, max(0, $rgb[0])); + $g = min(255, max(0, $rgb[1])); + $b = min(255, max(0, $rgb[2])); + $max = max($r, $g, $b); + $min = min($r, $g, $b); + + $v = $max / 255; + $s = (!$max) ? 0 : 1 - ($min / $max); + + // if max - min is 0, we want hue to be 0 anyway. + $h = $max - $min; + + if ($h) + { + switch ($max) + { + case $g: + $h = 120 + (60 * ($b - $r) / $h); + break; + + case $b: + $h = 240 + (60 * ($r - $g) / $h); + break; + + case $r: + $h = 360 + (60 * ($g - $b) / $h); + break; + } + } + $this->normalize_hue($h); + + return array($h, $s * 100, $v * 100); + } + + /** + */ + function normalize_hue(&$hue) + { + $hue %= 360; + + if ($hue < 0) + { + $hue += 360; + } + } + + /** + * Alternate hue to hue + */ + function ah2h($ahue) + { + if (is_array($ahue)) + { + $ahue[0] = $this->ah2h($ahue[0]); + return $ahue; + } + $this->normalize_hue($ahue); + + // blue through red is already ok + if ($ahue >= 240) + { + return $ahue; + } + + // ahue green is at 180 + if ($ahue >= 180) + { + // return (240 - (2 * (240 - $ahue))); + return (2 * $ahue) - 240; // equivalent + } + + // ahue yellow is at 120 (RYB rather than RGB) + if ($ahue >= 120) + { + return $ahue - 60; + } + + return $ahue / 2; + } + + /** + * hue to Alternate hue + */ + function h2ah($hue) + { + if (is_array($hue)) + { + $hue[0] = $this->h2ah($hue[0]); + return $hue; + } + $this->normalize_hue($hue); + + // blue through red is already ok + if ($hue >= 240) + { + return $hue; + } + else if ($hue <= 60) + { + return $hue * 2; + } + else if ($hue <= 120) + { + return $hue + 60; + } + else + { + return ($hue + 240) / 2; + } + } +} |