0
votes

I want to copy BitmapData into byte[], but I get non existant zeroes in the middle of array (indexes 6 and 7). What I am doing wrong?

        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

2 Answers

4
votes

Those zeros are paddings because you use a Format24bppRgb format, which is 3 bytes per pixel so there is a padding at the end of each line in the image. The BitmapData.Stride property returns the size of a line in memory. For top-down images this is a positive value, for bottom-up ones a negative value. The stride always can be divided by 4 for the .NET memory bitmaps.

So if you want to use a managed byte array, you can do it like this:

byte[] data = new byte[Math.Abs(bitmapData.Stride * bitmapData.Height)];
Marshal.Copy(bitmapData.Scan0, data, 0, data.Length);

Or, if you use unsafe code, you can scan through the lines like this:

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;
     }
 }
0
votes

This is by design as bitmap pixel array format requires to pad each row start offset to point to an address that is a multiple of 4.

From Wikipedia For file storage purposes, only the size of each row must be a multiple of 4 bytes while the file offset can be arbitrary.[5] A 24-bit bitmap with Width=1, would have 3 bytes of data per row (blue, green, red) and 1 byte of padding, while Width=2 would have 2 bytes of padding, Width=3 would have 3 bytes of padding, and Width=4 would not have any padding at all.

By the way, your number of bytes calculation seems to be incorrect, and according to the documentation, instead should be:

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