<?php
class captcha {
static private $_str = 'aAbBdDeEfFgGhHiJLmMnNqQrRtTyY23456789';//只允许的字符串
/*
* 清空验证码
*/
static public function clear() {
!session_id() && session_start();
unset($_SESSION['qidiziCaptcha']);
}
/* * 验证输入 * return 正确:true,错误:false,其它:false
*/
static public function verify($input = null) {
!session_id() && session_start();
if (empty($input) || empty($_SESSION['qidiziCaptcha'])) {
return false;
}
$input = (strtolower($input) === $_SESSION['qidiziCaptcha']);
captcha::clear(); //防止只请求一次输出验证码,而多次请求验证的漏洞.
return $input;
}
/* * 输出图片 * return 验证码字符串 */
static public function init() {
!session_id() && session_start();
$minCount = 4;//码最少个数
$maxCount = 5;//码最多个数
$captchaText = captcha::_rmBadWord(rand($minCount, $maxCount));//获取随机字符
$_SESSION['qidiziCaptcha'] = strtolower($captchaText);
$rndM = 10;
$rnd = rand(-$rndM, $rndM);
$scale3d = 1.3 + abs($rnd / $rndM);//3d背景的比率
// Calculate projection matrix
$T = captcha::_cameraTransform(//三维图像的向量
array(//方向
$rnd//左右旋转角度,随机之间
,-40 //前后旋转角度,负数为从前向后转(正字),正数从后向前转(倒字)
,50 //3d凸起感,跟前面二个参数相互作用.针对于看得清的原则,前后角度可以达到凸起角度二倍,但是左右角度就很小了.
)
,array(//标量
0 //3d投影离左边距,正数左移,负数右移
, 0 //3d投影离顶边距,正数上移,负数下移
, 0 //3d投影z轴投影深度,正数上移,负数下移
)
);
$T = captcha::_matrixProduct(
$T,
captcha::_viewingTransform(
80 //缩放,不能写0,负数反转,
, 300//二参数不明,后者必须大于前者
, 301
)
);
// Calculate coordinates
$coord = array();
$count = 0;
//echo $captchaText;
$chars = captcha::_getCharCode($captchaText);
//exit(show2d(turnLR($chars, 10)));
//show2d($chars);exit;
if (empty($chars)) {
exit('empty captcha text.');
}
$width = count($chars[0]);//坚向是宽
$height = count($chars);//横向是高
for ($y = 0; $y < $height; $y += 1) {//2d点变3d点,调整$y+=2剃度将改变上下线条密码
for ($x = 0; $x < $width; $x++) {
$xc = $x - $width / 2;//2d图片x,左上角是0,0
$yc = -$chars[$y][$x];
$zc = $y - $height / 2;
$xyz = array($xc, $yc, $zc, 1);
$xyz = captcha::_vectorProduct($xyz, $T);
$coord[$count] = $xyz;
$count++;
}
}
$scale = 3;//3d字与2d字缩放,最好在1到2之间
$image3d_x = $width * $scale * 1.3;//外背景横向边长,上面的绽放是60时,几乎是1.75关系
$image3d_y = $height * $scale * $scale3d;//外背景高度
$image3d = imagecreatetruecolor($image3d_x, $image3d_y);//创建外背景区域大小
$bgcolor = imagecolorallocate($image3d, 237, 237, 237);
//$txtcolor = imagecolorallocate($image3d, 0, 220, 0);
imagefill($image3d, 0, 0, $bgcolor);//填充外背景
//imagestring($image3d, 5, 5, 2, 'http://chinahrd.net', $txtcolor);//输入水印
$count = 0;
$core3dX = $image3d_x / 2;//3d背景中心xy
$core3dY = $image3d_y / 2;
$lineSpan = 1.3;//三d上下二线间隔
$fgcolor = imagecolorallocate($image3d, 0, 0, 0);//随机线笔色
for ($y = 0; $y < $height; $y++) {
for ($x = 0; $x < $width; $x++) {
if ($x > 0) {
$x0 = $core3dX + $coord[$count - 1][0] * $scale;//原坐标加上背景中心坐标可以让3d显示居中,*以倍数可以放大.
$y0 = $core3dY + $coord[$count - 1][1] * $scale * $lineSpan;
$x1 = $core3dX + $coord[$count][0] * $scale ;
$y1 = $core3dY + $coord[$count][1] * $scale * $lineSpan;
imageline($image3d, $x0, $y0, $x1, $y1, $fgcolor);//在点0 到 点1之间画线
}
$count++;
}
}
header("Content-type: image/jpeg", true);
header("Cache-Control: no-cache, must-revalidate", true); // HTTP/1.1
header("Expires: Mon, 03 Apr 1977 11:05:00 GMT", true); // 立刻过期,不缓存
imagejpeg($image3d);
return $captchaText;
}
//生成一个三维点坐标
static private function _addVector($a, $b) {
return array($a[0] + $b[0], $a[1] + $b[1], $a[2] + $b[2]);
}
//二个数乘积:针对三维点改变量,方向不变
static private function _scalarProduct($vector, $scalar) {
return array($vector[0] * $scalar, $vector[1] * $scalar, $vector[2] * $scalar);
}
//三维点积,得到标量:计算机图形学常用来进行方向性判断,如两矢量点积大于0,则它们的方向朝向相近;如果小于0,则方向相反
static private function _dotProduct($a, $b) {
return ($a[0] * $b[0] + $a[1] * $b[1] + $a[2] * $b[2]);
}
//对某三维点缩小为:点积平方根分之一
static private function _normalize($vector) {
return captcha::_scalarProduct($vector, 1 / sqrt(captcha::_dotProduct($vector, $vector))/*点积平方根分之一*/ );
}
//叉积:两个向量的叉积与这两个向量都垂直,最终形成一个立方体,如平台二线积形成长方形一样.
static private function _crossProduct($a, $b) {
return array(
($a[1] * $b[2] - $a[2] * $b[1]),
($a[2] * $b[0] - $a[0] * $b[2]),
($a[0] * $b[1] - $a[1] * $b[0])
);
}
//计算出3d外围二点:投影参数+平面点
static private function _vectorProductIndexed($xyz, $m, $i) {
return array(
$xyz[$i + 0] * $m[0] + $xyz[$i + 1] * $m[4] + $xyz[$i + 2] * $m[8] + $xyz[$i + 3] * $m[12],
$xyz[$i + 0] * $m[1] + $xyz[$i + 1] * $m[5] + $xyz[$i + 2] * $m[9] + $xyz[$i + 3] * $m[13],
$xyz[$i + 0] * $m[2] + $xyz[$i + 1] * $m[6] + $xyz[$i + 2] * $m[10]+ $xyz[$i + 3] * $m[14],
$xyz[$i + 0] * $m[3] + $xyz[$i + 1] * $m[7] + $xyz[$i + 2] * $m[11]+ $xyz[$i + 3] * $m[15]
);
}
//对字点(笔划1/背景0=y,背景width点=x,背景height点=z)应用投影的参数);
static private function _vectorProduct($xyz, $mv) {
return captcha::_vectorProductIndexed($xyz, $mv, 0);
}
//矩阵积:相当于把3d点附加有xyz/绽放属性
static private function _matrixProduct($a, $b) {
$o1 = captcha::_vectorProductIndexed($a, $b, 0);
$o2 = captcha::_vectorProductIndexed($a, $b, 4);
$o3 = captcha::_vectorProductIndexed($a, $b, 8);
$o4 = captcha::_vectorProductIndexed($a, $b, 12);
return array(
$o1[0], $o1[1], $o1[2], $o1[3],
$o2[0], $o2[1], $o2[2], $o2[3],
$o3[0], $o3[1], $o3[2], $o3[3],
$o4[0], $o4[1], $o4[2], $o4[3]
);
}
//首先来介绍三个基本的矩阵,这是direct 3d中必须设置的三个矩阵,即世界矩阵,视图矩阵,投影矩阵。而对应的三个变换就是世界变换(World Transform),摄像机变换(Camera Transform),和投影变换(Projection Transform)
//摄像机转换
//应该是使用了斜平面投影
// http://graphics.idav.ucdavis.edu/education/GraphicsNotes/Camera-Transform/Camera-Transform.html
static private function _cameraTransform($C, $A) {
$w = captcha::_normalize(captcha::_addVector($C, captcha::_scalarProduct($A, -1)));//世界坐标点
$y = array(0, 1, 0);
$u = captcha::_normalize(captcha::_crossProduct($y, $w));//用户坐标点
$v = captcha::_crossProduct($w, $u);
$t = captcha::_scalarProduct($C, -1);//投影坐标点
return array(
$u[0], $v[0], $w[0], 0,
$u[1], $v[1], $w[1], 0,
$u[2], $v[2], $w[2], 0,
captcha::_dotProduct($u, $t), captcha::_dotProduct($v, $t), captcha::_dotProduct($w, $t), 1
);
}
//三维观察变换
// http://graphics.idav.ucdavis.edu/education/GraphicsNotes/Viewing-Transformation/Viewing-Transformation.html
static private function _viewingTransform($fov, $n, $f) {
$fov *= (M_PI / 180);
$cot = 1 / tan($fov / 2);
return array(
$cot, 0, 0, 0,
0, $cot, 0, 0,
0, 0, ($f + $n) / ($f - $n), -1,
0, 0, 2 * $f * $n / ($f - $n), 0
);
}
/* * 随机产生字符 */
static private function _rndStr($length) {
$n = strlen(captcha::$_str);
$captchaText = '';
for ($i = 0; $i < $length; $i++) {
$captchaText .= captcha::$_str[rand(0, $n - 1)];
}
return $captchaText;
}
/*
* 移除不文明英文
*/
static private function _rmBadWord($length) {
$badWords = array(
'anal'
,'whore'
);
do {
$cleanText = captcha::_rndStr($length);
} while (in_array($cleanText, $badWords));
return $cleanText;
}
/*
* 插算拉宽
*/
static private function _doWidths($font, $times) {
if ($times < 1) {
return $font;
}
foreach ($font as $rk => $row) {
$tmp = array();
foreach ($row as $cell) {
$tmp = array_pad($tmp, count($tmp) + $times, $cell);//复制列点n次
}
$font[$rk] = $tmp;
}
return $font;
}
/*
* 插算拉高
*/
static private function _doHeights($font, $times) {
if ($times < 1) {
return $font;
}
$tmp = array();
foreach ($font as $row) {
$tmp = array_pad($tmp, count($tmp) + $times, $row);//行复制n次
}
return $tmp;
}
---代码未完-----
--------------使用了预点阵码,去掉写字再像素提取步骤,大部分是3维知识,资料过少,网上有直接是代码形态的知识点,中文详细次数比较少,难理解,3d坐标计算比较难掌握----