使用前面定义的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. 工作中用到和应该知道的eclipse快捷键

    Eclipse最初是由IBM公司开发的替代商业软件Visual Age for Java的下一代IDE开发环境,2001年11月贡献给开源社区,现在它由非营利软件供应商联盟Eclipse基金会(Ecl ...

  2. JAVA WEB开发中的资源国际化

    为什么要国际化? 不同国家与地区语言,文化,生活习惯等差异.在数字,时间,语言,货币,日期,百分数等的不同. 两个名词: I18N:即资源国际化,全称为Internationalization,因为首 ...

  3. 一个取消事件的简单js例子(事件冒泡与取消默认行为)

    先上代码: <div id='outer' onclick='alert("我是outer")'> <div id="middle" oncl ...

  4. Xtrabackup安装及使用

    官方安装步骤:https://www.percona.com/doc/percona-xtrabackup/2.4/installation/yum_repo.html 安装percona repo源 ...

  5. HTML meta 文本 格式排版 链接图表 列表 表单 frame后台布局实例

    meta标签 content属性必须和http-equiv或者name属性一起使用 http-equiv属性,就是http当量,用于和服务器发送数据前的提交交互使用.(另层含义这个当量在浏览器和服务器 ...

  6. js 删除节点,jquery遍历通过内容定位节点

    $(".class1 .class2").each(function (index, item) { var gettedValue = $(item).find(".c ...

  7. keynote

    [keynote] 1.如何保证文档加载完才运行代码? 2.元素选择器. 3.属性选择器. 4.更新css. 5.更复杂的例子. 6.常用事件. 7.hide & slow 8.您可以使用 t ...

  8. My Goal For SE

    2016年2月份,我开始进入软件工程的学习.作为一名大三学生,我对于此项课程,希望我自己能够通过制定学习目标来学好它. 首先,当我们在执行软件开发的时候,我们应该明确每个人的负责模块,我们应该明确自己 ...

  9. poj3186(区间DP)

    题目链接:http://poj.org/problem?id=3186 思路: 区间DP,给treat编号为1..n,状态很明显是上界i和下界j,dp[i][j]表示从下标i到下标j之间数据的最大价值 ...

  10. eclipse搭建struts2环境及所遇到的问题

    最近几天一直在搭建struts2框架,本身struts2框架的搭建是非常简单的,但不知道为什么最近就是总是报错,报了一大串的错 首先就是每次在类的根路径下创建struts.xml时,就报错,也不知道为 ...