.NET 6结合SkiaSharp实现拼接验证码功能

从最初的滑动验证码,到实现旋转验证码!不光实践了SkiaSharp的使用,也学到了很多东西。在网上看到一个拼接验证码功能,手痒了起来,结合前面实现的两种验证码,我们来学习一下如何实现拼接验证码功能!

效果预览

在这里插入图片描述

实现原理

其实拼接验证码实现起来比滑动验证码与选择验证码要简单的多,因为拼接验证码不涉及凹槽与滑块模板。只需要一张背景图片就可以了!
获取图片:首先获取背景图片。将背景图片转换成SKBitmap
获取一个随机值:根据图片的宽和高,设定一个固定的X坐标间距spacingX与一个Y坐标间距spacingY,再定义一个X坐标的随机值randomX与Y坐标随机值randomY
横着切图:根据得到的随机值randomY,横着把背景图切成2块,使其成为上下两部分。
在这里插入图片描述
竖着切图:根据得到的随机值randomX,将上半部分竖着切成两块!
在这里插入图片描述
这样,我们就得到了4张图片。
将上半部分左右两张图。位置调换,组成一张新图
在这里插入图片描述
将组合好的新图,与下半部分组合成一张图。
在这里插入图片描述
这样,我们就完成了拼接验证码的图片。将他传到前端。
前端根据服务端传递的数据,将滑块定位到上半部分。然后控制上半部分图片的移动来达到拼接验证码的效果!

代码部分

获取背景图片部分,前面讲过了。
可参考:NET 6 实现滑动验证码(六)、验证码背景图、滑块图与凹槽图的生成

获取背景图

//拼接验证码没有模板,只需要背景图
//获取背景图
var background = await _resourceManager.RandomBackground();

将背景图转为SKBitmap

 using var backgroundImage = SKBitmap.Decode(background);

计算间隔

//X坐标间距
int spacingX = backgroundImage.Width / 8;
//Y坐标间距
int spacingY = backgroundImage.Height / 4;

定义间隔主要是为了生成X轴和Y轴的随机值。

定义随机值

int randomX = _random.Next(spacingX, backgroundImage.Width - backgroundImage.Width /5);
int randomY = _random.Next(spacingY, backgroundImage.Height - spacingY);

定义X轴随机值randomX 与Y轴随机值randomY
**randomY **:randomY主要作用是随机将背景图片切成上下两部分使用的。
**randomX **主要作用是随机将背景图切成左右两部分使用的。

将背景图切成上下两部分

首先定义一个方法,这个方法的作用是根据传入的内容,将指定图片切成两部分。

public static List<byte[]> SplitImage(int pos,bool direction,SKBitmap img)
        {
            int StartImageWidth,
                StartImageHeight,
                EndImageWidth,
                EndImageHeight;
            int EndScanX, EndScanY;
            int ImageWidth = img.Width;
            int ImageHeight = img.Height;

            StartImageWidth = direction ? ImageWidth : pos;
            StartImageHeight = direction ? ImageHeight - pos : ImageHeight;
            EndImageWidth = direction ? ImageWidth : ImageWidth - StartImageWidth;
            EndImageHeight = direction ? pos : ImageHeight;
            EndScanX = direction ? 0 : pos;
            EndScanY = direction ? StartImageHeight : 0;


            using var StartImage = new SKBitmap(StartImageWidth, StartImageHeight);
            using var StartCanvas = new SKCanvas(StartImage);
            SKRect StartSourceRect = new SKRect(0, 0, StartImageWidth, StartImageHeight);
            SKRect StartDestRect = new SKRect(0, 0, StartImageWidth, StartImageHeight);
            StartCanvas.DrawBitmap(img, StartSourceRect, StartDestRect);

            using var EndImage = new SKBitmap(EndImageWidth, EndImageHeight);
            using var EndCanvas = new SKCanvas(EndImage);
            SKRect EndSourceRect = new SKRect(EndScanX, EndScanY, EndScanX + EndImageWidth, EndScanY + EndImageHeight);
            SKRect EndDestRect = new SKRect(0, 0, EndImageWidth, EndImageHeight);
            EndCanvas.DrawBitmap(img, EndSourceRect, EndDestRect);
            List<byte[]> result = new List<byte[]>();
            using var StartImg = SKImage.FromBitmap(StartImage);
            using var StartData = StartImg.Encode(SKEncodedImageFormat.Png, 100);
            using var EndImg = SKImage.FromBitmap(EndImage);
            using var EndData = EndImg.Encode(SKEncodedImageFormat.Png, 100);
            result.Add(StartData.ToArray());
            result.Add(EndData.ToArray());
            return result;

        }

