WriteableBitmap(二) 实例
使用前面定义的WriteableBitmap,我们可以很容易地创建一个足够容纳整个100 x 100图像的数组:
byte[] pixels = new byte[wbmap.PixelHeight*wbmap.PixelWidth*wbmap.Format.BitsPerPixel/8];
用于创建数组的所有数据都是从WriteableBitmap本身获得的——包括用于存储每个像素的字节数。注意,给定的代码不工作的原因是每个像素的字节数不是一个整数。在本例中,stride = width *(字节数/像素)。
为了演示整个过程,让我们将第一个像素设置为蓝色:
pixels[0] = 0xff;
pixels[1] = 0x00;
pixels[2] = 0x00;
pixels[3] = 0xff;
字节的顺序是蓝色、绿色、红色和Alpha。请注意,要显示像素,我们必须记住将Alpha通道设置为255,因为默认值为0会使颜色100%透明,因此不会显示。
现在我们已经设置好了数组,我们可以使用WritePixels方法将数据写入位图:
wbmap.WritePixels(new Int32Rect(0, 0, wbmap.PixelWidth, wbmap.PixelHeight), pixels, wbmap.PixelWidth*wbmap.Format.BitsPerPixel/8, 0);
注意,这里使用了一个矩形,它覆盖了整个位图,并指定了数组中的stride的方式。最后一个参数是数据开始的数组中的偏移量。这用于跳过标题数据或格式化像素数据中的任何其他序言。在这种情况下,数据立即启动,因此偏移量为零。同样,stride被设置为指示数组中单个数据行的存储量。
CopyPixels
可以使用使用CopyPixels方法将数据从WriteableBitmap传输到的数组中。这与WritePixels的工作方式类似,只是传输方向颠倒了。同样值得注意的是,CopyPixels方法是从BitmapSource继承而来的,因此并不是什么新东西。
例如,要将位于左上角的20×10像素块复制到数组中,可以使用以下方法:
byte[] pixels2 = new byte[10 *20 *4];
wbmap.CopyPixels(new Int32Rect(0, 0, 20, 10), pixels2, 20 * 4, 0);
同样,您需要创建一个足够大的数组来容纳所有像素数据,并在位图中指定要复制的矩形。需要指定数组中的跨度和偏移量,但这在前面的示例之后应该很明显。
值得记住的是,如果你想将位图中的所有像素都转移到一个数组中,就会出现这样的重载:
wbmap.CopyPixels(pixels, 100 * 4, 0);
在这种情况下,所有像素数据都使用400步长和0偏移量转换。
CopyPixels和WritePixels都有一些重载,但它们的主要区别在于如何提供像素数据——以数组或IntPtr的形式提供给原始非托管内存区域。还有一些重载允许您在数组中选择一个矩形像素或原始非托管内存来传输到位图。例如:
wbmap.WritePixels(new Int32Rect(0, 0, 10, 10), pixels, 100 * 4, 0, 0);
这将复制位于源数组左上角的10×10正方形中的像素,即位图左上角的像素,起点为0,0。注意,指定的stride,即100 * 4,是作为位图的整个数组的stride。WritePixels方法使用您指定的步幅来计算矩形中像素的位置。
在实践中,您经常需要按照坐标和颜色值来操作数组中的数据,就好像它是位图一样。要做到这一点,您需要合适的存储和颜色映射功能。
存储映射函数非常简单: x、y处的像素存储在x*b+s*y中,其中s为步幅,b为每个像素的字节数。
颜色映射函数比较复杂,显然依赖于所选择的像素格式。
Backbuffer 访问
如果您只想块修改位图,那么WritePixel和CopyPixel方法就很好,但是如果您想频繁地更改单个像素,那么块操作并不是最好的方法。有一种替代方法,但不幸的是,它涉及一些不安全的代码。考虑到任务的性质,代码并不是特别不安全,实际上它可以以托管的方式实现——就像Silverlight的WriteableBitmap版本中所做的那样。
这个想法是有一个backbuffer,你可以通过一个指针直接访问它。当需要时,backbuffer被复制到frontbuffer,这是通过一种不让位图在更新过程中闪烁或撕裂的方式实现的。
要访问backbuffer,只需使用backbuffer属性,该属性返回一个带有backbuffer起始地址的IntPtr。backbuffer被固定在堆中,只要你在使用它,它就不会移动——不过,似乎更安全的做法是每隔一段时间获取地址。当你使用backbuffer时,为了阻止渲染线程更新frontbuffer,你必须锁定WriteableBitmap:
wbmap.Lock();
IntPtr buff = wbmap.BackBuffer;
使用IntPtr最简单的方法是将其转换为一个指针,特别是一个指向字节的指针,这使得可以将该指针视为一个字节数组。这段代码是不安全的,就像指针的任何使用一样,因此它必须被包装在不安全的语句中。将IntPtr转换为指向void的指针后,我们可以将其转换为指向byte的指针:
unsafe
{
byte* pbuff = (byte*)buff.ToPointer();
从现在开始,我们可以把pbuff当作一个数组来处理,因为c#会自动解除对指针索引的引用。例如,pbuf[1]相当于写*(pbuf+1)。将第一个像素设置为蓝色的方法与前面的例子相同:
pbuff[0] = 0xff;
pbuff[1] = 0x00;
pbuff[2] = 0x00;
pbuff[3] = 0xff;
}
这就关闭了不安全子句因为我们不会再使用指针了。剩下的就是将backbuffer的一个区域标记为“dirty”,这样渲染系统就知道将这些像素复制到frontbuffer进行显示并解锁WriteableBitmap。如果您不将backbuffer标记为dirty,则更新不会执行,并且您看不到更改:
wbmap.AddDirtyRect(new Int32Rect(0,0,10,10));
wbmap.Unlock();
这种工作方法的唯一缺点是使用了不安全的代码。
WriteableBitmap(二) 实例的更多相关文章
- 自学vue笔记 (二) : 实例与生命周期
一: 什么是实例 我们说了,Vue 应用都应该从构建一个 Vue 实例开始.它管理着挂载在它身上的所有内容,因此实例是一个根实例, 所有的组件都应该挂载在根实例上面.创建一个 Vue 实例,需要通过 ...
- Android Fragment (二) 实例2
由于看客的要求,我就把读者所要的写出来. 由于上一篇是每一个Fragment 实例了同一个layout.xml ,造成了读者的困惑,这篇我就让每一个Fragment 加载一个不同的layout.xml ...
- Android Fragment (二) 实例1
千呼万唤始出来,今天就也写一篇Frament 的简单实例.先看效果: 一看这效果,首先我们的配置资源文件:new -->android xml -->selector --> 四个图 ...
- vue 源码学习二 实例初始化和挂载过程
vue 入口 从vue的构建过程可以知道,web环境下,入口文件在 src/platforms/web/entry-runtime-with-compiler.js(以Runtime + Compil ...
- oracle之二实例管理及数据库的启动/关闭
实例管理及数据库的启动/关闭 2.1 实例和参数文件 1.instance 功能:用于管理和访问database.instance在启动阶段读取初始化参数文件(init parameter fil ...
- oracle之二实例与数据库
实例与数据库 1.Oracle 网络架构及应用环境 看PPT,Oracle结构的基本单元.术语 2.Oracle 体系结构 1)oracle server :database + instanc ...
- Discuptor入门(二)-实例
前言:最近在项目中看到有人使用的discuptor框架,因为没有接触过所以网上找了些资料.但最终发现开荒者太少,好像没什么人用那.最后感觉还是官方入门文档靠谱点.所以自己翻译了下(翻译器~),希望能帮 ...
- WCF学习之旅—HTTP双工模式(二十)
WCF学习之旅—请求与答复模式和单向模式(十九) 四.HTTP双工模式 双工模式建立在上文所实现的两种模式的基础之上,实现客户端与服务端相互调用:前面介绍的两种方法只是在客户端调用服务端的方法,然后服 ...
- Python 文件读写,条件循环(三次登录锁定账号实例)
通过文件读写,条件循环相关语法,实现三次登录失败则锁定该账号的功能 需求一 """需求描述: 1.输入正确账号,密码,退出程序 2.登录失败,重新输入账号密码 3.同一账 ...
- XML参考 :XmlReader 详解、实例
XML参考 :XmlReader 详解.实例-- 详解 转:http://www.cnblogs.com/Dlonghow/archive/2008/07/28/1252191.html XML参考 ...
随机推荐
- JVM gc介绍
Java语言出来之前,大家都在拼命的写C或者C++的程序,而此时存在一个很大的矛盾,C++等语言创建对象要不断的去开辟空间,不用的时候有需要不断的去释放控件,既要写构造函数,又要写析构函数,很多时候都 ...
- 编写jQuery插件(二)——jQuery插件类型和机制
jQuery插件类型 jQuery插件主要有3种类型: 1.封装对象方法的插件 这种插件类型是最常见的一种插件,它将对象方法封装起来,对通过选择器获取的jQuery对象进行操作. 2.封装全局函数的插 ...
- Python3 impyla 连接 hiveserver2
简介: 接到一个任务,需要从 hive 中读取数据,生成报表. 于是找到了官方文档:https://cwiki.apache.org/confluence/display/Hive/Setting+U ...
- sqlserver分布式 用触发器插入数据
这个月总公司收购了一家小公司,这家小公司的数据库用的是32位的 Sql2000 ,已经使用很长一段时间了,系统也比较稳定.本着节约成本的原则,总公司保留原公司的一套管理系统,但要求重要数据每天上传到总 ...
- python 学习备忘
list列表排序 def listdir(path): #返回一个排序后的目录list files=os.listdir(path) files.sort(key=lambda x:str(x[:-4 ...
- MVC 发布到IIS中的配置方法
MVC 发布到IIS中的配置方法 http://msdn.microsoft.com/zh-cn/library/gg703322(v=vs.98).aspx
- 【Java】JavaIO(二)、节点流
一.InputStream & outputStream Java字节流主要是以InputStream (输入流),outputStream(输出流)为基类,本身是抽象类不能创建实例,但是是字 ...
- pod优先级与抢占测试
# kubectl describe node k8s-n2Name: k8s-n2Roles: <none>Labels: ...
- dubbo hessian+dubbo协议
Dubbo缺省协议采用单一长连接和NIO异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况 Hessian协议用于集成Hessian的服务,Hessian底层采 ...
- linux 配置Tomcat开机启动
一台安装有tomcat的linux服务器 方法/步骤 1 请自行下载安装配置tomcat的服务器环境 本经验仅仅介绍如何配置tomcat的开机自动启动 2 切换到tomcat/bin目录下 用vi ...