每个字符旋转随机角度的可视验证码

验证码大概如下:

------------------------

------------------------

------------------------


演示网站下载:Click me to DOWNLOAD
验证码多种多样,为了防止被机器识别,通常都会加入干扰元素,如绘制干扰线、干扰点、背景图片,还有让字符扭曲、旋转字符等等。

我个人认为验证扭曲或旋转字符这样的干扰元素比较好,因为加入干扰线,背景图片虽然强悍,但有时可能连人都认不出来,这个会让网站显得很不友好,用户不禁火大:(。

要实现扭曲验证码必须图象大一些(至少要每字符至少要40*40的尺寸)才看地清楚,但有时我们的需求可能不允许让一个如此大尺寸的图象,此时,何不考虑用旋转的形式呢?!

旋转验证码思路不难,分几步就可以完成:
  1. 遍历随机字符,每个字符生成一个图象
  2. 旋转每一个字符图象
  3. 将所有字符图象拼接成一个图象
其中,旋转图象调用 Graphics.RotateTransform() 方法,这个方法有个很糟糕的问题,就是旋转中心点是在图象坐标的(0,0)而不是图象的中心,这就产生了一个麻烦:旋转后越出图象大小的部分会被裁掉,要解决这个问题比较麻烦,网上现成的代码,为了简单起见,在 DrawString 时,调整一个 Point.X|Y,在旋转 [20, 50] 度,字体在 [12, 35]px 上基本没有问题。

C# 代码如下:
using System;
using System.Drawing;
using System.Web;


namespace Oran.Image
{
    
/// <summary>
    
/// 旋转的可视验证码图象
    
/// </summary>
    public class RotatedVlidationCode
    {
        
public enum RandomStringMode
        {
            
/// <summary>
            
/// 小写字母
            
/// </summary>
            LowerLetter,
            
/// <summary>
            
/// 大写字母
            
/// </summary>
            UpperLetter,
            
/// <summary>
            
/// 混合大小写字母
            
/// </summary>
            Letter,
            
/// <summary>
            
/// 数字
            
/// </summary>
            Digital,
            
/// <summary>
            
/// 混合数字与大小字母
            
/// </summary>
            Mix
        }

        
public static string GenerateRandomString(int length, RandomStringMode mode)
        {
            
string rndStr = string.Empty;
            
if (length == 0)
                
return rndStr;

            
//以数组方式候选字符,可以更方便的剔除不要的字符,如数字 0 与字母 o
            char[] digitals = new char[10] { '0''1''2''3''4''5''6''7''8''9' };
            
char[] lowerLetters = new char[26] {
                
'a''b''c''d''e''f''g'
                
'h''i''j''k''l''m''n'
                
'o''p''q''r''s''t'
                
'u''v''w''x''y''z' };
            
char[] upperLetters = new char[26] {
                
'A''B''C''D''E''F''G'
                
'H''I''J''K''L''M''N'
                
'O''P''Q''R''S''T'
                
'U''V''W''X''Y''Z' };
            
char[] letters = new char[52]{
                
'a''b''c''d''e''f''g'
                
'h''i''j''k''l''m''n'
                
'o''p''q''r''s''t'
                
'u''v''w''x''y''z',
                
'A''B''C''D''E''F''G'
                
'H''I''J''K''L''M''N'
                
'O''P''Q''R''S''T'
                
'U''V''W''X''Y''Z' };
            
char[] mix = new char[62]{
                
'0''1''2''3''4''5''6''7''8''9',
                
'a''b''c''d''e''f''g'
                
'h''i''j''k''l''m''n'
                
'o''p''q''r''s''t'
                
'u''v''w''x''y''z',
                
'A''B''C''D''E''F''G'
                
'H''I''J''K''L''M''N'
                
'O''P''Q''R''S''T'
                
'U''V''W''X''Y''Z' };

            
int[] range = new int[2] { 00 };
            Random random 
= new Random();

            
switch (mode)
            {
                
case RandomStringMode.Digital:
                    
for (int i = 0; i < length; ++i)
                        rndStr 
+= digitals[random.Next(0, digitals.Length)];
                    
break;

                
case RandomStringMode.LowerLetter:
                    
for (int i = 0; i < length; ++i)
                        rndStr 
+= lowerLetters[random.Next(0, lowerLetters.Length)];
                    
break;

                
case RandomStringMode.UpperLetter:
                    
for (int i = 0; i < length; ++i)
                        rndStr 
+= upperLetters[random.Next(0, upperLetters.Length)];
                    
break;

                
case RandomStringMode.Letter:
                    
for (int i = 0; i < length; ++i)
                        rndStr 
+= letters[random.Next(0, letters.Length)];
                    
break;

                
default:
                    
for (int i = 0; i < length; ++i)
                        rndStr 
+= mix[random.Next(0, mix.Length)];
                    
break;
            }

            
return rndStr;
        }

        
/// <summary>
        
/// 显示验证码
        
/// </summary>
        
/// <param name="seed">随机数辅助种子</param>
        
/// <param name="strLen">验证码字符长度</param>
        
/// <param name="fontSize">字体大小</param>
        
/// <param name="mode">随机字符模式</param>
        
/// <param name="clrFont">字体颜色</param>
        
/// <param name="clrBg">背景颜色</param>
        public static void ShowValidationCode(ref int seed, int strLen, int fontSize, RandomStringMode mode, Color clrFont, Color clrBg)
        {
            
int tmpSeed;
            
unchecked
            {
                tmpSeed 
= (int)(seed * DateTime.Now.Ticks);
                
++seed;
            }
            Random rnd 
= new Random(tmpSeed);

            
string text = GenerateRandomString(strLen, mode);
            
int height = fontSize * 2;
            
int width = height * text.Length;
            Bitmap bmp 
= new Bitmap(width, height);

            Graphics graphics 
= Graphics.FromImage(bmp);
            Font font 
= new Font("Courier New", fontSize, FontStyle.Bold);
            Brush brush 
= new SolidBrush(clrFont);
            Brush brushBg 
= new SolidBrush(clrBg);
            graphics.FillRectangle(brushBg, 
00, width, height);
            Bitmap tmpBmp 
= new Bitmap(height, width / text.Length);
            Graphics tmpGph 
= null;
            
int degree = 40;
            Point tmpPoint 
= new Point();
            
for (int i = 0; i < text.Length; i++)
            {
                tmpBmp 
= new Bitmap(height, width / text.Length);
                tmpGph 
= Graphics.FromImage(tmpBmp);
                tmpGph.TextRenderingHint 
= System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
                tmpGph.FillRectangle(brushBg, 
00, tmpBmp.Width, tmpBmp.Height);
                degree 
= rnd.Next(2051); // [20, 50]随机角度
                if (rnd.Next(02== 0)
                {
                    tmpPoint.X 
= 12// 调整文本坐标以适应旋转后的图象
                    tmpPoint.Y = -6;
                }
                
else
                {
                    degree 
= ~degree + 1// 逆时针旋转
                    tmpPoint.X = -10;
                    tmpPoint.Y 
= 6;
                }

                tmpGph.RotateTransform(degree);
                tmpGph.DrawString(text[i].ToString(), font, brush, tmpPoint);
                graphics.DrawImage(tmpBmp, i 
* tmpBmp.Width, 0); // 拼接图象
            }
            
            
//输出图象
            System.IO.MemoryStream memoryStream = new System.IO.MemoryStream();
            bmp.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Gif);
            HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
            HttpContext.Current.Response.ClearContent();
            HttpContext.Current.Response.ContentType 
= "image/gif";
            HttpContext.Current.Response.BinaryWrite(memoryStream.ToArray());
            HttpContext.Current.Response.End();

            
//释放资源
            font.Dispose();
            brush.Dispose();
            brushBg.Dispose();
            tmpGph.Dispose();
            tmpBmp.Dispose();
            graphics.Dispose();
            bmp.Dispose();
            memoryStream.Dispose();
        }
    }
}


posted on 2008-06-11 19:56  iTonyDay  阅读(3476)  评论(3编辑  收藏  举报