SkiaSharp跨平台绘图研究1-WPF桌面应用
背景
Skia首页、文档和下载 - Google 图形处理引擎 - OSCHINA - 中文开源技术交流社区
skia是个2D向量图形处理函数库,包含字型、座标转换,以及点阵图都有高效能且简洁的表现。不仅用于Google Chrome浏览器,新兴的Android开放手机平台也采用skia作为绘图处理,搭配OpenGL/ES与特定的硬体特征,强化显示的效果
自2005年Skia被Google收购后,一直相当神秘低调,直到2007年初,Skia GL相关的程式码才被揭露,作为Google Android平台的图形引擎,稍候的Google Chrome浏览器也采用Skia引擎。随着Android与Chrome (开放版本称为"Chromium")两大专案公布程式码后,skia也一并公开原始程式码,以Apache License v2释出(注意,这意味着与GPLv2授权不相容) ,而Android与Chrome的程式码库中都有一份[skia]的复制,因需求不同,做了部份的修改,比方说Chrome专案底下的 [chrome/trunk/src/skia],需要注意的是,Skia本身是不涉及底层环境,如Linux Framebuffer或Gtk+衔接的处理,这也是何以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是一个强大跨平台绘图框架,我曾经用SkiaSharp在WPF、安卓Xamarin.Forms客户端绘图,也用来创建过PDF绘图,但是由于它不支持网页绘图,所以总觉得很遗憾,因为目前主流的浏览器都是谷歌Chrom内核,谷歌为什么不支持自家的Skia在网页直接绘图呢?如果SkiaSharp可以直接在网页绘图,那它就是跨越全平台的绘图框架了。
终于到了2021年10月12日,.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.0的WPF桌面软件,还是用上述代码,运行完全没问题!那么就是开发环境的问题,老司机都知道,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