Search code examples
c#arraysbitmapdata

How can I copy BitmapData into Byte array using C#?


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");

Solution

  • 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;
         }
     }