原文:WPF 使用不安全代码快速从数组转 WriteableBitmap

本文告诉大家一个快速的方法,直接把数组转 WriteableBitmap

先来说下以前的方法,以前使用的是 BitmapSource ,这个方法是大法官方提供的。

BitmapSource.Create(LogicalWidth, LogicalHeight, 96, 96, PixelFormats.Bgra32, null,
dest,
stride);

其中 dest 是一个大数组,数据大小为 ,经常在转换的时候出现内存不足异常。假如现在内存占用是 1.5G ,转换的图片大小是 2000*2000 ,于是几乎一跑就出现内存不足。

为何还有 500 M 内存却出现内存不足?因为图片转换需要的是一段大的连续内存空间。砸桌子,再说一次,图片转换需要一段【大的连续】内存空间,虽然有500M内存,但是连续的空间没有那么多。

这就是以前方法的缺点。

使用不安全代码转换是把数组直接复制到WriteableBitmap,请看使用不安全代码将 Bitmap 位图转为 WPF 的 ImageSource 以获得高性能和持续小的内存占用 - walterlv,这里讲了如何从 Bitmap 转 WriteableBitmap ,于是下面只需要把数组转 Bitmap 就可以了。

我在最后会给大家全部代码,所以现在讲原理。

如果已经拿到了数组,知道数组的存放,那么就可以直接把数组复制到 WriteableBitmap 就可以显示。数组的存放就是数组是如何放数据的,是不是还在想,上面的 dest 是一个大数组,他的计算是
为什么是*4,因为存放的数据是 A R B G 一个点需要4个int来放。那么放的顺序是什么?这就是PixelFormat指定的类型,可以使用Bgra32或者其他的格式,不过指定了格式就需要数组存放和指定一样