SplitImage方法有3个参数:
pos:分割点,指在图上从哪开始切。
direction:切割方向。true为水平方向,false为垂直方向!
img:待切割的图片。

int StartImageWidth,
    StartImageHeight,
    EndImageWidth,
    EndImageHeight;
int EndScanX, EndScanY;
int ImageWidth = img.Width;
int ImageHeight = img.Height;

因为要将图片切成2张图片,所以StartImageWidthStartImageHeight表示第一张图片的宽和高。EndImageWidthEndImageHeight表示第二张图片的宽和高。
EndScanX表示X坐标的切割点,EndScanY表示Y坐标的切割点。
接下来分别定义两个SKCanvasStartCanvasEndCanvas。表示第一张图的画布与第二张图的画布。定义好之后,再定义两个SKRectSKRect表示矩形,他有4个参数。分别是lefttoprightbottom。刚看到这4个参数有点蒙,这是啥意思?随即反应过来了。其中lerttop确定左上角的坐标。rightbottom确定右下角的坐标。再使用DrawBitmap方法,得到两张图片。
需要注意的是,返回的是一个List<byte[]>。里面保存了两张图片的byte[]数据。为什么不直接返回List呢?因为在代码中使用using var。如果返回List的话。出了这个方法,两张图的SKBitmap就被释放了。那么再使用这两张图的SKBitmap,就会异常了。如果是使用var,并不好找一个合适的时机将他们释放掉。所以就返回了一个List<byte[]>。这样都不耽误。

有了这个方法,我们就可以把图片按照要求,切割成2张需要的图片了
将图片切割成上下两部分:

 List<byte[]> bgImageList = CaptchaImageUtils.SplitImage(randomY, true, backgroundImage);
using var bgImage0 = SKBitmap.Decode(bgImageList[0]);
using var bgImage1 = SKBitmap.Decode(bgImageList[1]);

将上半部分图片切割成左右两部分

List<byte[]> bgImageTopList = CaptchaImageUtils.SplitImage(randomX, false, bgImage0);
using var bgImageTop0 = SKBitmap.Decode(bgImageTopList[0]);
using var bgImageTop1 = SKBitmap.Decode(bgImageTopList[1]);

使用SplitImage方法,将刚刚切好的上下2部分的图中的上半部分,切成左右2部分。这时,我们就有4张图片了!

拼接图片

我们计划是,将bgImageTop0 bgImageTop1 拼接一下,但是拼接的顺序是bgImageTop1+bgImageTop0。这样就得到了一张这样的图,我们叫他SliderImage
在这里插入图片描述
然后再将SliderImagebgImage1拼接,就完成了。
先来看一下拼接的方法,给这个拼接方法起个名字,叫ConcatImage吧。具体代码为:

public static byte[] ConcatImage(bool direction,int width,int height, params SKBitmap[] imgArr)
{
    int pos = 0;
    using var NewImage = new SKBitmap(width, height);
    using var NewImageCanvas = new SKCanvas(NewImage);
    foreach(var img in imgArr)
    {
        float x = direction ? pos : 0;
        float y = direction ? 0 : pos;
        NewImageCanvas.DrawBitmap(img, x,y);
        pos += direction ? img.Width : img.Height;
                
    }
    using var NewImg = SKImage.FromBitmap(NewImage);
    using var NewData = NewImg.Encode(SKEncodedImageFormat.Png, 100);
    return NewData.ToArray();
}

ConcatImage方法有4个参数,
directionbool类型,表示拼接方向,true为水平方向拼接,false为垂直方向拼接。
widthint类型,拼接图片的宽度
heightint类型,拼接图片的高度
imgArrparams 类型,要拼接图片的数组

分别定义SKBitmapSKCanvas。然后循环数组。将图片拼接起来,最后返回一个byte[]

拼接完成后,将图片返回给前端。

return new ConcatImageCaptchaInfo
{
    BackgroundImage = bgImage.ToBase64String(SKEncodedImageFormat.Png),
    SliderImage = null,
    BackgroundImageWidth = bgImage.Width,
    BackgroundImageHeight = bgImage.Height,
    SliderImageWidth = 0,
    SliderImageHeight = 0,
    RandomX = randomX,
    RandomY = randomY,
    Tolerant = 0.05F,
    CaptchaType = CaptchaTypeConstant.CONCAT
};

ToBase64String为自己写的扩展方法。可以参考.Net 6实现旋转验证码,这篇文章写了这个方法了。
前端代码跟之前的滑动验证码基本一样。稍微改下就可以了!

总结

还有其他种类的验证码,比如点选验证码、图文验证码等。会慢慢完善。

点击下方公众号卡片,关注我!一起学习,一起进步!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

倾斜的水瓶座

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值