如何使用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");

如何使用C#将BitmapData复制到Byte数组中

这些零是填充,因为您使用的是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;