首页 > 代码库 > mvc 如何生成空心3D风格的验证码

mvc 如何生成空心3D风格的验证码

做网站往往少不了要用到图片验证码。虽然现在已经有很多更先进的判断真实用户的手段,但验证码是最基本的一种。


博主要介绍一种用C#生成空心风格验证码的最简单方法,哈哈,真的很简单哦。说来以前我看到腾讯的空心字验证码也是很流口水啊,比默认字体的漂亮太多了,无奈工作太忙一直没有时间尝试,有一些偶然被设计MM提醒了一下,茅塞顿开呀。


实现方法:这里要用到.net framework里面的一个类:PrivateFontCollection,直观的翻成中文就是私有字体集合,msdn上的解释是:

“提供一个字体系列集合,该集合是基于客户端应用程序提供的字体文件生成的。“

也就是说可以在程序中使用自己的字体,那么能不能可web程序 中使用呢,呵呵,当然可以了.


下面就跟博主一步步来实现吧


1、创建一个类,CaptchaController,继承自Controller,返回图片的方法为Show

     public class CaptchaController : Controller
     {
        //
        // GET: /Captcha/

        public ActionResult Show(int? len, int? w,int? h)
        {
        }
     }

        接下来添加两个私有成员

      

PrivateFontCollection privateFontCollection = new PrivateFontCollection();
FontFamily[] _families = { };


PrivateFontCollection将为我们加载空心字体


2、下载空心字体,引入到代码中,到哪里去下载呢,当然是百度了,不过博主是找公司的设计MM要的,毕竟她们整天接触这个,下面是博主找到的三个空心字体文件,可以把它们放在app_data文件夹里。

技术分享

接下来直接引用到代码中

  privateFontCollection.AddFontFile(Server.MapPath("/App_Data/swisscbo.ttf"));

            privateFontCollection.AddFontFile(Server.MapPath("/App_Data/LARSON.TTF"));
            privateFontCollection.AddFontFile(Server.MapPath("/App_Data/AgentRed.TTF"));
            
            //new FontFamily("Acidic"),
            //                                     new FontFamily("Rosewood Std"),
            _families = privateFontCollection.Families;

3、生成随机码,方法如下

     private string RandomCode(int count)
        {
            Random rand = new Random((int)now.Ticks / 20000);
            String allChar = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            Char[] allCharArray = allChar.ToCharArray();

            String randomCode = "";

            for (int i = 0; i < count; i++)
            {
                int t = rand.Next(35);
                randomCode = String.Concat(randomCode, Char.ToString(allCharArray[t]));
            }
            return randomCode;
        }

4、开始画图,代码如下

       public Bitmap GenerateImage(string text, Size size)
        {
            VXin.Core.RandomNumbers rand = new VXin.Core.RandomNumbers();
            // Create the new Bitmap of the specified size and render to it
            Bitmap bmp = new Bitmap(size.Width, size.Height);
            using (Graphics g = Graphics.FromImage(bmp))
            {
                g.SmoothingMode = SmoothingMode.AntiAlias;  //使绘图质量最高,即消除锯齿
                g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                g.CompositingQuality = CompositingQuality.HighQuality;

                //Draw the background with a white brush
                using (Brush b = new SolidBrush(Color.FromArgb(0xec, 0xf8, 0xff)))
                {
                    g.FillRectangle(b, 0, 0, bmp.Width, bmp.Height);
                }
      
                // Select a font family and create the default sized font.  We then need to shrink
                // the font size until the text fits.
                FontFamily ff = _families[rand.Next(_families.Length)];
                int emSize = (int)(size.Width * 2.5 / text.Length);
                Font f = new Font(ff, emSize);
                try
                {
                    // Make sure that the font size we have will fit with the selected text.
                    SizeF measured = new SizeF(0, 0);
                    SizeF workingSize = new SizeF(size.Width, size.Height);
                    while (emSize > 2 &&
                        (measured = g.MeasureString(text, f)).Width > workingSize.Width ||
                        measured.Height > workingSize.Height)
                    {
                        f.Dispose();
                        f = new Font(ff, emSize -= 2, FontStyle.Bold);
                    }

                    // Select a color and draw the string into the center of the image
                    using (StringFormat fmt = new StringFormat())
                    {
                        fmt.Alignment = fmt.LineAlignment = StringAlignment.Center;

                        using (Brush b = new LinearGradientBrush(
                                  new Rectangle(0, 0, size.Width / 2, size.Height / 2),
                                  _colors[rand.Next(_colors.Length)],
                                  _colors[rand.Next(_colors.Length)],
                                  (float)(rand.NextDouble() * 360), false))
                        {
                            g.DrawString(text, f, b, new Rectangle(0, 5, bmp.Width, bmp.Height), fmt);
                        }
                    }
                }
                finally
                {
                    // Clean up
                    f.Dispose();
                }
            }

            return bmp;
        }

