原文:2019-7-3-WPF-使用-Win2d-渲染

title author date CreateTime categories
WPF 使用 Win2d 渲染
lindexi
2019-07-03 16:27:59 +0800
2019-7-3 10:31:2 +0800
WPF win2d

在当前所有渲染框架里面,做 2D 渲染的,最好的框架是 Win2d 这个提供了大量底层接口封装,不仅性能高同时接口设计非常好
在很久之前,只有在 UWP 等现代应用才能使用 Win2d 而 WPF 是不能使用的。好在微软开放了一些黑科技,可以在 WPF 上使用 Win2d 渲染,下面就让我告诉大家如何在 WPF 上使用

在 2019年7月03日 这个技术还是属于黑科技,还没有正式发布,在开始使用之前,有一定的环境要求

  • 需要 VisualStudio 2017 和以上,点击Visual Studio下载链接下载最新的工具
  • 需要在 VisualStudio 的开发添加 UWP 和桌面开发
  • 需要在设备安装 .NET Framework 4.7.2 及以上,同时新建项目需要选择 .NET Framework 4.7.2 和以上。有小伙伴说 45 也是可以的,但是步骤会更多,建议小伙伴在看完本文测试过了才尝试降级
  • 开发和运行设备是 Windows 10 版本 1903 和以上
  • 开发设备上安装 UWP 的 SDK 版本是 18362 及以上 Windows 10 SDK - Windows 应用开发

新建一个 WPF 的 .NET Framework 4.8 项目,在这个项目上面安装下面 NuGet 包

  • Microsoft.Windows.SDK.Contracts
  • Microsoft.VCRTForwarders.140
  • System.Numerics.Vectors
  • Win2D.uwp

如果有安装不上的,例如有下面提示

无法安装程序包“Win2D.uwp 1.23.0”。你正在尝试将此程序包安装到目标为“.NETFramework,Version=v4.8”的项目中,但该程序包不包含任何与该框架兼容的程序集引用或内容文件。有关详细信息,请联系程序包作者。

右击编辑 csproj 文件,在 csproj 文件里面添加下面代码。注意此时你新建的项目使用的 nuget 格式需要新的 Nuget 格式,也就是不带 package.config 文件的格式

    <PackageReference Include="Microsoft.VCRTForwarders.140">
<Version>1.0.0-rc</Version>
</PackageReference>
<PackageReference Include="Microsoft.Windows.SDK.Contracts">
<Version>10.0.18362.2002-preview</Version>
</PackageReference>
<PackageReference Include="System.Numerics.Vectors">
<Version>4.5.0</Version>
</PackageReference>
<PackageReference Include="Win2D.uwp">
<Version>1.22.0</Version>
</PackageReference>

这样就可以强制安装

准备好了环境和 NuGet 之后就可以开始开发了

和之前博客 WPF 使用 Composition API 做高性能渲染 所说方法搭建代码

搭建出 CompositionHost 和 CompositionHostControl 两个类,然后在 CompositionHostControl 里面添加 Win2d 相关方法

在 CompositionHostControl 的构造函数给 Win2d 初始化

        private readonly CanvasDevice _canvasDevice;

        public CompositionHostControl()
{
InitializeComponent(); // 手动高亮,下面代码用于创建 Win2d 的渲染设备
_canvasDevice = CanvasDevice.GetSharedDevice(); // 忽略其他代码
}

以上忽略代码可以在 Windows.UI.Composition-Win32-Samples 找到

CompositionHostControl_Loaded 事件里面才是核心代码

想要让 Win2d 绘制在界面上,需要在界面存放一张平面,让 Win2d 在这个平面上绘制,然后将平面加入到渲染的平面列表里面,这样就可以进行渲染了

