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
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
|
|
}
|
|
}
|
|
|