mirror of
https://github.com/YunoHost-Apps/phpbb_ynh.git
synced 2024-09-03 19:56:36 +02:00
527 lines
9.6 KiB
PHP
527 lines
9.6 KiB
PHP
<?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;
|
|
}
|
|
}
|
|
}
|