WriteableBitmap(三) 扩展
backbuffer使用您在创建WriteableBitmap时指定的像素格式,还有一个BackBufferStride属性,您可以使用它来创建一个合适的存储映射函数。
添加一些方法来设置和获取特定情况下的像素是很容易的,方法是在静态类中定义两个新的扩展方法,创建静态类只是为了承载扩展方法:
public static class bitmapextensions
{
setPixel方法首先检查x和y坐标是否在正确的范围内,格式是否为Brga32(您可以扩展该方法来处理其他格式):
public static void setPixel(this WriteableBitmap wbm, int x, int y, Color c)
{
if (y > wbm.PixelHeight - 1 || x > wbm.PixelWidth - 1) return;
if (y < 0 || x < 0) return;
if (!wbm.Format.Equals(PixelFormats.Bgra32))return;
然后它会得到backbuffer的详细信息:
wbm.Lock();
IntPtr buff = wbm.BackBuffer;
int Stride = wbm.BackBufferStride;
然后计算存储映射函数来访问x、y位置的像素,并将指定的颜色分割存储为像素格式对应的4个字节:
unsafe
{
byte* pbuff = (byte*)buff.ToPointer();
int loc=y *Stride + x*4;
pbuff[ loc]=c.B;
pbuff[loc+1]=c.G;
pbuff[loc+2]=c.R;
pbuff[loc+3]=c.A;
}
最后我们将像素标记为dirty并解锁WriteableBitmap:
wbm.AddDirtyRect(new Int32Rect(x,y,1,1));
wbm.Unlock();
}
getPixel方法非常类似,只是它汇编并返回一个Color struct:
public static Color getPixel( this WriteableBitmap wbm, int x, int y)
{
if (y > wbm.PixelHeight - 1 || x > wbm.PixelWidth - 1) return Color.FromArgb(0, 0, 0, 0);
if (y < 0 || x < 0) return Color.FromArgb(0, 0, 0, 0);
if (!wbm.Format.Equals(PixelFormats.Bgra32)) return Color.FromArgb(0, 0, 0, 0);;
IntPtr buff = wbm.BackBuffer;
int Stride = wbm.BackBufferStride;
Color c;
unsafe
{
byte* pbuff = (byte*)buff.ToPointer();
int loc = y * Stride + x * 4;
c=Color.FromArgb(pbuff[loc+3],pbuff[loc+2],pbuff[loc+1],pbuff[loc]);
}
return c;
}
请注意,由于我们只是访问位而不更改它们,因此没有必要将某个区域锁定或标记为dirty。
这两个扩展方法很容易使用,但是如果要操作大量像素,可能需要考虑使用更直接的方法。通过Pixel属性直接访问并分配预先计算的整数颜色值总是比使用一般方法和对象要快。
定义了这些扩展方法后,我们现在可以给出一个访问像素的例子:
wbmap.setPixel(5,10 , Colors.Red);
Color c= wbmap.getPixel(5, 10);
最后,作为一个动态图像的例子,下面根据一个简单的公式,绘制了一个依赖于像素位置的颜色范围:
private void button1_Click(object sender, RoutedEventArgs e)
{
WriteableBitmap wbmap = new WriteableBitmap(256, 256, 300, 300, PixelFormats.Bgra32, null);
for (int x = 0; x < 256; x++)
{
for (int y = 0; y < 256; y++)
{
wbmap.setPixel( x,y,Color.FromArgb(255,(byte) (x*x+y), (byte) (y*y+x),(byte)(x+y)));
}
}
image1.Source = wbmap;
}