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中通过点击一个按钮调用另外的一个窗口. 首先创建你要调用的另外一个窗口:点击最上面的项目 ...
随机推荐
- 自定义Key类型的字典无法序列化的N种解决方案
当我们使用System.Text.Json.JsonSerializer对一个字典对象进行序列化的时候,默认情况下字典的Key不能是一个自定义的类型,本文介绍几种解决方案. 一.问题重现 二.自定义J ...
- http内网穿透CYarp[开源]
0 前言 在物联网领域中,mqtt消息一直是海量设备连接到平台的标配协议,而平台向移动端开放的操作接口往往是http协议,这就要求平台为两种协议作消息一一适配.在某些情况下,这些设备是有操作系统的li ...
- 关于volatile与指令重排序的探讨
写在开头 在之前的学习我们了解到,为了充分利用缓存,提高程序的执行速度,编译器在底层执行的时候,会进行指令重排序的优化操作,但这种优化,在有些时候会带来 有序性 的问题. 那何为有序性呢?我们可以通俗 ...
- Linux快速入门(三)Linux文件管理
Linux文件操作 head head命令用于显示文件的前几行内容,可以通过-num参数展示文件前num行的内容. root@ubuntu:~# ls bb.txt cc.txt snap root@ ...
- 记录--怎么写一个可以鼠标控制旋转的div?
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 说在前面 鼠标控制元素旋转在现在也是一个很常见的功能,让我们从实现div元素的旋转控制开始来了解元素旋转的具体原理和实现方法吧. 效果展示 ...
- uni-app开发经验分享二十二: uni-app大转盘思路解析
最近在研究uni-app,制作了一个很有意思的集合项目demo,这里给大家分享下大转盘的前端设计思路 需求案例:大转盘抽奖 线上demo查看: 案例效果: 制作思路: 前端大转盘使用css3动画来做, ...
- 记录--一个好用的轮子 turn.js 实现仿真翻书的效果
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 国际惯例,官网链接 官网传送门 Github地址 github上有几个demos例子,介绍了基础用法. 我参考官网的例子,写了一个demo ...
- java实战字符串1:给定两个字符串 s 和 t,判断他们的编辑距离是否为 1。
题目描述给定两个字符串 s 和 t,判断他们的间距是否为 1.(满足以下三个条件) 往 s 中插入一个字符得到 t从 s 中删除一个字符得到 t在 s 中替换一个字符得到 t 例1 输入: ab ac ...
- 讲讲百度地图API遇到的坑,石锤百度官方代码的错,解决SN校验失败
这两天在做一个项目,用到了百度地图API,根据坐标获取具体位置,总结一下遇到的几个坑 本文基于最新的V3接口,网上好多要么是V2,要么根据地址获取坐标,本文是唯一一个最新的3,根据坐标获取位置的完整说 ...
- #分块,懒标记#LOJ 3631「2021 集训队互测」学姐买瓜
题目传送门 分析 有一个很简单的做法就是处理出每个位置能够一次到达的最左边的右端点(后继). 然后直接从 \(l\) 开始能跳就跳,这样单次询问时间复杂度是 \(O(n)\) 的. 观察到时间复杂度因 ...