SkiaSharp跨平台繪圖研究1-WPF桌面應用


SkiaSharp跨平台繪圖研究1-WPF桌面應用

背景

Skia首頁、文檔和下載 - Google 圖形處理引擎 - OSCHINA - 中文開源技術交流社區

skia是個2D向量圖形處理函數庫,包含字型、座標轉換,以及點陣圖都有高效能且簡潔的表現。不僅用於Google Chrome瀏覽器,新興的Android開放手機平台也采用skia作為繪圖處理,搭配OpenGL/ES與特定的硬體特征,強化顯示的效果

 

2005SkiaGoogle收購后,一直相當神秘低調,直到2007年初,Skia GL相關的程式碼才被揭露,作為Google Android平台的圖形引擎,稍候的Google Chrome瀏覽器也采用Skia引擎。隨着AndroidChrome (開放版本稱為"Chromium")兩大專案公布程式碼后,skia也一並公開原始程式碼,以Apache License v2釋出(注意,這意味着與GPLv2授權不相容) ,而AndroidChrome的程式碼庫中都有一份[skia]的復制,因需求不同,做了部份的修改,比方說Chrome專案底下的 [chrome/trunk/src/skia],需要注意的是,Skia本身是不涉及底層環境,如Linux FramebufferGtk+銜接的處理,這也是何以Android (透過Linux Framebuffer)Chrome (開發中的Linux版本使用Gtk+)需要提供一份修改,以便系統接軌。

 

SkiaSharp首頁、文檔和下載 - .NET 平台的跨平台 2D 圖形 API - OSCHINA - 中文開源技術交流社區

SkiaSharp 是基於 Google Skia Graphics Library .NET 平台的跨平台 2D 圖形 API。可用於移動設備,服務器和桌面設備渲染圖像。

SkiaSharp 目前適用於一下平台:

l .NET Standard 1.3

l .NET Core

l Tizen

l Xamarin.Android

l Xamarin.iOS

l Xamarin.tvOS

l Xamarin.watchOS

l Xamarin.Mac

l Windows Classic Desktop (Windows.Forms / WPF)

l Windows UWP (Desktop / Mobile / Xbox / HoloLens)

 

SkiaSharp是一個強大跨平台繪圖框架,我曾經用SkiaSharpWPF、安卓Xamarin.Forms客戶端繪圖,也用來創建過PDF繪圖,但是由於它不支持網頁繪圖,所以總覺得很遺憾,因為目前主流的瀏覽器都是谷歌Chrom內核,谷歌為什么不支持自家的Skia在網頁直接繪圖呢?如果SkiaSharp可以直接在網頁繪圖,那它就是跨越全平台的繪圖框架了。

終於到了20211012日,.NET 6發布RC2候選版本(正式發布前最后一版),宣布了一個突破性的技術:支持在Web網頁上采用SkiaSharp畫布繪圖。這是.NET跨平台技術發展的一個創舉,使用C#可以直接在網頁畫布上繪圖,打破了JavaScript+canvas的長期壟斷地位。C#是強類型語言,可以無縫對接從服務端獲取的結構化數據,有效提高開發效率和質量。

ASP.NET Core updates in .NET 6 Release Candidate 2 - ASP.NET Blog (microsoft.com)

SkiaSharp is a cross-platform 2D graphics library for .NET based on the native Skia graphics library, and it now has preview support for Blazor WebAssembly. Let’s give it a try!

 

受此鼓舞,決定寫一個系列文章,匯總SkiaSharp在各個平台上的繪圖方法。

 

創建WPF項目

使用VS2022 prewview6.0工具,其實對於WPF項目而言,VS2019完全足夠的。

NuGet安裝SkiaSharp.Views.WPF,當前最新版本是2.88.0-preview.152

 

簡單地添加一個按鈕和SkiaSharp畫布容器。

 

<Window x:Class="WpfDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfDemo" 
        xmlns:skia="clr-namespace:SkiaSharp.Views.WPF;assembly=SkiaSharp.Views.WPF"
        mc:Ignorable="d"
        Title="MainWindow" Height="400" Width="450">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Button x:Name="btnRefresh" Content="繪圖" Grid.Row="0" Width="100" Margin="4"></Button>
        <Grid Grid.Row="1" Margin="4" Height="300">
            <skia:SKElement x:Name="skContainer">
            </skia:SKElement>
        </Grid>
    </Grid>
</Window>

 

編寫一點代碼

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            btnRefresh.Click += BtnRefresh_Click;

            skContainer.PaintSurface += SkContainer_PaintSurface;
        }

        private void BtnRefresh_Click(object sender, RoutedEventArgs e)
        {
            Debug.WriteLine($"{DateTimeOffset.Now}, 刷新畫布");

            //強制畫布重繪,有些開發環境InvalidateVisual不會觸發調用OnRender
            skContainer.InvalidateVisual();
        }

        private void SkContainer_PaintSurface(object? sender, SkiaSharp.Views.Desktop.SKPaintSurfaceEventArgs e)
        {
            var canvas = e.Surface.Canvas;

            canvas.Clear(SKColors.SkyBlue);

            using var paint = new SKPaint
            {
                Color = SKColors.Black,
                IsAntialias = true,
                TextSize = 24
            };

            string msg = $"{DateTimeOffset.Now:T}, 還有1萬行Skia繪圖代碼...";
            canvas.DrawText(msg, 0, 30, paint);

            using var linePaint = new SKPaint()
            {
                Color = (DateTimeOffset.Now.Second % 4 <= 1) ? SKColors.Red : SKColors.Green,
                Style = SKPaintStyle.Stroke,//不填充
                StrokeWidth = 3,
            };
            canvas.DrawRect(10, 50, e.Info.Width - 20, e.Info.Height - 60, linePaint);

            msg += $", linePaint.Color={linePaint.Color}, skContainer.CanvasSize={skContainer.CanvasSize}";
            Debug.WriteLine(msg);

        }
    }

