using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
namespace NetAdmin.Infrastructure.Utils;
///
/// 验证码图片工具类
///
public static class CaptchaImageHelper
{
private static readonly int[] _randRange = { 70, 100 };
///
/// 创建一个缺口滑块验证码图片
///
/// 包含资源的程序集
/// 背景图路径
/// 滑块模板小图路径
/// 背景图随机序号范围(1-x)
/// 模板图随机序号范围(1-x)
/// 滑块尺寸
/// 背景图(base64),滑块图(base64),缺口坐标
#pragma warning disable SA1414
public static async Task<(string BackgroundImage, string SliderImage, Point OffsetSaw)> CreateSawSliderImageAsync(
Assembly resAsm, string bgPath, string tempPath, (int, int) bgIndexScope, (int, int) tempIndexScope
, Size sliderSize)
#pragma warning restore SA1414
{
// 深色模板图
var templateIndex = new[] { tempIndexScope.Item1, tempIndexScope.Item2 }.Rand();
await using var bgStream = resAsm.GetManifestResourceStream(
$"{bgPath}.{new[] { bgIndexScope.Item1, bgIndexScope.Item2 }.Rand()}.jpg");
await using var darkStream = resAsm.GetManifestResourceStream($"{tempPath}._{templateIndex}.dark.png");
await using var tranStream = resAsm.GetManifestResourceStream($"{tempPath}._{templateIndex}.transparent.png");
// 底图
using var backgroundImage = await Image.LoadAsync(bgStream).ConfigureAwait(false);
using var darkTemplateImage = await Image.LoadAsync(darkStream).ConfigureAwait(false);
// 透明模板图
using var transparentTemplateImage = await Image.LoadAsync(tranStream).ConfigureAwait(false);
// 调整模板图大小
darkTemplateImage.Mutate(x => x.Resize(sliderSize));
transparentTemplateImage.Mutate(x => x.Resize(sliderSize));
// 新建拼图
using var blockImage = new Image(sliderSize.Width, sliderSize.Height);
// 新建滑块拼图
using var sliderBlockImage = new Image(sliderSize.Width, backgroundImage.Height);
// 随机生成拼图坐标
var offsetRand = GeneratePoint(backgroundImage.Width, backgroundImage.Height, sliderSize.Width
, sliderSize.Height);
// 根据深色模板图计算轮廓形状
var blockShape = CalcBlockShape(darkTemplateImage);
// 生成拼图
blockImage.Mutate(x => {
// ReSharper disable once AccessToDisposedClosure
_ = x.Clip(blockShape, p => p.DrawImage(backgroundImage, new Point(-offsetRand.X, -offsetRand.Y), 1));
});
// 拼图叠加透明模板图层
// ReSharper disable once AccessToDisposedClosure
blockImage.Mutate(x => x.DrawImage(transparentTemplateImage, new Point(0, 0), 1));
// 生成滑块拼图
// ReSharper disable once AccessToDisposedClosure
sliderBlockImage.Mutate(x => x.DrawImage(blockImage, new Point(0, offsetRand.Y), 1));
var opacity = (float)(_randRange.Rand() * 0.01);
// 底图叠加深色模板图
// ReSharper disable once AccessToDisposedClosure
backgroundImage.Mutate(x => x.DrawImage(darkTemplateImage, new Point(offsetRand.X, offsetRand.Y), opacity));
// 生成干扰图坐标
var interferencePoint = GenerateInterferencePoint(backgroundImage.Width, backgroundImage.Height
, sliderSize.Width, sliderSize.Height, offsetRand.X
, offsetRand.Y);
// 底图叠加深色干扰模板图
// ReSharper disable once AccessToDisposedClosure
backgroundImage.Mutate(x => x.DrawImage(darkTemplateImage, new Point(interferencePoint.X, interferencePoint.Y)
, opacity));
return (backgroundImage.ToBase64String(PngFormat.Instance), sliderBlockImage.ToBase64String(PngFormat.Instance)
, offsetRand);
}
private static ComplexPolygon CalcBlockShape(Image templateDarkImage)
{
var temp = 0;
var pathList = new List();
templateDarkImage.ProcessPixelRows(accessor => {
for (var y = 0; y < templateDarkImage.Height; y++) {
var rowSpan = accessor.GetRowSpan(y);
for (var x = 0; x < rowSpan.Length; x++) {
ref var pixel = ref rowSpan[x];
if (pixel.A != 0) {
temp = temp switch { 0 => x, _ => temp };
}
else {
if (temp == 0) {
continue;
}
pathList.Add(new RectangularPolygon(temp, y, x - temp, 1));
temp = 0;
}
}
}
});
return new ComplexPolygon(new PathCollection(pathList));
}
///
/// 随机生成干扰图坐标
///
private static Point GenerateInterferencePoint(int originalWidth, int originalHeight, int templateWidth
, int templateHeight, int blockX, int blockY)
{
var x =
// 在原扣图右边插入干扰图
originalWidth - blockX - 5 > templateWidth * 2
? GetRandomInt(blockX + templateWidth + 5, originalWidth - templateWidth)
:
// 在原扣图左边插入干扰图
GetRandomInt(100, blockX - templateWidth - 5);
var y =
// 在原扣图下边插入干扰图
originalHeight - blockY - 5 > templateHeight * 2
? GetRandomInt(blockY + templateHeight + 5, originalHeight - templateHeight)
:
// 在原扣图上边插入干扰图
GetRandomInt(5, blockY - templateHeight - 5);
return new Point(x, y);
}
///
/// 随机生成拼图坐标
///
private static Point GeneratePoint(int originalWidth, int originalHeight, int templateWidth, int templateHeight)
{
var widthDifference = originalWidth - templateWidth;
var heightDifference = originalHeight - templateHeight;
var x = widthDifference switch {
<= 0 => 5, _ => new[] { 0, originalWidth - templateWidth - 100 }.Rand() + 100
};
var y = heightDifference switch { <= 0 => 5, _ => new[] { 0, originalHeight - templateHeight - 5 }.Rand() + 5 };
return new Point(x, y);
}
///
/// 随机范围内数字
///
private static int GetRandomInt(int startNum, int endNum)
{
return (endNum > startNum ? new[] { 0, endNum - startNum }.Rand() : 0) + startNum;
}
}