.net 8 + SkiaSharp 实现 黑底,浅色字体 6 位字符,扭曲,数字+字母,粘连+干扰线的图形验证码。

先看效果:

image

 

image

 

image

 

image

 实现代码如下:

using SkiaSharp; // SkiaSharp-3.119.0

public class verify_code
{
    private static readonly char[] CharacterTable =
    {
            '1','2','3','4','5','6','7','8','9',
            'A',    'C','D',    'F',
            'H',    'J','K','L',    'N',
                    'P',    'R','S','T',
            'U','V',        'X','Y','Z',
        };

    private static readonly Random Rnd = new Random();
    private static readonly double[,] WaveMatrix;
    private static readonly SKSizeI WaveMatrixSize;

    private static readonly SKColor[] Colors =
    {
            SKColors.Red, SKColors.LimeGreen, SKColors.DeepSkyBlue,
            SKColors.Yellow, SKColors.LightGray, SKColors.Orange
        };

    static verify_code()
    {
        WaveMatrixSize = new SKSizeI(600, 600);
        WaveMatrix = new double[WaveMatrixSize.Width, WaveMatrixSize.Height];

        for (int i = 0; i < WaveMatrixSize.Width; i++)
        {
            for (int j = 0; j < WaveMatrixSize.Height; j++)
            {
                WaveMatrix[i, j] = (Math.Sin((i + j) * Math.PI / 130.0) + 1.0) / 2.0;
            }
        }
    }

    public static string NextCode(int length = 6)
    {
        return string.Concat(Enumerable.Range(0, length)
            .Select(_ => CharacterTable[Rnd.Next(CharacterTable.Length)]));
    }

    public static SKBitmap NextImage(int length = 6, int width = 190, int height = 80)
    {
        string code = NextCode(length);
        return NextImage(code, width, height);
    }

    // ==================== 1. 修改 NextImage 方法(调用单条干扰线) ====================
    public static SKBitmap NextImage(string code, int width = 240, int height = 100)
    {
        var color = Colors[Rnd.Next(Colors.Length)];

        using var font = new SKFont(SKTypeface.FromFamilyName("Arial", SKFontStyle.BoldItalic), 52);
        using var paint = new SKPaint(font)
        {
            Color = color,
            IsAntialias = true,
            Style = SKPaintStyle.Fill,
        };

        var textBounds = new SKRect();
        paint.MeasureText(code, ref textBounds);

        float spacing = -9.5f; // 粘连度

        using var bitmap = new SKBitmap(width, height);
        using var canvas = new SKCanvas(bitmap);

        canvas.Clear(SKColors.Black);

        // ==================== 关键调整:整体靠左 ====================
        float totalTextWidth = textBounds.Width + spacing * (code.Length - 1);
        float x = (width - totalTextWidth) / 2f - 12;   // ← 改为 -12(靠左),之前是 +5

        float y = height * 0.68f;

        foreach (char c in code)
        {
            string ch = c.ToString();
            canvas.DrawText(ch, x, y, paint);
            x += paint.MeasureText(ch) + spacing;
        }

        DrawNoiseLine(canvas, width, height, color);

        return ApplyWaveDistortion(bitmap);
    }

    // ==================== 2. 新增单条干扰线方法 ====================
    private static void DrawNoiseLine(SKCanvas canvas, int width, int height, SKColor color)
    {
        using var paint = new SKPaint
        {
            Color = color.WithAlpha(200),
            StrokeWidth = 2.8f,
            Style = SKPaintStyle.Stroke,
            IsAntialias = true,
        };

        var path = new SKPath();
        path.MoveTo(Rnd.Next(width / 4), Rnd.Next(height));

        // 绘制一条平滑的曲线
        path.CubicTo(
            Rnd.Next(width * 2 / 5, width * 3 / 5), Rnd.Next(height / 4, height * 3 / 4),
            Rnd.Next(width * 2 / 5, width * 3 / 5), Rnd.Next(height / 4, height * 3 / 4),
            Rnd.Next(width * 3 / 4, width), Rnd.Next(height));

        canvas.DrawPath(path, paint);
    }

    private static void DrawNoiseLines(SKCanvas canvas, int width, int height, SKColor color)
    {
        using var paint = new SKPaint
        {
            Color = color.WithAlpha(180),
            StrokeWidth = 2.5f,
            Style = SKPaintStyle.Stroke,
            IsAntialias = true,
        };

        for (int i = 0; i < 4; i++)
        {
            var path = new SKPath();
            path.MoveTo(Rnd.Next(width), Rnd.Next(height));
            path.CubicTo(
                Rnd.Next(width), Rnd.Next(height),
                Rnd.Next(width), Rnd.Next(height),
                Rnd.Next(width), Rnd.Next(height));

            canvas.DrawPath(path, paint);
        }
    }

    private static SKBitmap ApplyWaveDistortion(SKBitmap source)
    {
        int w = source.Width;
        int h = source.Height;
        var result = new SKBitmap(w, h);

        int xi = Rnd.Next(WaveMatrixSize.Width - w);
        int yi = Rnd.Next(WaveMatrixSize.Width - w);
        int xj = Rnd.Next(WaveMatrixSize.Height - h);
        int yj = Rnd.Next(WaveMatrixSize.Height - h);

        for (int i = 0; i < w; i++)
        {
            for (int j = 0; j < h; j++)
            {
                int nx = i - (int)(WaveMatrix[xi + i, xj + j] * 12);
                int ny = j - (int)(WaveMatrix[yi + i, yj + j] * 12);

                if (nx >= 0 && nx < w && ny >= 0 && ny < h)
                {
                    result.SetPixel(i, j, source.GetPixel(nx, ny));
                }
            }
        }
        return result;
    }
}

 

posted @ 2026-07-18 10:53  威流  阅读(8)  评论(0)    收藏  举报