// 创建链接 _compositor 和 _canvasDevice 的链接
_compositionGraphicsDevice = CanvasComposition.CreateCompositionGraphicsDevice(_compositor, _canvasDevice); // 创建一个平面,将会让 Win2d 在这个平面渲染
var noiseDrawingSurface = _compositionGraphicsDevice.CreateDrawingSurface(
new Windows.Foundation.Size(_rectWidth, _rectHeight),
DirectXPixelFormat.B8G8R8A8UIntNormalized,
DirectXAlphaMode.Premultiplied);
// 忽略一些代码 // 在这个函数里面就是让 Win2d 绘制的方法,其实在我自己的项目里面没有用到 noiseFilePath 变量
LoadSurface(noiseDrawingSurface, noiseFilePath); // 在 Win2d 渲染到平面完成之后,将这个平面作为一个画刷用于在之后的效果
_noiseSurfaceBrush = _compositor.CreateSurfaceBrush(noiseDrawingSurface);

在 LoadSurface 方法里面进行绘制,请看创建代码

        void LoadSurface(CompositionDrawingSurface surface, string path)
{
// Draw to surface.
using (var ds = CanvasComposition.CreateDrawingSession(surface))
{
ds.Clear(Colors.Transparent); var rect = new Windows.Foundation.Rect(0, 0, _rectWidth, _rectHeight);
ds.FillRectangle(rect, Colors.Coral);
}
}

这样就可以将 Win2d 的内容渲染到一个平面上,然后将这个平面作为画刷,在微软的代码里面是将这个画刷作为亚克力的画刷,然后将亚克力放在内容里面

下面是简化的代码

LoadSurface(noiseDrawingSurface, noiseFilePath);
var visual = _compositor.CreateSpriteVisual();
// 忽略 visual 的参数设置
_acrylicVisual = visual;
// 创建亚克力画刷
_acrylicVisual.Brush = CreateAcrylicEffectBrush(); // 加入到图层
_containerVisual.Children.InsertAtTop(_acrylicVisual);

现在就完成了在 WPF 里面使用 Win2d 顺便还提供了亚克力的功能

其实本文主要不是告诉大家如何写代码,而是如何让官方的代码可以运行

在运行过程可能会遇到以下的坑

如在开始编译的时候提示下面代码

C:\Users\lindexi.github.io\.nuget\packages\win2d.uwp\1.23.0\build\Win2D.common.targets(37,5): Error: This version of Win2D requires Windows SDK >= 10.0.17134.0, but TargetPlatformVersion is 7.0.

主要原因是现在 Microsoft.Windows.SDK.Contracts 只能让 Win2d 的 1.22 版本运行,解决方案是将原本的 1.23 版本降级到 1.22 版本

在运行过程,运行到 _canvasDevice = CanvasDevice.GetSharedDevice() 的时候提示下面的代码

System.TypeLoadException:“所请求的 Windows 运行时类型“Microsoft.Graphics.Canvas.CanvasDevice”未注册。”

这个问题是因为没有在 app.manifest 文件里面添加下面代码

<file name="Microsoft.Graphics.Canvas.dll">
<activatableClass
name="Microsoft.Graphics.Canvas.CanvasDevice"
threadingModel="both"
xmlns="urn:schemas-microsoft-com:winrt.v1"/> <activatableClass name="Microsoft.Graphics.Canvas.UI.Composition.CanvasComposition"
threadingModel="both"
xmlns="urn:schemas-microsoft-com:winrt.v1"/> <activatableClass
name="Microsoft.Graphics.Canvas.Effects.SaturationEffect"
threadingModel="both"
xmlns="urn:schemas-microsoft-com:winrt.v1"/> <activatableClass
name="Microsoft.Graphics.Canvas.Effects.BlendEffect"
threadingModel="both"
xmlns="urn:schemas-microsoft-com:winrt.v1"/> <activatableClass
name="Microsoft.Graphics.Canvas.Effects.GaussianBlurEffect"
threadingModel="both"
xmlns="urn:schemas-microsoft-com:winrt.v1"/> <activatableClass
name="Microsoft.Graphics.Canvas.Effects.ColorSourceEffect"
threadingModel="both"
xmlns="urn:schemas-microsoft-com:winrt.v1"/> <activatableClass
name="Microsoft.Graphics.Canvas.Effects.CompositeEffect"
threadingModel="both"
xmlns="urn:schemas-microsoft-com:winrt.v1"/> <activatableClass
name="Microsoft.Graphics.Canvas.Effects.OpacityEffect"
threadingModel="both"
xmlns="urn:schemas-microsoft-com:winrt.v1"/> <activatableClass
name="Microsoft.Graphics.Canvas.CanvasBitmap"
threadingModel="both"
xmlns="urn:schemas-microsoft-com:winrt.v1"/>
<activatableClass
name="Microsoft.Graphics.Canvas.Effects.BorderEffect"
threadingModel="both"
xmlns="urn:schemas-microsoft-com:winrt.v1"/>
</file>

