You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

150 lines
5.5 KiB

using SixLabors.Fonts;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using System;
using System.IO;
using System.Numerics;
namespace Wood.Util
{
public class CaptchaHelper
{
#region 得到验证码
/// <summary>
/// Tuple第一个值是表达式,第二个值是表达式结果
/// </summary>
/// <returns></returns>
public static Tuple<string, int> GetCaptchaCode()
{
int value = 0;
char[] operators = { '+', '-', '*' };
string randomCode = string.Empty;
Random random = new Random();
int first = random.Next() % 10;
int second = random.Next() % 10;
char operatorChar = operators[random.Next(0, operators.Length)];
switch (operatorChar)
{
case '+': value = first + second; break;
case '-':
// 第1个数要大于第二个数
if (first < second)
{
int temp = first;
first = second;
second = temp;
}
value = first - second;
break;
case '*': value = first * second; break;
}
char code = (char)('0' + (char)first);
randomCode += code;
randomCode += operatorChar;
code = (char)('0' + (char)second);
randomCode += code;
randomCode += "=?";
return new Tuple<string, int>(randomCode, value);
}
#endregion
#region 生成验证码图片
/// <summary>
/// 生成验证码图片
/// </summary>
/// <param name="randomCode">验证码字符串</param>
/// <returns>包含验证码图片的字节数组</returns>
public static byte[] CreateCaptchaImage(string randomCode)
{
const int randAngle = 45; // 随机转动角度
FontFamily fontFamily = SystemFonts.Collection.Get("Verdana");
var font = fontFamily.CreateFont(36, FontStyle.Regular | FontStyle.Bold);
// 计算文本的尺寸
var textSize = TextMeasurer.MeasureSize(randomCode, new TextOptions(font));
int mapWidth = (int)(textSize.Width + 10);
int mapHeight = (int)(textSize.Height + 10);
int singleCodeWidth = mapWidth / randomCode.Length;
int paddingTop = 3;
var aliceBlue = new Rgba32(240, 248, 255);
using var image = new Image<Rgba32>(mapWidth, mapHeight, aliceBlue);
var random = new Random();
// 绘制干扰曲线
for (int i = 0; i < 2; i++)
{
PointF p1 = new(0, random.Next(mapHeight));
PointF p2 = new(random.Next(mapWidth), random.Next(mapHeight));
PointF p3 = new(random.Next(mapWidth), random.Next(mapHeight));
PointF p4 = new(mapWidth, random.Next(mapHeight));
var path = new PathBuilder();
path.AddCubicBezier(p1, p2, p3, p4);
image.Mutate(x => x.Draw(Color.Gray, 1f, path.Build()));
}
// 定义颜色和字体
Color[] colors = {
Color.Black, Color.Red, Color.DarkBlue, Color.Green,
Color.Orange, Color.Brown, Color.DarkCyan, Color.Purple
};
float x = 3; // 起始位置
foreach (char t in randomCode)
{
int cIndex = random.Next(colors.Length);
var color = colors[cIndex];
// 加减乘运算符不进行旋转
if (t == '+' || t == '-' || t == '*' || t == '=')
{
// 加减乘运算符不进行旋转
image.Mutate(ctx => ctx.DrawText(t.ToString(), font, color, new PointF(x, paddingTop)));
}
else
{
float angle = random.Next(-randAngle, randAngle + 1); // 确保包括正负randAngle
// 创建一个变换矩阵用于旋转文本
var transform = Matrix3x2.CreateRotation((float)Math.PI * angle / 180, new PointF(x + singleCodeWidth / 2, paddingTop + textSize.Height / 2));
// 设置绘图选项并应用变换矩阵
var drawingOptions = new DrawingOptions
{
Transform = transform,
GraphicsOptions = new GraphicsOptions()
{
Antialias = true,
ColorBlendingMode = PixelColorBlendingMode.Normal
}
};
// 绘制变换后的文本
image.Mutate(ctx =>
ctx.DrawText(drawingOptions, t.ToString(), font, color, new PointF(x, paddingTop))
);
}
x += singleCodeWidth; // 移动到下一个字符的位置
}
// 将图像保存为 GIF 格式并返回字节数组
using var ms = new MemoryStream();
image.Save(ms, new PngEncoder());
return ms.ToArray();
}
#endregion
}
}