因为没有直接从数组转 WriteableBitmap 所以需要先把数组转 Bitmap ,可以使用的方法请看下面

                unsafe
{
fixed (int* ptr = _dest)
{ try
{
using (Bitmap image = new Bitmap(LogicalWidth, LogicalHeight, LogicalWidth * 4,
PixelFormat.Format16bppArgb1555, new IntPtr(ptr)))
{ }
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}

Bitmap 的数据类型可以是任意,因为只是把他的数据转换到 WriteableBitmap 所以不需要指定他的数据

获得 Bitmap 就可以把他转 WriteableBitmap ,请看下面的代码

                unsafe
{
fixed (int* ptr = _dest)
{ try
{
using (Bitmap image = new Bitmap(LogicalWidth, LogicalHeight, LogicalWidth * 4,
PixelFormat.Format16bppArgb1555, new IntPtr(ptr)))
{
CopyFrom(source,image);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}

代码的 CopyFrom 就是吕毅提供的方法。

使用这个函数更新,不需要在更新了修改 Image 的 Source 因为会自动更新,用这个方法播放 gif 的性能比找到的Magick.NET库的性能都好。

对比一下性能,这时原先的 BitmapSource 方法占用内存

这是使用不安全代码占用内存

实际跑一张 gif 图的性能

可以看到这个方法可以节省很多的内存,而且占用的 cpu 很低,因为没有很多gc

但是不要太高兴,因为不安全代码的exception是接不住的,下面请修改一下代码,让他输入错误,于是就出现异常,结果程序就关了。

所以使用这个方法还是很大的坑。

全部的代码:

           Application.Current?.Dispatcher.BeginInvoke((Action) (() =>
{
unsafe
{
fixed (int* ptr = _dest)
{ try
{
using (Bitmap image = new Bitmap(LogicalWidth, LogicalHeight, LogicalWidth * 4,
PixelFormat.Format16bppArgb1555, new IntPtr(ptr)))
{
CopyFrom(_source, image);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
})); private void UpdateSource()
{
Application.Current?.Dispatcher.Invoke(() =>
{
_source = new WriteableBitmap(LogicalWidth, LogicalHeight, 96, 96,
System.Windows.Media.PixelFormats.Bgra32, null);
});
} public static void CopyFrom(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);
}

我把代码给小伙伴看,他说可以直接从数组转 WriteableBitmap ,我使用他的想法,修改了程序,请看代码

              unsafe
{
fixed (int* ptr = _dest)
{
_source.Lock(); var bytes = LogicalWidth * LogicalHeight * _source.Format.BitsPerPixel / 8; Buffer.MemoryCopy(ptr,_source.BackBuffer.ToPointer(), bytes, bytes); _source.AddDirtyRect(new Int32Rect(0, 0, LogicalWidth, LogicalHeight));
_source.Unlock();
}
}

实际上微软已经提供了不安全代码的转换,请看下面代码

bitmapImage.WritePixels(new Int32Rect(0, 0, 宽度, 高度), 图片数据, stride, 0)

stride 一般就是 4*宽度 因为一个像素使用4个byte


本文会经常更新,请阅读原文:
https://lindexi.gitee.io/lindexi/post/WPF-%E4%BD%BF%E7%94%A8%E4%B8%8D%E5%AE%89%E5%85%A8%E4%BB%A3%E7%A0%81%E5%BF%AB%E9%80%9F%E4%BB%8E%E6%95%B0%E7%BB%84%E8%BD%AC-WriteableBitmap.html
,以避免陈旧错误知识的误导,同时有更好的阅读体验。


本作品采用
知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议
进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接:
https://lindexi.gitee.io
),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请
与我联系

WPF 使用不安全代码快速从数组转 WriteableBitmap的更多相关文章

  1. 2018-8-10-WPF-使用不安全代码快速从数组转-WriteableBitmap

    title author date CreateTime categories WPF 使用不安全代码快速从数组转 WriteableBitmap lindexi 2018-08-10 19:16:5 ...

  2. 转载: Emmet:HTML/CSS代码快速编写神器

    Emmet:HTML/CSS代码快速编写神器 因为文章严禁转载,那本着做一个遵纪守法的好公民,我就不转载了,把链接放下面,方便查阅. http://www.iteye.com/news/27580

  3. WPF案例 (三) 模拟QQ“快速换装"界面

    原文:WPF案例 (三) 模拟QQ"快速换装"界面 这个小程序使用Wpf模拟QQ快速换装页面的动画特效,通过使用组合快捷键Ctrl+Left或Ctrl+Right,可实现Image ...

  4. javascript中快速求数组的全部元素的相加之和

    js中快速求数组的全部元素的相加之和: var arr = [1,2,3,4,5];var sum = eval(arr.join('+')); console.log(sum); 运行结果: 15

  5. C语言/C++编程学习:栈的代码实现之数组方案

    C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构.C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现 ...

  6. c#字符串加载wpf控件模板代码 - 简书

    原文:c#字符串加载wpf控件模板代码 - 简书 ResourceManager resManagerA = new ResourceManager("cn.qssq666.Properti ...

  7. WPF Grid 用 C# 代码后台设置

    WPF Grid 用 C# 代码后台设置 运行环境:Window7 64bit,.NetFramework4.61,C# 6.0: 编者:乌龙哈里 2017-02-21 参考: System.Wind ...

  8. 5_PHP数组_3_数组处理函数及其应用_1_快速创建数组的函数

    以下为学习孔祥盛主编的<PHP编程基础与实例教程>(第二版)所做的笔记. 一.快速创建数组的函数 1. range() 函数 程序: <?php $numbers = range(1 ...

  9. C#/WPF/WinForm/.NET程序代码实现软件程序开机自动启动的两种常用方法的示例与源码下载带详细注释-源码代码-注册表方式-启动目录快捷方式

    C#/WPF/WinForm/.NET程序代码实现软件程序开机自动启动的两种常用方法的示例与源码下载带详细注释-源码代码-注册表方式-启动目录快捷方式 C#实现自动启动的方法-两种方法 源码下载地址: ...

随机推荐

  1. Codeforces Round #100 E. New Year Garland (第二类斯特林数+dp)

    题目链接: http://codeforces.com/problemset/problem/140/E 题意: 圣诞树上挂彩球,要求从上到下挂\(n\)层彩球.已知有\(m\)种颜色的球,球的数量不 ...

  2. Codeforces Round #445 Div. 1 C Maximum Element (dp + 组合数学)

    题目链接: http://codeforces.com/contest/889/problem/C 题意: 给你 \(n\)和 \(k\). 让你找一种全排列长度为\(n\)的 \(p\),满足存在下 ...

  3. call,apply,求最大最小值,平均数等基础编程知识

    CALL/APPLY.一些编程基础以及一些基础知识.正则 call.apply.bind 求数组的最大值和最小值: 数组排序(SORT的原理->localeCompare实现汉字比较),取头取尾 ...

  4. 很安逸的离线API文档查询工具Dash和Zeal

    大家开发的时候难免会查询一些文档,看一下API的调用方法等,所以会不同的语言去某一个地方去找,确实很麻烦,今天给大家安逸两款软件,肯定会让你爱不释手! Dash for macOS 官方地址:http ...

  5. @RequestMapping value 能够反复吗 [

    @RequestMapping value 能够反复吗 [问题点数:40分,结帖人wangqiao4j] 不显示删除回复显示全部回复 显示星级回复显示得分回复 仅仅显示楼主 u=http://bbs. ...

  6. Openstack nova(二)——架构(一)

    架构源自需求 需求分析 软件架构大部分都来自于需求.能够说.有什么样的需求,就会有什么样的架构, 尽管不同一时候期,不同的人来实现,可能不全然一样.可是整体来说, 架构不会相差太远. 如今假设假设须要 ...

  7. XML Parser Errors See Details for more Information XML Parser Error on line 1: Document root ele

    1.错误描写叙述 XML Parser Errors See Details for more Information XML Parser Error on line 1: Document roo ...

  8. [Angular2 Form] Validation message for Reactive form

    <div class="form-field"> <label>Confirm Password: </label> <input typ ...

  9. 高并发解决方案--负载均衡(HTTP,DNS,反向代理服务器)(解决大流量,高并发)

    高并发解决方案--负载均衡(HTTP,DNS,反向代理服务器)(解决大流量,高并发) 一.总结 1.什么是负载均衡:当一台服务器的性能达到极限时,我们可以使用服务器集群来提高网站的整体性能.那么,在服 ...

  10. 使用C#版本的gdal库打开hdf文件

    作者:朱金灿 来源:http://blog.csdn.net/clever101 最近应同事的请求帮忙研究下使用C#版的gdal库读取hdf文件,今天算是有一点成果,特地做一些记录. 首先是编译C#版 ...