或者这个文件没有在 csproj 声明,也就是没有在项目文件找到下面代码

  <PropertyGroup>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>

在运行的时候,还是在 _canvasDevice = CanvasDevice.GetSharedDevice() 提示下面的代码

System.IO.FileNotFoundException:“找不到指定的模块。 (异常来自 HRESULT:0x8007007E)”

这个问题是在输出文件夹缺少文件,请确定当前使用的是 x64 的设备,同时进行 AnyCpu 编译,没有勾选首选 32 的程序

然后确定在 bin\debug 文件夹里面是否存在以下文件

  • vcruntime140_app.dll
  • msvcp140_app.dll
  • Microsoft.Graphics.Canvas.dll
  • System.Numerics.Vectors.dll
  • Microsoft.Graphics.Canvas.winmd

在安装 Microsoft.VCRTForwarders.140 默认就会创建前面两个文件,在进行x64编译的时候。如果使用的是 AnyCpu 编译,那么就需要手动拷贝文件

而 Microsoft.Graphics.Canvas.dll 文件是需要手动拷贝的

手动复制文件的方法是打开自己的本地 .nuget 源,在 c:\Users\用户名\.nuget\packages\ 里面可以找到

如我的 Microsoft.VCRTForwarders.140 内容在 c:\Users\lindexi.github.io\.nuget\packages\microsoft.vcrtforwarders.140\1.0.0-rc\runtimes\win10-x64\native\release\ 文件,将里面的文件复制到输出文件夹

打开 c:\Users\lindexi.github.io\.nuget\packages\win2d.uwp\1.22.0\runtimes\win10-x64\native\ 将 Microsoft.Graphics.Canvas.dll 文件复制到输出文件夹

请将上面文件夹的用户名替换为你自己的用户名

当然这样的呆魔大家一定不想使用,于是我将需要添加的文件放在项目文件,设置自动输出,请小伙伴换我的项目试试

我将代码放在 Github 只需要下载代码,然后打开 sln 文件,右击还原项目,然后在 AnyCpu 下按下运行就可以

2019-7-3-WPF-使用-Win2d-渲染的更多相关文章

  1. dotnet 读 WPF 源代码笔记 渲染收集是如何触发

    在 WPF 里面,渲染可以从架构上划分为两层.上层是 WPF 框架的 OnRender 之类的函数,作用是收集应用程序渲染的命令.上层将收集到的应用程序绘制渲染的命令传给下层,下层是 WPF 的 GF ...

  2. 2019-11-29-WPF-使用-Win2d-渲染

    原文:2019-11-29-WPF-使用-Win2d-渲染 title author date CreateTime categories WPF 使用 Win2d 渲染 lindexi 2019-1 ...

  3. WPF 渲染级别 (Tier)

    在WPF中,显卡的功能相差很大.当WPF评估显卡时,它会考虑许多因素,包括显卡上的RAM数量.对像素着色器(piexl shader)的支持(计算每个像素效果的内置程序,如透明效果),以及对顶点着色器 ...

  4. WPF 渲染原理

    原文:WPF 渲染原理 在 WPF 最主要的就是渲染,因为 WPF 是一个界面框架.想用一篇博客就能告诉大家完整的 WPF 渲染原理是不可能的.本文告诉大家 WPF 从开发者告诉如何画图像到在屏幕显示 ...

  5. WPF 使用 SharpDx 异步渲染

    本文告诉大家如何通过 SharpDx 进行异步渲染,但是因为在 WPF 是需要使用 D3DImage 画出来,所以渲染只是画出图片,最后的显示还是需要 WPF 在他自己的主线程渲染 本文是一个系列,希 ...

  6. WPF 设置纯软件渲染

    最近看到有小伙伴说 WPF 使用硬件渲染,如何让 WPF 不使用硬件渲染,因为他觉得性能太好了.万一这个版本发布了,产品经理说下个版本要提升性能就不好了.于是就找到一个快速的方法,让程序不使用硬件渲染 ...

  7. dotnet 读 WPF 源代码笔记 布局时 Arrange 如何影响元素渲染坐标

    大家是否好奇,在 WPF 里面,对 UIElement 重写 OnRender 方法进行渲染的内容,是如何受到上层容器控件的布局而进行坐标偏移.如有两个放入到 StackPanel 的自定义 UIEl ...

  8. WPF 使用 Silk.NET 进行 DirectX 渲染入门

    本文告诉大家如何使用 dotnet 基金会新开源的 Silk.NET 库调用 DirectX 进行渲染的方法.此库是对 DirectX 的底层基础封装,用上了 dotnet 和 C# 的各个新特性,相 ...

  9. 用 UI 多线程处理 WPF 大量渲染的解决方案

    众所周知, WPF 的 UI 渲染是单线程的,所以如果我们异步或者新建线程去进行数据处理的时候,处理完,想要更新 UI 的时候,需要调用一下 Dispatcher.Invoke,将处理完的数据推入到 ...

