-
Notifications
You must be signed in to change notification settings - Fork 6
/
captcha3D.php
213 lines (167 loc) · 6.98 KB
/
captcha3D.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
<?php
/**
* Used to generate 3d captchas
* @author [email protected]
* @version 1
* @example
$c = new captcha3D(captcha3D::generateRandomString());
$c->draw(); // or $c->draw('file.png');
*/
class captcha3D
{
protected $_text;
protected $_conf;
private $image = null;
private $textCanvas;
const characters = '0123456789abcdefghijklmnopqrst-*!@#$uvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
/**
* @param string $text text to write
* @param int $width resulting image's width
* @param int $height resulting image's height
* @param int $fontSize in points (30 pt.)
* @param array $rgbColor array(255, 255, 255) for white
* @param string $fontFile name of the file used as font
*/
public function __construct($text, $width = 400, $height = 200, $fontSize = 25, $fontFile = 'Arial.ttf', array $rgbColor = array(0, 228, 0), array $rgbBackground = array(255, 255, 255) )
{
$this->_text = $text;
$this->_conf = array('width' => $width, 'height' => $height, 'fontSize' => $fontSize, 'color' => $rgbColor, 'font' => $fontFile, 'background' => $rgbBackground);
if(!is_file($fontFile) || !is_readable($fontFile))
throw new InvalidArgumentException("The specified font file could not be found");
$this->image = $this->generateCaptchaImage();
}
static function generateRandomString($length = 4) {
return substr(str_shuffle(str_repeat($x=self::characters, ceil($length/strlen($x)) )),1,$length);
}
/**
* Either outputs or saves the image to a png file specified
* @param $filename save path or null
*/
public function draw($filename = null)
{
header("Content-type: image/png");
imagepng($this->getImage(),$filename);
}
/**
* Either outputs <img> tag with embed BASE64 image
*/
public function drawEmbed()
{
//header("Content-type: image/png");
ob_start();
imagepng($this->getImage(),null);
$image = ob_get_contents();
ob_end_clean();
$image = base64_encode($image);
echo '<img src="data:image/png;base64,'.$image.'" alt="some shit" />';
}
protected function getImage()
{
if(null === $this->image)
$this->GenerateCaptchaImage();
return $this->image;
}
protected function GenerateCaptchaImage()
{
$textHeight = 29.0/255.0/(mt_rand(5000,20000)/1000);
$zxzRotationMatrix = self::getZXZRotationMatrix ( pi()/(mt_rand(500,1600)/100) , pi()/(mt_rand(280,320)/100), 0.1);
$this->GenerateTextCanvas();
$this->CreateCaptchaCanvas();
$this->CopyTextCanvasIntoCaptcha($textHeight, $zxzRotationMatrix);
}
protected function GenerateTextCanvas()
{
$this->textCanvas = imagecreatetruecolor(100, 40);
$bgColor = $this->AllocateColorByRgbArray($this->textCanvas, $this->_conf['background']);
imagettftext($this->textCanvas, $this->_conf['fontSize'], 0, 10, 30, $bgColor, $this->_conf['font'], $this->_text);
}
protected function AllocateColorByRgbArray($image, array $rgb)
{
if(3 !== sizeof($rgb))
throw new InvalidArgumentException('Rgb with colors should contain array of 3 elements, each is a number between 0 and 255 (r, g, b)');
array_unshift($rgb, $image);
return call_user_func_array('imageColorAllocate', $rgb);
}
protected function CreateCaptchaCanvas()
{
// Create the image
$this->image = imagecreatetruecolor($this->_conf['width'], $this->_conf['height']);
$white = imagecolorallocate($this->image, 255, 255, 255);
imagefilledrectangle ( $this->image ,0 , 0 , $this->_conf['width'] , $this->_conf['height'] , $white );
}
protected function CopyTextCanvasIntoCaptcha($text3dHeight, $rotationMatrix)
{
$color = $this->AllocateColorByRgbArray($this->image, $this->_conf['color']);
$textCanvasHeight = imagesy($this->textCanvas);
$textCanvasWidth = imagesx($this->textCanvas);
for($y = 0; $y < $textCanvasHeight; $y++)
for($x = 0; $x < $textCanvasWidth; $x++)
{
$pixel = imagecolorat($this->textCanvas, $x, $y);
$pixelColor = (($pixel >> 16) & 0xFF) + (($pixel >> 8) & 0xFF) + ($pixel & 0xFF);
// calculate new (stertched) values
$newX = ($x/$textCanvasWidth - 0.5)*$this->_conf['width'];
$newY = ($y/$textCanvasHeight - 0.5)*$this->_conf['height'];
$newZ = $pixelColor * $text3dHeight;
$grid[$x][$y] = array($newX, $newY, $newZ);
$grid[$x][$y] = self::MultiplyMatrices($grid[$x][$y], $rotationMatrix);
// fix position
$grid[$x][$y][0] += $this->_conf['width']/2;
$grid[$x][$y][1] += $this->_conf['height']/2;
// draw vertical line
if( $y > 0)
imageline($this->image,$grid[$x][$y-1][0], $grid[$x][$y-1][1], $grid[$x][$y][0], $grid[$x][$y][1],$color);
// draw horizontal lines
if ($x > 0)
imageline($this->image,$grid[$x-1][$y][0], $grid[$x-1][$y][1], $grid[$x][$y][0], $grid[$x][$y][1],$color);
}
}
/**
* Returns zxz rotation matrix for the specified angles in radians
* Any x,y,z rotation could be expressed as z rotation, then x rotation and then z rotation again
* @see http://en.wikipedia.org/wiki/Euler_angles
* @static
* @param $_1 Z rotation angle in radians
* @param $_2 X rotation angle in radians
* @param $_3 Z rotation angle in radians
* @return array
*/
private static function getZXZRotationMatrix($_1,$_2,$_3)
{
$zxz = Array();
$c1 = cos($_1); $s1 = sin($_1);
$c2 = cos($_2); $s2 = sin($_2);
$c3 = cos($_3); $s3 = sin($_3);
$zxz[0] = Array($c1*$c3-$c2*$s1*$s3 , -$c3*$s1-$c1*$c2*$s3 , $s2*$s3);
$zxz[1] = Array($c2*$c3*$s1 + $c1*$s3 , $c1*$c2*$c3 - $s1*$s3, -$c3*$s2);
$zxz[2] = Array($s1*$s2,$c1*$s2,$c2);
return $zxz;
}
/**
* Multiplies one 3x3 matrix by another 3x3 matrix
* @static
* @param array $v1 matrix 1
* @param array $m2 matrix 2
* @return array
*/
private static function MultiplyMatrices(array $v1, array $m2)
{
try
{
$multi = Array();
$multi[0] = $v1[0] * $m2[0][0] + $v1[1] * $m2[0][1] + $v1[2] * $m2[0][2] ;
$multi[1] = $v1[0] * $m2[1][0] + $v1[1] * $m2[1][1] + $v1[2] * $m2[1][2] ;
$multi[2] = $v1[0] * $m2[2][0] + $v1[1] * $m2[2][1] + $v1[2] * $m2[2][2] ;
return $multi;
}
catch(Exception $e)
{
throw new InvalidArgumentException('Matrices provided for multiplication are not size of 3');
}
}
public function __destruct()
{
if(null !== $this->image)
imagedestroy($this->image);
}
}