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

 

posted on 2021-11-05 20:22  SunnyTrudeau  阅读(3860)  评论(1编辑  收藏  举报