2018-8-10-WPF-使用不安全代码快速从数组转-WriteableBitmap
| title | author | date | CreateTime | categories |
|---|---|---|---|---|
|
WPF 使用不安全代码快速从数组转 WriteableBitmap
|
lindexi
|
2018-08-10 19:16:53 +0800
|
2018-2-13 17:23:3 +0800
|
WPF
|
本文告诉大家一个快速的方法,直接把数组转 WriteableBitmap
先来说下以前的方法,以前使用的是 BitmapSource ,这个方法是大法官方提供的。
BitmapSource.Create(LogicalWidth, LogicalHeight, 96, 96, PixelFormats.Bgra32, null,
dest,
stride);
其中 dest 是一个大数组,数据大小为 $$ LogicalWidthLogicalHeight4 $$ ,经常在转换的时候出现内存不足异常。假如现在内存占用是 1.5G ,转换的图片大小是 2000*2000 ,于是几乎一跑就出现内存不足。
为何还有 500 M 内存却出现内存不足?因为图片转换需要的是一段大的连续内存空间。砸桌子,再说一次,图片转换需要一段【大的连续】内存空间,虽然有500M内存,但是连续的空间没有那么多。
这就是以前方法的缺点。
使用不安全代码转换是把数组直接复制到WriteableBitmap,请看使用不安全代码将 Bitmap 位图转为 WPF 的 ImageSource 以获得高性能和持续小的内存占用 - walterlv,这里讲了如何从 Bitmap 转 WriteableBitmap ,于是下面只需要把数组转 Bitmap 就可以了。
我在最后会给大家全部代码,所以现在讲原理。
如果已经拿到了数组,知道数组的存放,那么就可以直接把数组复制到 WriteableBitmap 就可以显示。数组的存放就是数组是如何放数据的,是不是还在想,上面的 dest 是一个大数组,他的计算是
$$ LogicalWidthLogicalHeight4 $$ 为什么是*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
2018-8-10-WPF-使用不安全代码快速从数组转-WriteableBitmap的更多相关文章
- WPF 使用不安全代码快速从数组转 WriteableBitmap
原文:WPF 使用不安全代码快速从数组转 WriteableBitmap 本文告诉大家一个快速的方法,直接把数组转 WriteableBitmap 先来说下以前的方法,以前使用的是 BitmapSou ...
- 01 mybatis框架整体概况(2018.7.10)-
01 mybatis框架整体概况(2018.7.10)- F:\廖雪峰 JavaEE 企业级分布式高级架构师课程\廖雪峰JavaEE一期\第一课(2018.7.10) maven用的是3.39的版本 ...
- 北京化工大学2018年10月程序设计竞赛部分题解(A,C,E,H)
目录 北京化工大学2018年10月程序设计竞赛部分题解(A,C,E,H) 竞赛事件相关 竞赛链接 竞赛题目 总结 北京化工大学2018年10月程序设计竞赛部分题解(A,C,E,H) 竞赛事件相关 竞赛 ...
- Burn Down Chart(2018.6.4~2018.6.10)
Burn Down Chart (2018.6.4~2018.6.10) 娄雨禛[前端部分] 曾子轩[后端部分+燃尽图] 前端 1. 娄雨禛+李鑫 1)在总工程中完成跳转,实现图片显示,并发布到Git ...
- 申请Office 365一年免费的开发者账号攻略(2018年10月份版本)
要进行Office 365开发,当然需要有完整的Office 365环境才可以.为了便于广大开发人员快速地启动这项工作,微软官方给所有开发人员提供了免费的一年开发者账号 那么如何申请Office ...
- IntelliJ IDEA 最新激活码(截止到2018年10月14日)
IntelliJ IDEA 注册码: EB101IWSWD-eyJsaWNlbnNlSWQiOiJFQjEwMUlXU1dEIiwibGljZW5zZWVOYW1lIjoibGFuIHl1IiwiYX ...
- 新手C#SQL Server使用记录2018.08.10
主键(PrimaryKey):主键就是每个数据行(记录)的唯一标识,不会有重复值的列(字段)才能当做主键.一个表可以没有主键,但是这样会很难处理表,因此一般情况表都要设置主键. 主键有两张选用策略,分 ...
- c#字符串加载wpf控件模板代码 - 简书
原文:c#字符串加载wpf控件模板代码 - 简书 ResourceManager resManagerA = new ResourceManager("cn.qssq666.Properti ...
- WPF Grid 用 C# 代码后台设置
WPF Grid 用 C# 代码后台设置 运行环境:Window7 64bit,.NetFramework4.61,C# 6.0: 编者:乌龙哈里 2017-02-21 参考: System.Wind ...
随机推荐
- vue使用axios实现前后端通信
安装依赖 npm install --save axios # vue-axios是对axios的简单封装 npm install --save vue-axios 用例 在main.js里面进行全局 ...
- 多重背包 /// 单调队列DP oj1943
题目大意: em.... 就是多重背包 挑战340页的东西 ...自己的笔记总结总是比较乱的 重点:原始的状态转移方程中 更新第 i 种物品时 重量%w[i] 的值不同 则它们之间是相互独立的: 1- ...
- vue表格之tableHeaderColor(修改表头背景色)
<el-table :header-cell-style="tableHeaderColor"></el-table> // 更改表头样式 tableHea ...
- 使用 /proc 文件系统
/proc 文件系统是一个特殊的软件创建的文件系统, 内核用来输出消息到外界. /proc 下 的每个文件都绑到一个内核函数上, 当文件被读的时候即时产生文件内容. 我们已经见到 一些这样的文件起作用 ...
- 锋利的Jquery(p的onclick()事件)
1.一个p元素的点击事件 <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="serve ...
- vue之vant组件下拉加载更多
vant地址:https://youzan.github.io/vant/#/zh-CN/intro 基础用法 List 组件通过loading和finished两个变量控制加载状态,当组件滚动到底部 ...
- SpringBoot学习笔记(三):SpringBoot集成Mybatis、SpringBoot事务管理、SpringBoot多数据源
SpringBoot集成Mybatis 第一步我们需要在pom.xml里面引入mybatis相关的jar包 <dependency> <groupId>org.mybatis. ...
- Synchronized理解及用法
加锁: 1.同步实例方法,锁是当前实例对象 2.同步类方法,锁的是当前类对象 3.同步代码块,锁是括号里面的对象 原理: JVM内置锁通过synchronized使用,通过内部对象Monitor(监视 ...
- Python实例1-Collatz 序列
编写一个名为 collatz()的函数,它有一个名为 number 的参数.如果参数是偶数,那么 collatz()就打印出 number // 2, 并返回该值.如果 number 是奇数, col ...
- 安装office2019
win10系统安装office2019 安装文件下载 https://pan.baidu.com/s/1VnqJ-hNwysPKBhdzE3FSww#list/path=%2F&parentP ...