在.NET平台的跨平台应用开发中,无论是Windows Forms、WPF、Silverlight还是Universal Apps,它们处理像素数据的方式各有不同。本文将介绍如何在Universal Apps中使用WriteableBitmap进行像素级操作,这是在之前的文章中提到的“Simple Life Simulation”应用的核心部分。
WriteableBitmap类是.NET跨平台应用中处理像素数据的关键。在Universal Apps中,这个类与Silverlight和WPF中的同名类有着相同的名称,因此理论上应该很容易使用。然而,MSDN文档中提到了一种访问像素内容的方法:
"To access the pixel content from C# or Microsoft Visual Basic, you can use the AsStream extension method to access the underlying buffer as a stream."
这让感到困惑,因为这种方法并不是期望的。文档接着提到:
"To access the pixel content from C++, you can query for the IBufferByteAccess type (defined in Robuffer.h) and directly access its Buffer property."
这正是期待的解决方案,但为什么C#不能使用相同的方法呢?
经过一番研究,发现可以使用C#直接访问字节缓冲区/BGRA缓冲区,尽管需要使用unsafe代码。以下是在C#中实现这一功能的步骤:
首先,需要在C#中导入IBufferByteAccess接口:
[ComImport]
[Guid("905a0fef-bc53-11df-8c49-001e4fc686da")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal unsafe interface IBufferByteAccess
{
byte* Buffer { get; }
}
这里最重要的是接口上使用的属性,这些属性使得.NET能够将接口派发到COM。
考虑到WriteableBitmap总是以BGRA格式存储像素数据(每个像素由8位的B、G、R、A组成),定义了以下结构来表示像素/颜色:
[StructLayout(LayoutKind.Sequential)]
public struct Bgra
{
public byte B;
public byte G;
public byte R;
public byte A;
}
实际上,可以更改Buffer属性的返回类型为Bgra*,但为了保持接口的完整性(与C++中的相同),选择自己进行类型转换。
最后,来讨论如何使用WriteableBitmap。假设有一个writeableBitmap变量,需要执行以下操作来获取可以操作的缓冲区:
unsafe
{
var pixelBuffer = writeableBitmap.PixelBuffer;
var bufferByteAccess = (IBufferByteAccess)pixelBuffer;
var pixels = (Bgra *)bufferByteAccess.Buffer;
// 剩余的代码在这里
}
然后,可以选择要操作的像素。例如,要访问单个像素,使用公式(y*bitmapWidth) + x。如果要访问所有像素(无论是读取还是写入),可以这样做:
for (int i = 0; i < width * height; i++)
{
// 在这里使用pixels[i]进行像素读取/操作
}