測試SkiaSharp繪圖

測試發現,點擊繪圖按鈕,畫布內容並沒有更新!我以前用得好好的,怎么突然就不行了?在另外一台電腦上用VS2019創建了一個net 5.0WPF桌面軟件,還是用上述代碼,運行完全沒問題!那么就是開發環境的問題,老司機都知道,Windows的開發環境對桌面軟件有一些匪夷所思的影響,這也是微軟桌面開發環境帶給.Net開發者多年的痛苦。

 

運行效果正常的開發環境

dotnet --info

.NET SDK (反映任何 global.json):

 Version:   5.0.300

 Commit:    2e0c8c940e

 

運行時環境:

 OS Name:     Windows

 OS Version:  10.0.18363

 OS Platform: Windows

 RID:         win10-x64

 Base Path:   C:\Program Files\dotnet\sdk\5.0.300\

 

Host (useful for support):

  Version: 5.0.6

  Commit:  478b2f8c0e

 

.NET SDKs installed:

  2.2.402 [C:\Program Files\dotnet\sdk]

  5.0.102 [C:\Program Files\dotnet\sdk]

  5.0.300 [C:\Program Files\dotnet\sdk]

 

.NET runtimes installed:

  Microsoft.AspNetCore.All 2.1.28 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]

  Microsoft.AspNetCore.All 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]

  Microsoft.AspNetCore.App 2.1.28 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]

  Microsoft.AspNetCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]

  Microsoft.AspNetCore.App 3.1.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]

  Microsoft.AspNetCore.App 5.0.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]

  Microsoft.NETCore.App 2.1.28 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]

  Microsoft.NETCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]

  Microsoft.NETCore.App 3.1.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]

  Microsoft.NETCore.App 5.0.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]

  Microsoft.WindowsDesktop.App 3.1.15 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

  Microsoft.WindowsDesktop.App 5.0.6 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

 

To install additional .NET runtimes or SDKs:

  https://aka.ms/dotnet-download

 

運行效果異常的開發環境

dotnet --info

.NET SDK (反映任何 global.json):

 Version:   6.0.100-rc.2.21505.57

 Commit:    ab39070116

 

運行時環境:

 OS Name:     Windows

 OS Version:  10.0.19042

 OS Platform: Windows

 RID:         win10-x64

 Base Path:   C:\Program Files\dotnet\sdk\6.0.100-rc.2.21505.57\

 

Host (useful for support):

  Version: 6.0.0-rc.2.21480.5

  Commit:  6b11d64e7e

 

.NET SDKs installed:

  5.0.402 [C:\Program Files\dotnet\sdk]

  6.0.100-rc.2.21505.57 [C:\Program Files\dotnet\sdk]

 

.NET runtimes installed:

  Microsoft.AspNetCore.App 3.1.20 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]

  Microsoft.AspNetCore.App 5.0.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]

  Microsoft.AspNetCore.App 6.0.0-rc.2.21480.10 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]

  Microsoft.NETCore.App 3.1.20 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]

  Microsoft.NETCore.App 5.0.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]

  Microsoft.NETCore.App 6.0.0-rc.2.21480.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]

  Microsoft.WindowsDesktop.App 3.1.20 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

  Microsoft.WindowsDesktop.App 5.0.11 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

  Microsoft.WindowsDesktop.App 6.0.0-rc.2.21501.6 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

 

To install additional .NET runtimes or SDKs:

  https://aka.ms/dotnet-download

 

對於異常的開發環境,可以通過改變窗口大小,強制引發重繪,看到畫布內容刷新。

 

解決中文顯示問題

SkiaSharp要創建中文字體,才能顯示中文,這也是個麻煩,為什么不能自動從操作系統獲取Unicode字庫呢?為了減少安裝包體積,選用體積小的DroidSansFallback.ttf字庫文件。

 

/// <summary>
    /// Skia中文字體
    /// </summary>
    public static class SkiaChinaFont
    {
        public static SKTypeface ChinaFont { get; private set; }

        static SkiaChinaFont()
        {
            //加載字體資源文件方案,需要把字體文件復制運行目錄下,設置文件屬性為如果較新則復制
            string fontPath = Path.Combine(AppContext.BaseDirectory, "DroidSansFallback.ttf");
            ChinaFont = SKTypeface.FromFile(fontPath);
        }
    }

 

顯示文字代碼增加中文字體即可

using var paint = new SKPaint
            {
                Color = SKColors.Black,
                IsAntialias = true,
                Typeface = SkiaChinaFont.ChinaFont,
                TextSize = 24
            };

            string msg = $"{DateTimeOffset.Now:T}, 還有1萬行Skia繪圖代碼...";
            canvas.DrawText(msg, 0, 30, paint);

最終結果如圖

 

DEMO源代碼參見:https://gitee.com/woodsun/skia-sharp-demo

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM