使用前面定义的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. ntohs, ntohl, htons,htonl的比较和详解【转】

    ntohs =net to host short int 16位 htons=host to net short int 16位 ntohs =net to host long int 32位 hto ...

  2. clientdataset 用法

    http://www.360doc.com/content/10/0709/01/2071424_37769962.shtml

  3. (1)shiro简介和第一个demo

    之前一直在用shiro开发,不过只是会使用,并没有深入了解,最近有时间学习了一下,把最近学习所得分享一下. shiro简介 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授 ...

  4. Access-Control-Allow-Origin与跨域

    文章转载地址: http://freewind.me/blog/20140327/2456.html 问题 在某域名下使用Ajax向另一个域名下的页面请求数据,会遇到跨域问题.另一个域名必须在resp ...

  5. RabbitMQ 高可用集群搭建

    面向EDA(事件驱动架构)的方式来设计你的消息 AMQP routing key的设计 RabbitMQ cluster搭建 Mirror queue policy设置 两个不错的RabbitMQ p ...

  6. HttpClient 超时时间

    setSoTimeout(MilSec):连接超时时间.如果在连接过程中有数据传输,超时时间重新计算. setConnectTimeout(MilSec):获取连接超时时间.如果该参数没有设置,那么默 ...

  7. 让IE浏览器支持CSS3表现

    http://www.zhangxinxu.com/wordpress/2010/04/%e8%ae%a9ie6ie7ie8%e6%b5%8f%e8%a7%88%e5%99%a8%e6%94%af%e ...

  8. MYSQL错误:You can't specify target table for update in FROM clause

    这句话意思是:不能先select再更新(修改)同一个表. 可以再外嵌套多一层,这个问题只有mysql有,mssql和oracle都没有. # 出错delete from Person where Id ...

  9. JS 图片切换

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="zzzz.aspx.cs&quo ...

  10. js td innerHTML

    用value不好使,用innerHTML可以.JS:document.getElementById("aa").innerHTML="单元格"; body:&l ...