草庐IT

dotnet 控制台 使用 Microsoft.Maui.Graphics 配合 Skia 进行绘图入门

lindexi 2023-03-28 原文

本文将告诉大家如何在 dotnet 的控制台模式下,采用 MAUI 自绘库 Microsoft.Maui.Graphics 进行绘图,设置 Microsoft.Maui.Graphics 底层调用 Microsoft.Maui.Graphics.Skia 库的 Skia 进行具体的绘图实现,此控制台可以跨平台运行,我在本机 Win10 和 WSL 的 Ubuntu 上都运行过,输出的结果图片像素级相似。本文将告诉大家如何采用 Microsoft.Maui.Graphics 进行跨平台的自绘

在开始之前,先理清一下概念。刚正式发布的 MAUI 指的是一个跨平台的 UI 框架,而 dotnet 指的是在 UI 框架下面的运行时,这是早已实现跨平台的了。本文所说的 Microsoft.Maui.Graphics 是属于 MAUI 的一个组件,是 MAUI 的渲染层里面的一个部分。相当于直接使用 Microsoft.Maui.Graphics 就是将 MAUI 的渲染里面的一个模块拆出来独立使用。可以看到 MAUI 的设计上,渲染的一个模块是可以拆处理独立使用的

本文将从一个控制台开始,从比较基础的层面告诉大家如何使用 Microsoft.Maui.Graphics 进行绘图。我采用 Microsoft.Maui.Graphics.Skia 库的 Skia 进行具体的绘图实现,实现将画出的内容存放到本地文件

新建一个控制台项目,我将项目放在 D:\lindexi\Code\SkiaSharp\SkiaSharp\BihuwelcairkiDelalurnere 文件夹里面

按照惯例,安装 Microsoft.Maui.Graphics.Skia 的 NuGet 包。为了可以在 Windows Subsystem for Linux (WSL)适用于 Linux 的 Windows 子系统上的 Ubuntu 上运行,继续添加 SkiaSharp.NativeAssets.Linux.NoDependencies 库,详细请看 dotnet 修复在 Linux 上使用 SkiaSharp 提示找不到 liblibSkiaSharp 库

添加完成库的 csproj 项目文件内容如下

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Maui.Graphics.Skia" Version="6.0.403" />
    <PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.0" />
  </ItemGroup>

</Project>

在 Program.cs 加上命名空间引用

using Microsoft.Maui.Graphics;
using Microsoft.Maui.Graphics.Skia;
using SkiaSharp;

在 Microsoft.Maui.Graphics 里,一切的逻辑都是从 ICanvas 画板开始。这是一个接口,可以采用 SkiaCanvas 来进行实现,代码如下

        var skiaCanvas = new SkiaCanvas();

而 SkiaCanvas 需要有一个具体的 Skia 绘制的画板,也就是 Canvas 属性。接下来开始构建 Skia 的画板,采用的是图片的方式,让 Skia 绘制到图片上

先新建图片的信息

var skImageInfo = new SKImageInfo(1920, 1080, SKColorType.Bgra8888, SKAlphaType.Opaque, SKColorSpace.CreateSrgb());

我喜欢配置颜色采用 Bgra8888 的格式。在通用性上来说,这个 Bgra8888 是 B(Blue蓝色) G(Green绿色) R(Red 红色) 和 A(Alpha透明度) 每个分量各 8 个位的 32 位表示一个像素的格式,由于足够简单,被很多个平台和框架和硬件所支持。尽管 Bgra8888 不是效率最高的方式,但好在简单也方便理解,同时也在多个平台可以方便共用,因此在不确定选什么颜色的时候,默认采用这个格式也是不错的

通过 SKImage.Create 方法创建出图片,这个图片不是只存放磁盘里的图片,而是 Skia 的一个概念

using var skImage = SKImage.Create(skImageInfo);

为了在此 SKImage 上绘制,需要取出 SKBitmap 对象,放入到 SKCanvas 里,代码如下

using (SKBitmap skBitmap = SKBitmap.FromImage(skImage))
{
    using (var skCanvas = new SKCanvas(skBitmap))
    {
    }
}

于是就获取到了 SKCanvas 的对象,可以放入到 SkiaCanvas 里面

using (SKBitmap skBitmap = SKBitmap.FromImage(skImage))
{
    using (var skCanvas = new SKCanvas(skBitmap))
    {
        var skiaCanvas = new SkiaCanvas();
        skiaCanvas.Canvas = skCanvas;
    }
}

如此即可拿到 ICanvas 的对象,这一层就是抽象的,无论具体的底层绘制采用的是什么基础,业务用 ICanvas 类型

        var skiaCanvas = new SkiaCanvas();
        skiaCanvas.Canvas = skCanvas;

        ICanvas canvas = skiaCanvas;

以上就完成了将 Microsoft.Maui.Graphics 的具体绘制底层逻辑更换使用为 Skia 进行绘制。相似的可以替换为采用 WPF 进行绘制,详细请看 WPF 使用 MAUI 的自绘制逻辑

接下来就是尝试画一条线段测试一下

        canvas.StrokeSize = 2;
        canvas.StrokeColor = Colors.Blue;

        canvas.DrawLine(10, 10, 100, 10);

将画出的内容保存到图片文件,就需要回到 SkiaSharp 的逻辑

        var fileName = $"xx.png";

        skCanvas.Flush();

        using (var skData = skBitmap.Encode(SKEncodedImageFormat.Png, 100))
        {
            var file = new FileInfo(fileName);
            using (var fileStream = file.OpenWrite())
            {
                fileStream.SetLength(0);
                skData.SaveTo(fileStream);
            }
        }

完成代码,先在 Windows 上运行一下,可以看到输出了图片如下

接下来进入 WLS 也运行一下代码

输出的图片和在 Windows 上输出的图片文件是完全二进制相同的

更多细节请看 绘制图形对象 - .NET MAUI Microsoft Docs

更多的 MAUI 相关博客,还请参阅我的 博客导航

本文的例子放在githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin d910685120d0a4be91792685ada4bd9c967f6e4a

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin d910685120d0a4be91792685ada4bd9c967f6e4a

获取代码之后,进入 SkiaSharp\BihuwelcairkiDelalurnere 文件夹

我建立了一个 SkiaSharp 的群: 788018852 欢迎大家加入讨论

有关dotnet 控制台 使用 Microsoft.Maui.Graphics 配合 Skia 进行绘图入门的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  5. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  7. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  8. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  9. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  10. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

随机推荐