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

时间:2015-11-20 12:58:51

标签: c# bytearray bitmapdata

我想将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");

2 个答案:

答案 0 :(得分:3)

这些零是填充,因为你使用Format24bppRgb格式,每个像素3个字节,所以在图像的每一行的末尾有一个填充。 BitmapData.Stride属性返回内存中行的大小。对于自上而下的图像,这是一个正值,对于自下而上的图像,这是一个负值。对于.NET内存位图,stride总是可以除以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;
     }
 }

答案 1 :(得分:0)

这是设计为位图像素阵列格式需要填充每一行的起始偏移量以指向一个4的倍数的地址。

来自Wikipedia 出于文件存储的目的,只有每行的大小必须是4个字节的倍数,而文件偏移量可以是任意的。[5]宽度= 1的24位位图,每行有3个字节的数据(蓝色,绿色,红色)和1个字节的填充,而 宽度= 2则有2个字节的填充 ,Width = 3将有3个字节的填充,而Width = 4则根本没有任何填充。

顺便说一下,你的字节数计算似乎是不正确的,根据the documentation,应该是:

bytes = Math.Abs(btData.Stride) * bt.Height;