WPF 对接 Vortice 调用 D2D 使用 IWICBitmap 离屏渲染
通过 Vortice 库可以使用非常底层的方式调用到 Direct2D1 进行渲染,本文将使用 D2D 离屏渲染到 IWICBitmap 上,再使用一点点反射黑科技,直接将此 IWICBitmap 对接到 WPF 框架里。本文提供的这个方法可以实现极高性能且只有很少的转换损耗的离屏渲染方式,唯一的一个缺点是需要进行一点反射调用,适合用来静态画面渲染上
在 WPF 的渲染底层里,对于图片来说,都是采用 WIC Bitmap 参与渲染。而刚好 Direct2D1 可以从一个 IWICBitmap 上使用 CreateWicBitmapRenderTarget 方法创建 ID2D1RenderTarget 对象,在 ID2D1RenderTarget 上执行绘制指导命令,从而实现将画面绘制到 IWICBitmap 上
于是新建出一个 IWICBitmap 对象,接着挂上 D2D 的 ID2D1RenderTarget 进行绘制。完成之后,将 IWICBitmap 封装为一个 BitmapSource 对象,扔给 WPF 层,当成图片接入 WPF 的渲染框架
创建 IWICBitmap 对象和挂上 D2D 以及绘制逻辑的细节,请参阅 dotnet C# 使用 Vortice 支持 Direct2D1 离屏渲染
以下是本文使用的代码,这里就不展开细节
private static Task<IWICBitmap> OffScreenRenderingWICBitmapAsync()
{
return Task.Run(OffScreenRenderingWICBitmap);
}
private static IWICBitmap OffScreenRenderingWICBitmap()
{
using var wicImagingFactory = new IWICImagingFactory();
IWICBitmap wicBitmap =
wicImagingFactory.CreateBitmap(1000, 1000, Win32.Graphics.Imaging.Apis.GUID_WICPixelFormat32bppPBGRA);
using D2D.ID2D1Factory1 d2DFactory = D2D.D2D1.D2D1CreateFactory<D2D.ID2D1Factory1>();
var renderTargetProperties = new D2D.RenderTargetProperties(PixelFormat.Premultiplied);
D2D.ID2D1RenderTarget d2D1RenderTarget =
d2DFactory.CreateWicBitmapRenderTarget(wicBitmap, renderTargetProperties);
using var renderTarget = d2D1RenderTarget;
// 开始绘制逻辑
renderTarget.BeginDraw();
Render(renderTarget);
renderTarget.EndDraw();
return wicBitmap;
}
private static void Render(D2D.ID2D1RenderTarget renderTarget)
{
// 以下是测试代码
// 假装是耗时的渲染
var color = new Color4((byte)Random.Shared.Next(255), (byte)Random.Shared.Next(255),
(byte)Random.Shared.Next(255));
renderTarget.Clear(color);
color = new Color4((byte)Random.Shared.Next(255), (byte)Random.Shared.Next(255),
(byte)Random.Shared.Next(255));
using var brush = renderTarget.CreateSolidColorBrush(color);
// 10万个圆,无论是啥都顶不住
for (int i = 0; i < 100000; i++)
{
renderTarget.DrawEllipse(new D2D.Ellipse(new Vector2(Random.Shared.Next(900), Random.Shared.Next(900)),Random.Shared.Next(1,5), Random.Shared.Next(1, 5)),brush,Random.Shared.Next(1,2));
}
}
从上面代码可以看到,这是完全在另一个线程执行的逻辑,如此将不会卡住主线程。可以放心在后台线程里,执行复杂的逻辑,绘制一张有趣的画面。例如本文就采用啥都顶不住的画 10 万个圆的方法
完成离屏渲染之后,需要将 IWICBitmap 的结果对接到 WPF 框架,对接方法是封装为一个 BitmapSource 对象。可以 WPF 框架里面没有对外公开的 UnmanagedBitmapWrapper 类型,只是使用没有公开的类型就需要用到一点点反射
private static BitmapSource WICBitmapToBitmapSource(IWICBitmap wicBitmap)
{
var presentationCoreAssembly = typeof(BitmapSource).Assembly;
var bitmapSourceSafeMILHandleType =
presentationCoreAssembly.GetType("System.Windows.Media.Imaging.BitmapSourceSafeMILHandle", throwOnError: true)!;
var bitmapSourceSafeMILHandleConstructor =
bitmapSourceSafeMILHandleType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance,
new Type[] { typeof(IntPtr) })!;
var bitmapSourceSafeMILHandle =
bitmapSourceSafeMILHandleConstructor.Invoke(new object[] { wicBitmap.NativePointer });
var unmanagedBitmapWrapperType =
presentationCoreAssembly.GetType("System.Windows.Media.Imaging.UnmanagedBitmapWrapper")!;
var unmanagedBitmapWrapperConstructor =
unmanagedBitmapWrapperType.GetConstructor(BindingFlags.Public | BindingFlags.Instance,
new Type[] { bitmapSourceSafeMILHandleType })!;
var unmanagedBitmapWrapper = unmanagedBitmapWrapperConstructor.Invoke(new object[] { bitmapSourceSafeMILHandle });
return (BitmapSource) unmanagedBitmapWrapper;
}
按照 WPF 框架的源代码,可以看到 UnmanagedBitmapWrapper 的定义和构造函数如下
internal sealed class UnmanagedBitmapWrapper : BitmapSource
{
public UnmanagedBitmapWrapper(BitmapSourceSafeMILHandle bitmapSource) :
base(true)
{
_bitmapInit.BeginInit();
// This constructor is used by BitmapDecoder and BitmapFrameDecode for thumbnails and
// previews. The bitmapSource parameter comes from BitmapSource.CreateCachedBitmap
// which already calculated memory pressure, so there's no need to do it here.
WicSourceHandle = bitmapSource;
_bitmapInit.EndInit();
UpdateCachedSettings();
}
// 忽略代码
}
原本这个 UnmanagedBitmapWrapper 是设计给解码器等使用的,不是公开使用的,但是我看起来这个类型很清真,也许开放出来也是可以的
在拿到继承 BitmapSource 的 UnmanagedBitmapWrapper 对象之后,即可作为某个图片的 Source 使用
Image.Source = unmanagedBitmapWrapper;
为了方便演示效果,在 WPF 的 MainWindow 放一个 Image 控件,如下面代码
<Image x:Name="Image"></Image>
接着在 Loaded 事件之后,先异步在后台线程调用 D2D 的渲染,将渲染结果封装为 BitmapSource 再设置给图片
private async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
try
{
using IWICBitmap wicBitmap = await OffScreenRenderingWICBitmapAsync();
var unmanagedBitmapWrapper = WICBitmapToBitmapSource(wicBitmap);
Image.Source = unmanagedBitmapWrapper;
}
catch (Exception)
{
// 这里是 async void 线程的顶层,如果有任何异常,那应用就炸了
// 而采用离屏渲染的 OffScreenRenderingWICBitmapAsync 是预期会有很多奇怪的异常
}
}
试试跑跑上本文的例子,可以从文本末尾获取到项目的全部代码的可构建运行项目,测试一下对 WPF 的影响。预计此方法对 WPF 的影响是非常小的,损耗约等于渲染一张图,而且还是一张不需要解码的图片的损耗。此方法和 D3DImage 对比如何?从代码分析上来说,如果不是静态画面绘制,那是比不过 D3DImage 的。静态画面绘制就是绘制一张静态画面,后续也不需要去更新,去修改画面的内容。在绘制静态画面的时候,性能预计和 D3DImage 持平。只是相对于 D3DImage 这么重的来说,使用 WICBitmap 会更加轻而已,而且由于本身设计上也不是作为动态画面使用,少了很多同步的逻辑
既然这个方式需要这么好用,那再用反射似乎也说不过去,刚好我是 WPF 框架的开发者,我在想着要不要将这个 UnmanagedBitmapWrapper 类型开放好了
可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 1de394636dc15865ab90301230ed7ce37fe01ca0
以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码
git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 1de394636dc15865ab90301230ed7ce37fe01ca0
获取代码之后,进入 WpfVorticeWicTest 文件夹
更多 DirectX 和 D2D 以及 Vortice 库的博客,请参阅我的 博客导航
另外,我创建了专门聊 Vortice 的 QQ 群: 622808968 欢迎加入交流技术
WPF 对接 Vortice 调用 D2D 使用 IWICBitmap 离屏渲染的更多相关文章
- 【ASP.NET Web API教程】3.3 通过WPF应用程序调用Web API(C#)
原文:[ASP.NET Web API教程]3.3 通过WPF应用程序调用Web API(C#) 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的 ...
- java接口对接——别人调用我们接口获取数据
java接口对接——别人调用我们接口获取数据,我们需要在我们系统中开发几个接口,给对方接口规范文档,包括访问我们的接口地址,以及入参名称和格式,还有我们的返回的状态的情况, 接口代码: package ...
- WPF 用代码调用dynamic resource动态更改背景 - CSDN博客
原文:WPF 用代码调用dynamic resource动态更改背景 - CSDN博客 一般dynamic resoource通常在XAML里调用,如下范例: <Button Click=&qu ...
- WPF 精修篇 调用Win32Api
原文:WPF 精修篇 调用Win32Api 栗子是 调用WIn32API 让窗口最前 后台代码 [DllImport("user32.dll")] private static e ...
- dotnet 读 WPF 源代码笔记 布局时 Arrange 如何影响元素渲染坐标
大家是否好奇,在 WPF 里面,对 UIElement 重写 OnRender 方法进行渲染的内容,是如何受到上层容器控件的布局而进行坐标偏移.如有两个放入到 StackPanel 的自定义 UIEl ...
- 修改ViewPager调用setCurrentItem时,滑屏的速度
原文摘自: 修改ViewPager调用setCurrentItem时,滑屏的速度 在使用ViewPager的过程中,有需要直接跳转到某一个页面的情况,这个时候就需要用到ViewPager的setCur ...
- 【WPF】Winform调用WPF窗体注意事项
1.需要添加一些引用 2.调用处使用如下方法进行调用 Window win= new Window(); ElementHost.EnableModelessKeyboardInterop(win) ...
- WPF 使用RPC调用其他进程
如果在 WPF 需要用多进程通信,一个推荐的方法是 WCF ,因为 WCF 是 RPC 计算.先来讲下 RPC (Remote Procedure Call) 远程过程调用,他是通过特定协议,包括 t ...
- 系统对接API调用
在与公司外部系统对接时,API接口一般采用REST风格,对外暴露HTTP服务.只需要将入参封装好,并发起HTTP请求即可.具体请求流程如下图所示: 数据格式 API调用参数分为系统参数和业务参数,请求 ...
- WPF中窗体调用窗体
在WPF中有时候我们需要在一个窗体中去调用另外的一个窗体,下面给出调用方法. 下面实现在MainWindow中通过点击一个按钮调用另外的一个窗口. 首先创建你要调用的另外一个窗口:点击最上面的项目 ...
随机推荐
- python基础四(字符编码)
一 了解字符编码的知识储备 计算机基础知识 文本编辑器存取文件的原理(nodepad++,pycharm,word) #1.打开编辑器就打开了启动了一个进程,是在内存中的,所以,用编辑器编写的内容也都 ...
- 三维模型3DTile格式轻量化在数据存储的重要性分析
三维模型3DTile格式轻量化在数据存储的重要性分析 三维模型3DTile格式轻量化在数据存储中占有重要地位.随着科技的不断发展,尤其是空间信息科技的进步,人们对于三维地理空间数据的需求日益增长.然而 ...
- 记录--npm, npx, cnpm, yarn, pnpm梭哈
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 依赖管理解决了在软件开发过程中管理和协调各种依赖项的问题,简化了开发流程,提高了项目的可靠性.可维护性和可重复性.它们帮助开发人员更高效地 ...
- rnacos v0.1.6版本发布
rnacos是一个用 rust重新实现的nacos. rnacos比java实现的nacos更轻量.快速.稳定:合适在开发.测试.受资限服务等环境平替nacos服务使用. rnacos v0.1.6 ...
- FPGA的PCB设计
FPGA的PCB设计 一.FPGA的高速电路板设计 PCB板的设计规模增大,IO传输问题也就出现.为了兼容其他高速模块,必须对PCB的设计进行优化. 1️⃣电源滤波,降低系统噪声2️⃣匹配信号线3️⃣ ...
- KingbaseES Insert On Conflict 功能
针对数据写入时有主键冲突的情况,INSERT ON CONFLICT语法可以将冲突主键的INSERT行为转换为UPDATE行为,从而实现冲突主键的覆盖写入.该特性又称UPSERT覆盖写,与MySQL的 ...
- Book-Riscv-XV6-Chap1
操作系统接口 – 阅读 xv6-riscv-book Xv6的时钟周期:定时器芯片两次中断之间的时间 xv6作为一个简单的操作系统,利用一个"内核kernel"向其他运行中的程序提 ...
- 简单c++构建第一人称
本文内容为UE4.27的文档教程 GameMode确定 新建的项目会自动生成GameMode,如果有更改,而不是使用默认的GameMode类,就需要在引擎的设置中更改 角色的实现 前后左右移动 //前 ...
- 【已解决】Android----java.lang.NullPointerException:---java.lang.NullPointerException:
2021-03-06 13:26:12.274 8544-8544/com.example.helloworld E/AndroidRuntime: FATAL EXCEPTION: main Pro ...
- postman测试接口时的参数对应接口的两种情况
第一种通过json字符串的情况去进行测试,最终将json字符串转成对应的对象 代码里面一定要加上@RequestBody注解,即使是一个字符串也需要加这个注解 第二种通过key-value的形式去加入 ...