使用不安全代码将 Bitmap 位图转为 WPF 的 ImageSource 以获得高性能和持续小的内存占用

简介: 原文:使用不安全代码将 Bitmap 位图转为 WPF 的 ImageSource 以获得高性能和持续小的内存占用 版权声明:本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
原文: 使用不安全代码将 Bitmap 位图转为 WPF 的 ImageSource 以获得高性能和持续小的内存占用

版权声明:本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:http://blog.csdn.net/wpwalter/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系(walter.lv@qq.com)。 https://blog.csdn.net/WPwalter/article/details/78619679

在 WPF 中将一个现成的 Bitmap 位图转换成 ImageSource 用于显示一个麻烦的事儿,因为 WPF 并没有提供多少可以转过来的方法。不过产生 Bitmap 来源却非常多,比如屏幕截图、GDI 图、数组或其它非托管框架生成的图片。


WPF 官方提供了一种方法,使用 System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap() 方法。官方解释称这是托管和非托管位图相互转换所用的方法。然而此方法有一个很严重的弊端——每次都会生成全新的位图,即便每次 DeleteObject 之后,内存依然不会即时释放。

DeleteObject:

[DllImport("gdi32")]
static extern int DeleteObject(IntPtr o);

DeleteObject 的指针源于 Bitmap.GetHbitmap() 方法,且得到的指针会作为 System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap() 的参数之一。


在持续输出图像的时候(例如播放 Gif 图、持续显示屏幕截图等)不及时释放内存非常致命!为了防止重复创建图片,WriteableBitmap 似乎成了比较好的选择。

但是 WriteableBitmap 没有提供与位图 Bitmap 的互操作。然而它们都提供了像素操作。

于是,我们考虑内存拷贝来完成转换,代码如下:

public static class WriteableBitmapExtensions
{
    public static void CopyFrom(this WriteableBitmap wb, Bitmap bitmap)
    {
        if (wb == null) throw new ArgumentNullException(nameof(wb));
        if (bitmap == null) throw new ArgumentNullException(nameof(bitmap));

        var ws = wb.PixelWidth;
        var hs = wb.PixelHeight;
        var wt = bitmap.Width;
        var ht = bitmap.Height;
        if (ws != wt || hs != ht) throw new ArgumentException("暂时只支持相同尺寸图片的复制。");

        var width = ws;
        var height = hs;
        var bytes = ws * hs * wb.Format.BitsPerPixel / 8;

        var rBitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height),
            ImageLockMode.ReadOnly, bitmap.PixelFormat);

        wb.Lock();
        unsafe
        {
            Buffer.MemoryCopy(rBitmapData.Scan0.ToPointer(), wb.BackBuffer.ToPointer(), bytes, bytes);
        }
        wb.AddDirtyRect(new Int32Rect(0, 0, width, height));
        wb.Unlock();

        bitmap.UnlockBits(rBitmapData);
    }
}

我写了一个持续不断截取屏幕并输出显示的控件,在我的 The New Surface Pro 2736*1826 分辨率下内存一直保持 168M 从不变化。

内存占用

这个方法的简化空间还非常大,比如,如果数据源是一个一次申请不断修改的数组,那么连 Bitmap 都可以不需要了,直接拷贝数组空间即可。我的朋友林德熙为此将这段代码简化得只剩下几行代码了:WPF 使用不安全代码快速从数组转 WriteableBitmap - 林德熙

目录
相关文章
|
2月前
|
Rust 安全 编译器
Rust中的生命周期与借用检查器:内存安全的守护神
本文深入探讨了Rust编程语言中生命周期与借用检查器的概念及其工作原理。Rust通过这些机制,在编译时确保了内存安全,避免了数据竞争和悬挂指针等常见问题。我们将详细解释生命周期如何管理数据的存活期,以及借用检查器如何确保数据的独占或共享访问,从而在不牺牲性能的前提下,为开发者提供了强大的内存安全保障。
|
3月前
|
存储 数据可视化 C++
提高代码效率的6个Python内存优化技巧
当项目变得越来越大时,有效地管理计算资源是一个不可避免的需求。Python与C或c++等低级语言相比,似乎不够节省内存。 但是其实有许多方法可以显著优化Python程序的内存使用,这些方法可能在实际应用中并没有人注意,所以本文将重点介绍Python的内置机制,掌握它们将大大提高Python编程技能。
91 0
|
29天前
|
IDE Linux 开发工具
内存泄漏检测工具Valgrind:C++代码问题检测的利器(一)
内存泄漏检测工具Valgrind:C++代码问题检测的利器
65 0
|
16天前
|
存储 缓存 NoSQL
Redis 服务器指南:高性能内存数据库的完整使用指南
Redis 服务器指南:高性能内存数据库的完整使用指南
|
29天前
|
缓存 测试技术 开发工具
内存泄漏检测工具Valgrind:C++代码问题检测的利器(二)
内存泄漏检测工具Valgrind:C++代码问题检测的利器
35 0
|
2月前
|
存储 缓存 算法
Golang高性能内存缓存库BigCache设计与分析
【2月更文挑战第4天】分析Golang高性能内存缓存库BigCache设计
64 0
|
2月前
|
Rust 安全 开发者
Rust的安全特性概览:守护内存安全与空指针的终结者
Rust作为一种系统级编程语言,以其独特的内存安全特性和对空指针的严格管理,为开发者提供了更加稳健和安全的编程环境。本文将对Rust的内存安全机制、空指针处理策略以及其他安全特性进行概览,旨在展示Rust如何帮助开发者构建更加安全和可靠的软件系统。
|
3月前
小技巧分享:如何使用动态断点快速找到成对的 ABAP 内存 IMPORT 和 EXPORT 的代码位置
小技巧分享:如何使用动态断点快速找到成对的 ABAP 内存 IMPORT 和 EXPORT 的代码位置
22 0
|
3月前
|
Java 数据库连接
警惕内存泄漏与溢出!你的代码是否隐藏着致命危机?
警惕内存泄漏与溢出!你的代码是否隐藏着致命危机?
|
4月前
|
C++
c cpp 代码 数据竞争 分析, 以及 内存泄露 分析 工具 使用 demo
c cpp 代码 数据竞争 分析, 以及 内存泄露 分析 工具 使用 demo
30 0