.NET 採用 SkiaSharp 生成二維條碼和圖形驗證碼及圖片進行指定區域擷取方法實現

2022-10-12 15:00:17

在最新版的 .NET 平臺中,微軟在逐步放棄 System.Drawing.Imaging ,給出的理由如下:

System.Drawing名稱空間對某些作業系統和應用程式型別有一些限制。

  1. 在Windows, System.Drawing 依賴於GDI+作業系統附帶的本機庫。 某些Windows SKUS Windows Server Core 或 Windows Nano)不包含此本機庫作為 OS 的一部分。 如果使用此名稱空間並且無法載入庫,則執行時將引發異常。
  2. 名稱空間中的某些型別依賴於 GDI+ ,而 Windows 服務以及 ASP.NET Core 和 System.Drawing ASP.NET 應用不支援。 這些型別在System.Drawing.Common NuGet包中,幷包括 System.Drawing.Bitmap 和 System.Drawing.Font 。 但是,名稱空間中的基元型別(如 System.Drawing.Color 、、 和 System.Drawing.Size System.Drawing.Point System.Drawing.Rectangle )可以在任何應用程式中使用。
  3. 在 .NET 5 和早期版本中,System.Drawing.Common NuGet 包適用於 Windows、Linux 和 macOS。 但是,存在一些平臺差異。 在 Linux 和 macOS 上,GDI+功能由libgdiplus) 庫實現。 預設情況下,大多數 Linux 發行版中不會安裝此庫,也不支援 GDI+ 和 macOS 上Windows的所有功能。 還有一些平臺,其中 libgdiplus 完全不可用。 若要在 Linux 和 macOS 上使用 System.Drawing.Common 包中的型別,必須單獨安裝 libgdiplus。 有關詳細資訊,請參閱在Linux 上安裝 .NET或在macOS 上安裝 .NET。
  4. 在 .NET 6 及更高版本中,System.Drawing.Common NuGet 包僅在 Windows作業系統上受支援。 有關詳細資訊,請參閱 僅支援System.Drawing.Common Windows。

所以我將專案中原先使用 System.Drawing.Imaging 實現的方法採用 SkiaSharp 進行了重寫。
SkiaSharp是 Google 的Skia 圖形庫的 .NET 包裝器,可用於跨移動、伺服器和桌面平臺繪製 2D 圖形。SkiaSharp 可與 OpenGL 一起用於硬體加速渲染。SkiaSharp 最初由 Mono 開發,但現在由 Microsoft 維護,並根據MIT License提供。

依賴的 Nuget 元件如下:

  1. SkiaSharp
  2. SkiaSharp.NativeAssets.Linux
  3. SkiaSharp.QrCode
using SkiaSharp.QrCode;

namespace Common
{
    public class ImgHelper
    {

        /// <summary>
        /// 生成二維條碼
        /// </summary>
        /// <param name="text">二維條碼內容</param>
        /// <returns></returns>
        public static byte[] GetQrCode(string text)
        {
            using QRCodeGenerator generator = new();
            using var qr = generator.CreateQrCode(text, ECCLevel.L);
            SKImageInfo info = new(500, 500);

            using var surface = SKSurface.Create(info);
            using var canvas = surface.Canvas;
            canvas.Render(qr, info.Width, info.Height, SKColors.White, SKColors.Black);

            using var image = surface.Snapshot();
            using var data = image.Encode(SKEncodedImageFormat.Png, 100);
            return data.ToArray();
        }


        /// <summary>
        /// 從圖片擷取部分割區域
        /// </summary>
        /// <param name="fromImagePath">源圖路徑</param>
        /// <param name="offsetX">距上</param>
        /// <param name="offsetY">距左</param>
        /// <param name="width">寬度</param>
        /// <param name="height">高度</param>
        /// <returns></returns>
        public static byte[] Screenshot(string fromImagePath, int offsetX, int offsetY, int width, int height)
        {
            using var original = SKBitmap.Decode(fromImagePath);
            using SKBitmap bitmap = new(width, height);
            using SKCanvas canvas = new(bitmap);
            SKRect sourceRect = new(offsetX, offsetY, offsetX + width, offsetY + height);
            SKRect destRect = new(0, 0, width, height);

            canvas.DrawBitmap(original, sourceRect, destRect);

            using var img = SKImage.FromBitmap(bitmap);
            using SKData p = img.Encode(SKEncodedImageFormat.Png, 100);
            return p.ToArray();
        }


        /// <summary>
        /// 獲取影象數位驗證碼
        /// </summary>
        /// <param name="text">驗證碼內容,如4為數位</param>
        /// <returns></returns>
        public static byte[] GetVerifyCode(string text)
        {

            int width = 128;
            int height = 45;

            Random random = new();

            //建立bitmap點陣圖
            using SKBitmap image = new(width, height, SKColorType.Bgra8888, SKAlphaType.Premul);
            //建立畫筆
            using SKCanvas canvas = new(image);
            //填充背景顏色為白色
            canvas.DrawColor(SKColors.White);

            //畫圖片的背景噪音線
            for (int i = 0; i < (width * height * 0.015); i++)
            {
                using SKPaint drawStyle = new();
                drawStyle.Color = new(Convert.ToUInt32(random.Next(Int32.MaxValue)));

                canvas.DrawLine(random.Next(0, width), random.Next(0, height), random.Next(0, width), random.Next(0, height), drawStyle);
            }

            //將文字寫到畫布上
            using (SKPaint drawStyle = new())
            {
                drawStyle.Color = SKColors.Red;
                drawStyle.TextSize = height;
                drawStyle.StrokeWidth = 1;

                float emHeight = height - (float)height * (float)0.14;
                float emWidth = ((float)width / text.Length) - ((float)width * (float)0.13);

                canvas.DrawText(text, emWidth, emHeight, drawStyle);
            }

            //畫圖片的前景噪音點
            for (int i = 0; i < (width * height * 0.6); i++)
            {
                image.SetPixel(random.Next(0, width), random.Next(0, height), new SKColor(Convert.ToUInt32(random.Next(Int32.MaxValue))));
            }

            using var img = SKImage.FromBitmap(image);
            using SKData p = img.Encode(SKEncodedImageFormat.Png, 100);
            return p.ToArray();
        }

    }

}

專案如果是在 windows 伺服器下執行則不需要任何安裝任何依賴項,如果是在 linux 服務下執行則需要安裝 libfontconfig1,如 ubuntu 的安裝命令

apt-get update
apt-get -y install libfontconfig1

如果是採用 docker 模式執行,則需要在 dockerfile 中新增如下設定,該命令適用於 debian 和 ubuntu 的 docker
RUN apt-get update && apt-get -y install libfontconfig1

至此 .NET 採用 SkiaSharp 生成二維條碼和圖形驗證碼及圖片進行指定區域擷取方法實現 就講解完了,有任何不明白的,可以在文章下面評論或者私信我,歡迎大家積極的討論交流,有興趣的朋友可以關注我目前在維護的一個 .NET 基礎框架專案,專案地址如下
https://github.com/berkerdong/NetEngine.git
https://gitee.com/berkerdong/NetEngine.git