【Win2D】【译】Win2D 快速入门

时间:2022-02-22 00:30:03

原文链接:http://microsoft.github.io/Win2D/html/QuickStart.htm

快速入门

这是 Win2D 的快速入门教程,将会介绍 Win2D 中的基本功能。你将会在此教程中学到:

• 添加 Win2D 到 C# XAML 项目

• 绘制文本和几何图形

• 应用滤镜效果

• 对 Win2D 内容进行动画效果

• 根据 Win2D 进行最佳实现

安装 Visual Studio

• 如果你还没有安装受支持的 Visual Studio 版本,请按照该步骤:快速开始

创建一个带有 Win2D 的新项目

1、运行 Visual Studio 并创建新项目:文件→新建→项目。

2、如果你的目标是 Windows 10,选择:已安装→模板→Visual C#→Windows→通用→空白应用(通用 Windows)。

如果你的目标是 Windows 8.1 且使用 Visual Studio 2015,选择:已安装→模板→Visual C#→Windows 8→通用→空白应用(通用 Windows 8.1)。

如果你的目标是 Windows 8.1 且使用 Visual Studio 2013,选择:已安装→模板→Visual C#→Windows 8.1→空白应用。

3、输入项目名称,选择项目存放路径,然后点击确定按钮创建。

4、Win2D 目前是发布为一个 nuget 包,你需要安装它才能够使用。这里有两个包,一个是 Windows 10 的,另一个是 Windows 8.1 的。选择:工具→Nuget 包管理器→管理解决方案的 Nuget 程序包。

5、搜索 Win2D,然后选择对应的包。

如果目标是 Windows 10,选择 Win2D.uwp。

如果目标是 Windows 8.1,选择 Win2D.win81。

最后,点击安装。

添加 Win2D 的 CanvasControl 到你的 App 的 Xaml 文件中

1、要使用 Win2D,你需要一个地方来绘制你的图形。在 Xaml App 中,最简单的方法就是添加一个 CanvasControl 到你的 Xaml 页面。

在这之前,请先确保你的项目的解决方案平台是 x86 或者 x64,而不是 Any CPU。因为 Win2D 是基于 C++ 实现的,因此使用到 Win2D 的项目都必须指定解决方案平台。

2、在解决方案资源管理器中双击 MainPage.xaml 来打开这个页面。

3、在使用 CanvasControl 之前,你必须得告诉 Xaml,这个控件是哪里定义的。在页面定义中,添加以下这句:

xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"

然后你的 Xaml 应该是这样的:

<Page
...
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"
mc:Ignorable="d">

4、现在,添加 canvas:CanvasControl 到 Grid 元素中,并且给它一个名字,这里我们就叫“canvas”。现在你的 Xaml 应该就变成这样了:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<canvas:CanvasControl x:Name="canvas"/>
</Grid>

5、接下来,定义 Draw 事件。CnavasControl 将会在需要绘制或者重绘的时候调用 Draw 事件。

<canvas:CanvasControl x:Name="canvas" Draw="canvas_Draw" />

使用 Win2D 绘制你的第一条文本

1、在解决方案资源管理器中打开 MainPage.xaml.cs。

2、在 C# 文件顶部添加以下命名空间引用:

using Windows.UI;
using System.Numerics;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Effects;

3、确保添加了 canvas_Draw 方法:

private void canvas_Draw(
Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender,
Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args)
{}

4、CanvasDrawEventArgs 参数中有一个叫做 DrawingSession 的成员,并且类型是CanvasDrawingSession。这个类型提供了 Win2D 中绝大部分的绘制功能。它有一堆名字叫 CanvasDrawingSession.DrawXXX 的方法,例如 CanvasDrawingSession.DrawRectangleCanvasDrawingSession.DrawImage 和我们现在需要用的 CanvasDrawingSession.DrawText。

添加这行代码到 canvas_Draw 方法中:

args.DrawingSession.DrawText("Hello, World!", , , Colors.Black);

第一个参数 "Hello, World!" 是你想 Win2D 显示的字符串。接下来两个 100 是指,在 100 DIPs 下,距离容器左边和顶部的距离。最后一个参数是这个文本显示的颜色。

5、现在可以运行你的 App 了。按 F5 编译并运行。你应该可以看见一个空白的窗口上显示着黑色的 Hello, World! 了。

正确释放 Win2D 所使用的资源

1、在你打算继续绘制其它内容之前,你应该先加入一些代码来避免内存泄漏。绝大部分使用 .Net 语言编写的 Win2D 程序和使用了 Win2D 中的控件(例如 CanvasControl)都需要添加以下步骤。严格来说,像上面那个 Hello World 不会产生影响,但是,遵循常规是一个好习惯。

关于内存泄漏的更多信息,可以点击此链接:http://microsoft.github.io/Win2D/html/RefCycles.htm

2、打开 MainPage.xaml 并找到包含你那个 CanvasControl 的 Page 元素。

3、在 Page 元素中订阅 Unloaded 事件,Xaml 代码变成这样:

<Page
...
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"
mc:Ignorable="d"
Unloaded="Page_Unloaded">

4、转到 MainPage.xaml.cs 文件,并在 Page_Unloaded 添加以下代码:

void Page_Unloaded(object sender, RoutedEventArgs e)
{
this.canvas.RemoveFromVisualTree();
this.canvas = null;
}

5、如果你的 App 包含多个 Win2D 控件,那么你需要对每一个 Win2D 控件重复上面步骤。因为现在你的 App 就只有这么一个 CanvasControl,所以现在这样子就已经完成内存释放了。

绘制一些图形

1、在你的 App 中添加一些 2D 几何形状是很简单的。在 canvas_Draw 方法的最后添加以下代码:

args.DrawingSession.DrawCircle(, , , Colors.Green);
args.DrawingSession.DrawLine(, , , , Colors.Red);

这两个方法的参数类似于 DrawText 方法。第一行绘制了一个中心点为(125,125),半径为 100 ,圆周颜色为绿色的圆。第二行绘制了一条从(0,0)到(50,200)的红色直线。

2、按 F5 编译并运行,你应该会看见 Hello,World! 附近有一个绿色边的圆和红色的线。

你可能想知道如何使用更高级的绘制方式,例如控制线的粗细、样式,或者使用刷子来填充图案这些更加复杂的操作。Win2D 提供了所有的选项来方便你去实现你所想的功能。所有 DrawXXX 方法都提供了多个重载来接受额外的参数,例如 CanvasTextFormat(字体、文字大小等等)类型的参数和 CanvasStrokeStyle(点、线的开始结束等等)类型的参数。

你可以通过查看 API 来了解这些参数。

动态生成绘制参数

1、现在,我们添加一点变化来绘制随机颜色的图形和文字。

添加以下代码到 MainPage.xaml.cs 中。这会帮助你在绘制的时候产生随机的值。

Random rnd = new Random();
private Vector2 RndPosition()
{
double x = rnd.NextDouble() * 500f;
double y = rnd.NextDouble() * 500f;
return new Vector2((float)x, (float)y);
} private float RndRadius()
{
return (float)rnd.NextDouble() * 150f;
} private byte RndByte()
{
return (byte)rnd.Next();
}

2、修改 canvas_Draw 方法中的代码并使用随机参数。

private void canvas_Draw(
Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender,
Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args)
{
args.DrawingSession.DrawText("Hello, World!", RndPosition(), Color.FromArgb(, RndByte(), RndByte(), RndByte()));
args.DrawingSession.DrawCircle(RndPosition(), RndRadius(), Color.FromArgb(, RndByte(), RndByte(), RndByte()));
args.DrawingSession.DrawLine(RndPosition(), RndPosition(), Color.FromArgb(, RndByte(), RndByte(), RndByte()));
}

让我们暂停下来看看 DrawText 方法有什么变化。第一个参数“Hello, World!”的意思跟之前的一样不变。x 和 y 位置参数变为了一个由 RndPosition 生成的 Vector2 对象。最后,使用 Color.FromArgb 方法并传递 A,R,G,B 来定义一个颜色。A 是 Alpha 通道的值,也就是颜色的不透明度。在这个例子中我们始终使用完全不透明,也就是 255。

DrawCircle 和 DrawLine 的操作类似于 DrawText,这里就不详细说明了。

3、最后,用个循环将它包起来。canvas_Draw 应该以这些代码结束:

for (int i = ; i < ; i++)
{
args.DrawingSession.DrawText("Hello, World!", RndPosition(), Color.FromArgb(, RndByte(), RndByte(), RndByte()));
args.DrawingSession.DrawCircle(RndPosition(), RndRadius(), Color.FromArgb(, RndByte(), RndByte(), RndByte()));
args.DrawingSession.DrawLine(RndPosition(), RndPosition(), Color.FromArgb(, RndByte(), RndByte(), RndByte()));
}

4、再次运行程序,你应该会看见一大堆随机位置、随机大小的文字、线条和圆圈。

对内容应用图像效果

1、图像效果,也称滤镜效果,是像素级别的图形变换。饱和度、色相模糊和高斯模糊是一些常见的图像效果。图像效果可以串联在一起应用,这样就可以以最小的代价来实现复杂的视觉效果。

你可以通过提供一幅源图像(你的初始内容),创建一个特效(例如 GaussianBlurEffect)并设置其属性(例如 BlurAmount),然后调用 DrawImage 方法来绘制出特效处理过的图像。

如果你要对文字和图形应用图像效果,你需要先将内容呈现到一个 CanvasCommandList 对象中。这个对象可作为效果的输入。

2、修改 canvas_Draw 方法的代码为如下:

CanvasCommandList cl = new CanvasCommandList(sender);
using (CanvasDrawingSession clds = cl.CreateDrawingSession())
{
for (int i = ; i < ; i++)
{
clds.DrawText("Hello, World!", RndPosition(), Color.FromArgb(, RndByte(), RndByte(), RndByte()));
clds.DrawCircle(RndPosition(), RndRadius(), Color.FromArgb(, RndByte(), RndByte(), RndByte()));
clds.DrawLine(RndPosition(), RndPosition(), Color.FromArgb(, RndByte(), RndByte(), RndByte()));
}
}

就像你如何从 CanvasDrawEventArgs 对象中获得一个 CanvasDrawingSession 一样,你从 CanvasCommandList 中也能得到一个 CanvasDrawingSession 对象。唯一的区别是,你只是将绘图命令添加到绘图任务当中,而不是直接渲染到 CanvasControl 上面。反过来说,命令列表是一个存放了渲染命令的中间对象,供给以后实际渲染时使用。

你也许会注意我们使用了 using 来封装绘图命令。DrawingSession 是一个实现了 IDisposable 接口的对象,并且必须在已经完成绘制的时候释放掉。注意:从 CanvasDrawEventArgs 获取的 CanvasDrawingSession 会自动释放,并不需要你手动去释放它。但是,你自己创建的 CanvasDrawingSession 必须要自己手动释放。

3、最后,定义 GaussianBlurEffect 并添加下面代码到 canvas_Draw 方法的最后。

GaussianBlurEffect blur = new GaussianBlurEffect();
blur.Source = cl;
blur.BlurAmount = 10.0f;
args.DrawingSession.DrawImage(blur);

这段代码创建了一个高斯模糊,并设置源为刚刚绘制的 CanvasCommandList 对象,设置模糊半径为 10,最后交由原始的 DrawingSession 来渲染输出。

4、再次运行程序,你会看见你的直线、文字、圆圈都带有模糊效果了。

使用 CanvasAnimatedControl 来使你的 App 充满动画

1、Win2D 允许你在运行时实时更新内容或者应用动画效果。举个例子,每一帧改变高斯模糊的半径。要实现这个功能,你需要使用 CanvasAnimatedControl

CanvasControl 适用于静态内容,它仅仅在需要更新或者重绘的时候触发 Draw 事件。如果你的内容是不断变化的,那么你应该使用 CanvasAnimatedControl。这两个控件的使用上相当类似,除了 CanvasAnimatedControl 会定期触发 Draw 事件。在一般情况下,每秒会触发 60 次 Draw 事件。

2、现在我们开始转为使用 CanvasAnimatedControl,转到 MainPage.xaml,删除之前的代码,并修改成这样:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<canvas:CanvasAnimatedControl x:Name="canvas" Draw="canvas_DrawAnimated" CreateResources="canvas_CreateResources"/>
</Grid>

类似于 CanvasControl,订阅上 Draw 事件(方法名为 canvas_DrawAnimated)。另外,你也应该订阅一个名字叫做 CreateResources 的事件。

现在,你的程序将会以每秒 60 帧的方式进行渲染。对于那些只需创建一次,能每帧复用的资源,我们应该尽可以将它优化,不要在每一帧都创建一次。CreateResources 事件仅当 Win2D 认为需要重新创建资源的时候才会触发,例如在页面加载的时候。

3、回到 MainPage.xaml.cs,你之前的代码应该是这样的:

private void canvas_Draw(
Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender,
Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args)
{
CanvasCommandList cl = new CanvasCommandList(sender);
using (CanvasDrawingSession clds = cl.CreateDrawingSession())
{
for (int i = ; i < ; i++)
{
clds.DrawText("Hello, World!", RndPosition(), Color.FromArgb(, RndByte(), RndByte(), RndByte()));
clds.DrawCircle(RndPosition(), RndRadius(), Color.FromArgb(, RndByte(), RndByte(), RndByte()));
clds.DrawLine(RndPosition(), RndPosition(), Color.FromArgb(, RndByte(), RndByte(), RndByte()));
}
} GaussianBlurEffect blur = new GaussianBlurEffect();
blur.Source = cl;
blur.BlurAmount = 10.0f;
args.DrawingSession.DrawImage(blur);
}

现在这段代码大部分的地方并不需要每一帧都执行,绘制文本、直线和圆圈的命令列表是不变的,需要变的只是高斯模糊的半径,因此我们将“静态”的那部分代码移动到 CreateResources 方法当中。

使用 Ctrl + X 将除了最后一行(args.DrawingSession.DrawImage(blur);)以外的代码剪切掉。然后可以删除掉 canvas_Draw 这个方法了,因为 CanvasAnimatedControl 将使用 canvas_AnimatedDraw 方法。

4、找到 canvas_CreateResources 方法:

private void canvas_CreateResources(
Microsoft.Graphics.Canvas.UI.Xaml.CanvasAnimatedControl sender,
Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesEventArgs args)
{}

按 Ctrl + V 粘贴上之前剪切的代码。接下来,将 GaussianBlurEffect 的定义放到方法体外面,使其成为 MainPage 类的一个成员。现在,你的代码应该是这样的:

GaussianBlurEffect blur;
private void canvas_CreateResources(
Microsoft.Graphics.Canvas.UI.Xaml.CanvasAnimatedControl sender,
Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesEventArgs args)
{
CanvasCommandList cl = new CanvasCommandList(sender);
using (CanvasDrawingSession clds = cl.CreateDrawingSession())
{
for (int i = ; i < ; i++)
{
clds.DrawText("Hello, World!", RndPosition(), Color.FromArgb(, RndByte(), RndByte(), RndByte()));
clds.DrawCircle(RndPosition(), RndRadius(), Color.FromArgb(, RndByte(), RndByte(), RndByte()));
clds.DrawLine(RndPosition(), RndPosition(), Color.FromArgb(, RndByte(), RndByte(), RndByte()));
}
} blur = new GaussianBlurEffect()
{
Source = cl,
BlurAmount = 10.0f
};
}

5、现在,你可以动画化高斯模糊了,找到 canvas_AnimatedDraw 方法,然后加入下面的代码:

private void canvas_AnimatedDraw(
Microsoft.Graphics.Canvas.UI.Xaml.ICanvasAnimatedControl sender,
Microsoft.Graphics.Canvas.UI.Xaml.CanvasAnimatedDrawEventArgs args)
{
float radius = (float)( + Math.Sin(args.Timing.TotalTime.TotalSeconds)) * 10f;
blur.BlurAmount = radius;
args.DrawingSession.DrawImage(blur);
}

这里从 CanvasAnimatedDrawEventArgs 参数中读取了已经运行时间,并以此来计算高斯模糊的半径。使用正弦函数来表现一个根据时间周期变化的效果。最后,当然是由 GaussianBlurEffect 来呈现。

6、运行程序,你会看到随着时间变化,内容的模糊程度也在变化。

恭喜你完成了这个快速入门教程!希望你已经知道如何通过短短几行代码来创造一个丰富生动的视图。

PS:本文是渣译,不喜勿喷;如有帮助,请点推荐。谢谢!