随机推荐

  1. jmeter 中使用正则表达式提取依赖参数

    1:登录接口 这里有一个实际的登录接口,在响应中返回了一串token,如下图 那么我们在接下来的接口-经验库列表中,就必须带入这一串token,否则响应报错,如下图所示    如何获取登录的口令呢?这 ...

  2. 改变JAVA窗体属性的操作方法

    在本篇内容里小编给大家详细分析了关于改变JAVA窗体属性的操作方法和步骤,需要的朋友们学习下. 若将JDK版本升级到最新版本,Java窗体就可以简单实现窗体的透明效果,用户可以通过拉动滑块(Slide ...

  3. 史诗级最强教科书式“NIO与Netty编程”

    史诗级最强教科书式“NIO与Netty编程” 1.1 概述 1.2 文件IO 1.2.1 概述和核心API 1.2.2 案例 1.3 网络IO 1.3.1 概述和核心API 3.4 AIO编程 3.5 ...

  4. C#基本语法<二>_线程

    线程 多线程和异步函数 当异步线程在工作完成时如何通知调用线程 当异步线程出现异常的时候该如何处理 异步线程工作的进度如何实时的通知调用线程 如何在调用线程中取消正在工作的异步线程,并进行回滚操作 异 ...

  5. PostgreSQL 表字段起别名

    使用Postgreq Sql 表字段起别名时注意要用双引号,使用单引号会出现语法错误,执行结果如图

  6. Vue.js+vue-element搭建属于自己的后台管理模板:更深入了解Vue.js(三)

    前言 上一章我们介绍了关于Vue实例中一些基本用法,但是组件.自定义指令.Render函数这些放到了本章来介绍,原因是它们要比前面讲的要难一些,组件是Vue.js最核心的功能,学习使用组件也是必不可少 ...

  7. opencv-python 图像基础处理(三)

    腐蚀操作 #腐蚀操作 import cv2 import numpy as np img=cv2.imread("d:/ke.png") kernel = np.ones((3,3 ...

  8. 【Gradle】Groovy基础

    Groovy基础 Groovy是基于JVM虚拟机的一种动态语言.每个Gradle的build脚本文件都是一个Groovy脚本文件. 字符串 在Groovy中,分号不是必需的.在Groovy中,单引号和 ...

  9. MySQL数据库:在命令提示符中使用mysql

    服务启动 在命令提示符中 启动mysql服务 net start mysql 停止mysql服务 net sotp mysql 通过命令行进入 mysql -u 用户名 -p 键入后会提示输入密码 如 ...

  10. Jenkins实现单一安卓项目打包多个module填坑实录

    今天接手一个任务,已有项目结构上新添加了一个module,这个module打包工作需要在Jenkins上添加一个方便测试点击的打包工程 因为之前已有现成的打包工程,我直接新建了一个工程并且复制原有工程 ...