Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 14 |
CRAP | |
0.00% |
0 / 353 |
| colour_manager | |
0.00% |
0 / 1 |
|
0.00% |
0 / 14 |
6972 | |
0.00% |
0 / 353 |
| __construct | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 11 |
|||
| get_resource | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 11 |
|||
| name_colour | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
| allocate_named | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 8 |
|||
| allocate | |
0.00% |
0 / 1 |
72 | |
0.00% |
0 / 34 |
|||
| random_colour | |
0.00% |
0 / 1 |
420 | |
0.00% |
0 / 77 |
|||
| colour_scheme | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 17 |
|||
| mono_range | |
0.00% |
0 / 1 |
56 | |
0.00% |
0 / 31 |
|||
| model_convert | |
0.00% |
0 / 1 |
132 | |
0.00% |
0 / 43 |
|||
| hsv2rgb | |
0.00% |
0 / 1 |
72 | |
0.00% |
0 / 39 |
|||
| rgb2hsv | |
0.00% |
0 / 1 |
42 | |
0.00% |
0 / 27 |
|||
| normalize_hue | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 7 |
|||
| ah2h | |
0.00% |
0 / 1 |
30 | |
0.00% |
0 / 21 |
|||
| h2ah | |
0.00% |
0 / 1 |
30 | |
0.00% |
0 / 24 |
|||
| <?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 = count($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; | |
| } | |
| } | |
| } |