6

.NET 6 圖形處理跨平台注意事項

 2 years ago
source link: https://blog.darkthread.net/blog/netcore-iamge-processing/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

.NET 6 圖形處理跨平台注意事項

2021-12-06 09:09 PM 0 999

.NET Core/.NET 6 號稱跨平台,但實際推進到 Linux,有些眉角遇上才會知道。

繼續嘗試在 CentOS 整合事務機掃描功能,掃描結果的圖檔格式是 TIFF,想寫段 C# 程式將它轉成 JPG,這用 Bitmap 來做是小事一椿。要在 .NET 6 使用 Bitmap,可安裝 System.Drawing.Common,寫幾行程式搞定:

TIFF 成功轉成 JPG,但出現一些編譯警示,提醒我們 Image.Save()、Bitmap、ImageFormat.Jpeg 這些 API 只能在 Windows 平台使用:

warning CA1416: This call site is reachable on all platforms. 'Image.Save(Stream, ImageFormat)' is only supported on: 'windows'.
warning CA1416: This call site is reachable on all platforms. 'Bitmap' is only supported on: 'windows'.
warning CA1416: This call site is reachable on all platforms. 'ImageFormat.Jpeg' is only supported on: 'windows'.

依據文件 System.Drawing.Common only supported on Windows,System.Drawing.Common 跨平台依賴 libgdiplus,它移植自 Windows 移植,是個三萬行 C 程式碼的程式庫,但功能不完整、測試不足,還依賴 cairo、pango 等原生程式庫,對部署環境要求較多,品質上無法跟其他 .NET 程式庫相比,故 System.Drawing.Common 只被定位成方便 .NET Framework 程式碼升級 .NET Core/.NET 5+ 繼續在 Windows 平台執行,不保證跨平台。從 .NET 6 起,若程式用到 System.Drawing.Common 不支援跨平台功能,將會出現編譯警告,在非 Windows 平台執行則會抛出 TypeInitializationException (InnerException 為 PlatformNotSupportedException):(下圖為文章開頭程式在 Linux 執行的結果)

幸好在 Coding4Fun 時發現,若在正式專案,天真地想把 .NET Framework 既有圖檔產生函式直接搬進 ASP.NET Core Docker, 遇到肯定手忙腳亂。

解決這個問題,治標做法是在 Linux 環境安裝 libdgiplus (參考:DotNetCore跨平台~System.DrawingCore部署Linux需要注意的 by 张占岭),並在 appName.runtimeconfig.json 加入:參考

{
   "runtimeOptions": {
      "configProperties": {
         "System.Drawing.EnableUnixSupport": true
      }
   }
}

不過,非 Windows 平台的 libgdiplus 已被放生,有 Bug 也不再修正,而 EnableUnixSupport 在未來版本可能被取消,非長久之計。因此,官方建議的治本之道是改用其他較可靠的跨平台程式庫,例如:

我這次的處理目標是 TIFF 轉 JPG,研究了一下,ImageSharp 最大優點是純 .NET 打造,不依賴其他原生程式庫,但目前不支援 TIFF,需自行整合額外程式庫(例如:LibTiff.Net),有個加入 TIFF 支援的 PR 已併入 master,但還沒正式發佈;SkiaSharp 基於 Google 的 Skia 2D 繪圖程式庫, 也不支援 TIFF,需自行整合;Magick.NET 基於跨平台的 ImageMagick 程式庫,支援檔案格式高達一百多種,但要在 Linux 平台使用需要額外步驟建立程式庫連結;Microsoft.Maui.Graphics 支援 Xamarin iOS/Android/Mac、WPF、UWP、WinForms、Linux,仍在實驗階段暫無技術支援,不過它重構自使用超過十年的程式碼,有一定可靠度,但它還太新,參考範例跟 API 文件稀少較難上手。

經過評估,我決定用 ImageSharp 的 2.0 Alpha 版,這個版本內建 TIFF 格式支援,用起來最省事。安裝 Alpha 版需指定指號及來源,指令為dotnet add package SixLabors.ImageSharp --version 2.0.0-alpha.0.124 --source https://www.myget.org/F/sixlabors/api/v3/index.json,安裝好 ImageSharp,簡單修改程式:

using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Jpeg;

using (var src = Image.Load("./sample.tiff"))
{
    using (var ms = new MemoryStream())
    {
        src.Save(ms, new JpegEncoder { Quality = 85 });
        File.WriteAllBytes("sample.jpg", ms.ToArray());
    }
}

在 Windows 測試成功後,將程式移到 CentOS 執行,因 Alpha 版來源不同,dotnet 無法自動還原 ImageSharp,需手動跑指定 --source 的 dotnet add package 指令,再遇到 error: NU1101: Unable to find package System.Runtime.CompilerServices.Unsafe. No packages exist with this id in source(s): https://www.myget.org/F/sixlabors/api/v3/index.json 錯誤,試著手動執行 dotnet add package System.Runtime.CompilerServices.Unsafe 從 NuGet 安裝 CompilerServices.Unsafe 後再試一次,錯誤消失,也順利完成轉檔!

一個簡單的用 C# 將 TIFF 轉 JPG 動作,我花了超過六個小時才成功在 Linux 上成功執行,這就是所謂「魔鬼都在細節裡」吧!

【延伸閱讀】


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK