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参考 ...
随机推荐
- Mybatis知识(3)
1.JDBC编程有哪些不足之处,MyBatis是如何解决这些问题的? ① 数据库链接创建.释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题. 解决:在SqlMapConfig ...
- java是如何编码解码的
在上篇博客中LZ阐述了java各个渠道转码的过程,阐述了java在运行过程中那些步骤在进行转码,在这些转码过程中如果一处出现问题就很有可能会产生乱码!下面LZ就讲述java在转码过程中是如何来进行编码 ...
- 书单BookList
1. <代码大全> 史蒂夫·迈克康奈尔 (Code Complete) 2. <程序员修炼之道> Andrew Hunt [读过了,非常好的一本书] (Pragmatic Pr ...
- Why ExerciseAlone May Not Be the Key to Weight Loss
加强运动也许并不能减肥Why ExerciseAlone May Not Be the Key to Weight LossIf you give a mouse a running wheel, i ...
- (转)游戏引擎中三大及时光照渲染方法介绍(以unity3d为例)
重要:在目前市面上常见的游戏引擎中,主要采用以下三种灯光实现方式: 顶点照明渲染路径细节 Vertex Lit Rendering Path Details 正向渲染路径细节 Forward Rend ...
- 给pyspark 设置新的环境
如果是从pysparkshell里面进去,此时已经有了pyspark环境了,所以要加一个:sc.stop() from pyspark import SparkContext, SparkConfsc ...
- 根据条件决定My97DatePicker日期控件弹出的日期格式
代码如下: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <tit ...
- 【Java】JVM(一)、Java内存区域
一.程序计数器(Program Counter Register) 当前执行字节码的行号指示器,可以通过修改该计数器的值来实现字节码指令(分支,循环,跳转等), 每个线程都都有一个程序计数器, 属于线 ...
- Python oct() 函数
Python oct() 函数 Python 内置函数 描述 oct() 函数将一个整数转换成8进制字符串. 语法 oct 语法: oct(x) 参数说明: x -- 整数. 返回值 返回8进制字符 ...
- Largest Rectangle in a Histogram(附上几组测试数据)
Largest Rectangle in a Histogram http://acm.hdu.edu.cn/showproblem.php?pid=1506 Time Limit: 2000/100 ...