使用前面定义的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(二) 实例的更多相关文章

  1. 自学vue笔记 (二) : 实例与生命周期

    一: 什么是实例 我们说了,Vue 应用都应该从构建一个 Vue 实例开始.它管理着挂载在它身上的所有内容,因此实例是一个根实例, 所有的组件都应该挂载在根实例上面.创建一个 Vue 实例,需要通过 ...

  2. Android Fragment (二) 实例2

    由于看客的要求,我就把读者所要的写出来. 由于上一篇是每一个Fragment 实例了同一个layout.xml ,造成了读者的困惑,这篇我就让每一个Fragment 加载一个不同的layout.xml ...

  3. Android Fragment (二) 实例1

    千呼万唤始出来,今天就也写一篇Frament 的简单实例.先看效果: 一看这效果,首先我们的配置资源文件:new -->android xml -->selector --> 四个图 ...

  4. vue 源码学习二 实例初始化和挂载过程

    vue 入口 从vue的构建过程可以知道,web环境下,入口文件在 src/platforms/web/entry-runtime-with-compiler.js(以Runtime + Compil ...

  5. oracle之二实例管理及数据库的启动/关闭

    实例管理及数据库的启动/关闭   2.1 实例和参数文件 1.instance 功能:用于管理和访问database.instance在启动阶段读取初始化参数文件(init parameter fil ...

  6. oracle之二实例与数据库

    实例与数据库 1.Oracle 网络架构及应用环境 看PPT,Oracle结构的基本单元.术语 2.Oracle 体系结构    1)oracle server :database + instanc ...

  7. Discuptor入门(二)-实例

    前言:最近在项目中看到有人使用的discuptor框架,因为没有接触过所以网上找了些资料.但最终发现开荒者太少,好像没什么人用那.最后感觉还是官方入门文档靠谱点.所以自己翻译了下(翻译器~),希望能帮 ...

  8. WCF学习之旅—HTTP双工模式(二十)

    WCF学习之旅—请求与答复模式和单向模式(十九) 四.HTTP双工模式 双工模式建立在上文所实现的两种模式的基础之上,实现客户端与服务端相互调用:前面介绍的两种方法只是在客户端调用服务端的方法,然后服 ...

  9. Python 文件读写,条件循环(三次登录锁定账号实例)

    通过文件读写,条件循环相关语法,实现三次登录失败则锁定该账号的功能 需求一 """需求描述: 1.输入正确账号,密码,退出程序 2.登录失败,重新输入账号密码 3.同一账 ...

  10. XML参考 :XmlReader 详解、实例

    XML参考 :XmlReader 详解.实例-- 详解 转:http://www.cnblogs.com/Dlonghow/archive/2008/07/28/1252191.html XML参考 ...

随机推荐

  1. DOM对象模型

  2. Nagios自定义扩展

    原理:监控端通过check_nrpe把要监控的指令发送给被监控端,被监控端在本机执行监控任务,并把执行的结果发送回监控端. 如何扩展Nagios,以实现自定义监控? 借助插件进行的每一次有效的Nagi ...

  3. delphi 中判断对象是否具备某一属性

    Uses   TypInfo;         {$R   *.dfm}         procedure   TForm1.Button1Click(Sender:   TObject);     ...

  4. toString方法的用法

    public class JLDtoS {   public static void main(String[]args)   {    long a=123;    Long aa=new Long ...

  5. ubuntu连有线网 无法连接外网

    问题:连上网线后,有ip,但是无法访问外网. 我的解决方案是: .通过命令行ifconfig命令查看以太网(即网线插口)的名称,如下图,'enp3s0'是网线插口(Ethernet以太网): zhum ...

  6. monkey初接触

    第一次听说monkey,根本不知道是什么东西,脑海里就一个印象,很厉害的自动化测试工具,可是体验了一下,似乎不是那么回事... 一.Monkey 是什么? monkey就是SDK中附带的一个工具. 二 ...

  7. Android logcat输出中文乱码

    使用adb的logcat 命令查看系统日志缓冲区的内容,会发现在CMD的界面面,直接输出的中文内容是乱码. 这个问题出现在使用logcat将日志直接打印在当前的DOS窗口的时候会出现:使用logcat ...

  8. Django的cookie学习

    为什么要有cookie,因为http是无状态的,每次请求都是独立的,但是我们还需要保持状态,所以就有了cookie cookie就是保存在客户端浏览器上的键值对,别人可以利用他来做登陆 rep = r ...

  9. php.ini memory_limit引起的问题

    故障现象    在运行PHP程序,通常会遇到“Fatal Error: Allowed memory size of xxxxxx bytes exhausted”的错误, 这个意味着PHP脚本使用了 ...

  10. Spring整合Struts2框架的第二种方式(Action由Spring框架来创建)(推荐大家来使用的)

    1. spring整合struts的基本操作见我的博文:https://www.cnblogs.com/wyhluckdog/p/10140588.html,这里面将spring与struts2框架整 ...