5、到这里画图这一步就完成了,输入应该是一个比较规整的显示字母的图片,但这是不是我想要的验证码啊,验证码不是防止机器识别的吗,不要急,接下来通过最后一步,使图片扭曲,如何扭曲呢,博主打算给它来个波浪型变幻,也就是正弦变幻啦,怎么做到呢,呵呵,用笨方法,挪动每个相素的位置,大家如果有好的办法也可以在下面留言,代码如下

 private static void DistortImage(Bitmap b, double distortion)
        {
            int width = b.Width, height = b.Height;

            // Copy the image so that we‘re always using the original for source color
            using (Bitmap copy = (Bitmap)b.Clone())
            {
                // Iterate over every pixel
                for (int y = 0; y < height; y++)
                {
                    for (int x = 0; x < width; x++)
                    {
                        // Adds a simple wave
                        int newX = (int)(x + (distortion * Math.Sin(Math.PI * y / 64.0)));
                        int newY = (int)(y + (distortion * Math.Cos(Math.PI * x / 64.0)));
                        if (newX < 0 || newX >= width) newX = 0;
                        if (newY < 0 || newY >= height) newY = 0;
                        b.SetPixel(x, y, copy.GetPixel(newX, newY));
                    }
                }

            }
        }


6、完整代码

  public class CaptchaController : Controller
    {
        //
        // GET: /Captcha/

        public ActionResult Show(int? len, int? w,int? h)
        {
            privateFontCollection.AddFontFile(Server.MapPath("/App_Data/swisscbo.ttf"));

            privateFontCollection.AddFontFile(Server.MapPath("/App_Data/LARSON.TTF"));
            privateFontCollection.AddFontFile(Server.MapPath("/App_Data/AgentRed.TTF"));
            
            _families = privateFontCollection.Families;
            
            if (!len.HasValue)
                len = 5;
            var code = RandomCode(len);
            var bmpOut = GenerateImage(code, new Size(90, 42));
            var ms = new MemoryStream();
            bmpOut.Save(ms, ImageFormat.Png);
            var bmpBytes = ms.GetBuffer();
            bmpOut.Dispose();
            ms.Close();
            privateFontCollection.Dispose();
            Session["Captcha"] = code;
            
            return File(bmpBytes, @"image/jpeg");    
        }

        /// <summary>Generates the challenge image.</summary>
        /// <param name="text">The text to be rendered into the image.</param>
        /// <param name="size">The size of the image to generate.</param>
        /// <returns>A dynamically-generated challenge image.</returns>
        public Bitmap GenerateImage(string text, Size size)
        {
            VXin.Core.RandomNumbers rand = new VXin.Core.RandomNumbers();
            // Create the new Bitmap of the specified size and render to it
            Bitmap bmp = new Bitmap(size.Width, size.Height);
            using (Graphics g = Graphics.FromImage(bmp))
            {
                g.SmoothingMode = SmoothingMode.AntiAlias;  //使绘图质量最高,即消除锯齿
                g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                g.CompositingQuality = CompositingQuality.HighQuality;
  
                //Draw the background with a white brush
                using (Brush b = new SolidBrush(Color.FromArgb(0xec, 0xf8, 0xff)))
                {
                    g.FillRectangle(b, 0, 0, bmp.Width, bmp.Height);
                }

                // Select a font family and create the default sized font.  We then need to shrink
                // the font size until the text fits.
                FontFamily ff = _families[rand.Next(_families.Length)];
                int emSize = (int)(size.Width * 2.5 / text.Length);
                Font f = new Font(ff, emSize);
                try
                {
                    // Make sure that the font size we have will fit with the selected text.
                    SizeF measured = new SizeF(0, 0);
                    SizeF workingSize = new SizeF(size.Width, size.Height);
                    while (emSize > 2 &&
                        (measured = g.MeasureString(text, f)).Width > workingSize.Width ||
                        measured.Height > workingSize.Height)
                    {
                        f.Dispose();
                        f = new Font(ff, emSize -= 2, FontStyle.Bold);
                    }

                    // Select a color and draw the string into the center of the image
                    using (StringFormat fmt = new StringFormat())
                    {
                        fmt.Alignment = fmt.LineAlignment = StringAlignment.Center;

                        using (Brush b = new LinearGradientBrush(
                                  new Rectangle(0, 0, size.Width / 2, size.Height / 2),
                                  _colors[rand.Next(_colors.Length)],
                                  _colors[rand.Next(_colors.Length)],
                                  (float)(rand.NextDouble() * 360), false))
                        {
                            g.DrawString(text, f, b, new Rectangle(0, 5, bmp.Width, bmp.Height), fmt);
                        }
                    }
                }
                finally
                {
                    // Clean up
                    f.Dispose();
                }
            }

            // Distort the final image and return it.  This distortion amount is fairly arbitrary.
            DistortImage(bmp, rand.Next(5, 10) * (rand.Next(2) == 1 ? 1 : -1));
            
            using (Graphics g = Graphics.FromImage(bmp))
            {
                using (var pen = new Pen(Color.FromArgb(0xd7, 0xed, 0xfa)))
                {
                    g.DrawRectangle(pen, new Rectangle(0, 0, bmp.Width - 1, bmp.Height - 1));
                }
            }
            return bmp;
        }

        /// <summary>Distorts the image.</summary>
        /// <param name="b">The image to be transformed.</param>
        /// <param name="distortion">An amount of distortion.</param>
        private static void DistortImage(Bitmap b, double distortion)
        {
            int width = b.Width, height = b.Height;

            // Copy the image so that we‘re always using the original for source color
            using (Bitmap copy = (Bitmap)b.Clone())
            {
                // Iterate over every pixel
                for (int y = 0; y < height; y++)
                {
                    for (int x = 0; x < width; x++)
                    {
                        // Adds a simple wave
                        int newX = (int)(x + (distortion * Math.Sin(Math.PI * y / 64.0)));
                        int newY = (int)(y + (distortion * Math.Cos(Math.PI * x / 64.0)));
                        if (newX < 0 || newX >= width) newX = 0;
                        if (newY < 0 || newY >= height) newY = 0;
                        b.SetPixel(x, y, copy.GetPixel(newX, newY));
                    }
                }

            }
        }
    
       private string RandomCode(int count)
        {
            Random rand = new Random((int)now.Ticks / 20000);
            String allChar = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            Char[] allCharArray = allChar.ToCharArray();

            String randomCode = "";

            for (int i = 0; i < count; i++)
            {
                int t = rand.Next(35);
                randomCode = String.Concat(randomCode, Char.ToString(allCharArray[t]));
            }
            return randomCode;
        }
        
        
        private static Color[] _colors = {
                                        Color.ForestGreen,
                                        Color.FromArgb(0,68, 0xA3),
                                        Color.FromArgb(10, 0x1f, 28),
                                        Color.FromArgb(0xe8, 0,0),
                                        Color.FromArgb(84, 0x0b, 0x0b),                                        
                                        Color.FromArgb(0x1f, 84, 0x0b),
                                        Color.FromArgb(11, 65, 01),
                                        Color.FromArgb(79, 0x2c, 0x8f),
                                        Color.FromArgb(0x0d, 01, 65)
                                        };
        /// <summary>List of fonts that can be used for rendering text.</summary>
        /// <remarks>This list can be changed to include any families available on the current system.</remarks>

        PrivateFontCollection privateFontCollection = new PrivateFontCollection();
        private FontFamily[] _families = { };
    }

好了,现在可以把这段程序应用到自己网站上了,刷新一下,显示的应该是这样

技术分享

也可以是这样

技术分享

还可以是这样

技术分享

以及这样

技术分享

每次显示的曲线都不一样哦,还是空心3D字体,看上去是不是很拉轰呢

mvc 如何生成空心3D风格的验证码