这里首先要介绍一个类System.Drawing.Imaging.BitmapData,直接实例化这个类没有用处,我们需要将一个Bitmap锁定到内存中,来获取一个BitmapData的实例。方法如下:
使用Bitmap.LockBits(Rectangle rect, ImageLockMode flags, PixelFormat format)或者它的另一个重载Bitmap.LockBits(Rectangle rect, ImageLockMode flags, PixelFormat format, BitmapData bitmapData)来将图像数据锁定到内存中,以此来获取一个与指定图片相关联的BitmapData实例。
在BitmapData中有一个重要的属性Scan0,它是一个指针,指向了图片数据所在内存的第一个位置。使用内存跟踪,将Scan0的值填入地址中,可以看到内存的分配情况(Format32bppArgb颜色深度):
这些值与图片像素的对应关系是这样的:
现在我们可以使用System.Runtime.InteropServices.Marshal.WriteByte(IntPtr ptr, byte val)的方法来更改指定位置的像素值了,修改后只要再调用一次Bitmap.UnlockBits(BitmapData bitmapdata)来解锁内存就可以了,例如:
以下为引用的内容:
private void LockUnlockBitsExample(PaintEventArgs e)
{
Bitmap bmp = new Bitmap("c:\\fakePhoto.jpg");
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData =
bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0;
int bytes = bmp.Width * bmp.Height * 3;
byte[] rgbValues = new byte[bytes];
for (int counter = 0; counter < rgbValues.Length; counter += 3)
{
Marshal.WriteByte(ptr, counter, 255);
}
bmp.UnlockBits(bmpData);
e.Graphics.DrawImage(bmp, 0, 0);
}
此示例将图片上所有像素的Red向量设置为255。运行此实例可以看到图片变色了。
每次调用System.Runtime.InteropServices.Marshal.WriteByte(IntPtr ptr, byte val)的方法并不方便,因此我们构造一个ColorBgra类用来储存这4个颜色向量,它的主要代码是这样的(参考自Paint.Net提供的源码):
以下为引用的内容:
[StructLayout(LayoutKind.Explicit)]
public struct ColorBgra
{
[FieldOffset(0)]
public byte B;
[FieldOffset(1)]
public byte G;
[FieldOffset(2)]
public byte R;
[FieldOffset(3)]
public byte A;
/// <summary>
/// Lets you change B, G, R, and A at the same time.
/// </summary>
[FieldOffset(0)]
public uint Bgra;
public override string ToString()
{
return "B: " + B + ", G: " + G + ", R: " + R + ", A: " + A;
}
}
使用这个类在声明为unsafe的上下文中就可以通过计算偏移量的办法寻址找到指定位置像素的地址(指针),例如在Format32bppArgb颜色深度的图片中可以这样计算:
以下为引用的内容:
public unsafe ColorBgra* GetPointAddress(int x, int y)
{
return y * 4 + x;
}
将计算返回的指针赋给ColorBgra*。之后使用如下方法:
以下为引用的内容:
color->B = i;
color ->G = i;
color ->R = i;
color ->A = i;
直接把值写入内存中,实现对图片像素的快速操作。