如何使用C#将BitmapData复制到Byte数组中
本文关键字:Byte 数组 复制 BitmapData 何使用 | 更新日期: 2023-09-27 18:22:48
我想将BitmapData复制到byte[]中,但我在数组的中间得到了不存在的零(索引6和7)。我做错了什么?
Bitmap bt = new Bitmap(2, 2, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
for(int ii = 0; ii < bt.Width; ii++)
for(int jj = 0; jj < bt.Height; jj++)
{
int tempVal = (ii + jj * 2)*85;
bt.SetPixel(ii, jj, System.Drawing.Color.FromArgb(tempVal, tempVal, tempVal));
}
Rectangle rect = new Rectangle(0,0,bt.Width, bt.Height);
System.Drawing.Imaging.BitmapData btData = bt.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, bt.PixelFormat);
IntPtr ptr = btData.Scan0;
int bytes = bt.Width * bt.Height * 3;
byte[] rgbValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
bt.UnlockBits(btData);
for (var ii = 0; ii < bytes; ii++)
System.Diagnostics.Debug.WriteLine(rgbValues[ii]);
//bt.Save("test.png");
这些零是填充,因为您使用的是Format24bppRgb
格式,即每像素3个字节,因此在图像中的每行末尾都有一个填充。BitmapData.Stride
属性返回内存中一行的大小。对于自上而下的图像,这是一个正值,而对于自下而上的图像,则是负值。对于.NET内存位图,步长总是可以除以4。
因此,如果你想使用托管字节数组,你可以这样做:
byte[] data = new byte[Math.Abs(bitmapData.Stride * bitmapData.Height)];
Marshal.Copy(bitmapData.Scan0, data, 0, data.Length);
或者,如果你使用了不安全的代码,你可以像这样扫描:
unsafe
{
byte* line = (byte*)bitmapData.Scan0;
for (int y = 0; y < data.Height; y++)
{
for (int x = 0; x < data.Width; x++)
{
byte* pos = line + x * 3;
int pixel = Color.FromArgb(pos[0], pos[1], pos[2]).ToArgb();
// do whatever
}
line += data.Stride;
}
}
这是由于位图像素阵列格式需要填充每行起始偏移量以指向4的倍数的地址而设计的。
来自维基百科出于文件存储的目的,只有每行的大小必须是4字节的倍数,而文件偏移量可以是任意的。[5] 宽度=1的24位位图每行将有3个字节的数据(蓝色、绿色、红色)和1个字节的填充,而宽度=2将有2个字节的填补,宽度=3将有3字节的填补,宽度=4将根本没有任何填充。
顺便说一句,你的字节数计算似乎不正确,根据文档,应该是:
bytes = Math.Abs(btData.Stride) * bt.Height;