1

I'm using a similar method like this answer to create a bitmap from a byte array.

private static void MySaveBMP(byte[] buffer, int width, int height)
{
    Bitmap b = new Bitmap(width, height, PixelFormat.Format24bppRgb);
    Rectangle BoundsRect = new Rectangle(0, 0, width, height);
    BitmapData bmpData = b.LockBits(BoundsRect,
                                    ImageLockMode.WriteOnly,
                                    b.PixelFormat);
    IntPtr ptr = bmpData.Scan0;

    // fill in rgbValues
    Marshal.Copy(buffer, 0, ptr, buffer.Length);
    b.UnlockBits(bmpData);
    b.Save(@"D:\myPic.bmp", ImageFormat.Bmp);        
}

And I generate a byte array, fill in some values like this:

int width = 20;
int height = 30;
int bytesPerPixel = 3;
int bytesPerRow = width * bytesPerPixel;
int totalLength = bytesPerRow * height;            
byte[] managedArray = new byte[totalLength];

// fill background with white
for (int i = 0; i < totalLength; i++)
    managedArray[i] = 255;

// draw on each row
for (int i = 0; i < height; i++)
{
    // first pixel is green
    managedArray[0 + i * bytesPerRow] = 0;
    managedArray[1 + i * bytesPerRow] = 255;
    managedArray[2 + i * bytesPerRow] = 0;
    // last pixel is red
    managedArray[bytesPerRow - 3 + i * bytesPerRow] = 0;
    managedArray[bytesPerRow - 2 + i * bytesPerRow] = 0;
    managedArray[bytesPerRow - 1 + i * bytesPerRow] = 255;
}
MySaveBMP(managedArray, width, height);

The resulting 20x30 bmp image looks like this:

20x30 bmp image

However, if I change the image height (for example, to 21), the resulting image seems corrupted. Each row looks like it shift to left a bit:

21x30 bmp image

What am I doing wrong while making the bitmap image?

Dia
  • 851
  • 1
  • 15
  • 35
  • 1
    Where’s the code where you call MySaveBMP? Also, I’d add parens under “last pixel is red” just to clarify the order of operations between the + and -. Don’t think it’s your problem, but – zzxyz Dec 21 '17 at 06:05

1 Answers1

4

I think I found the answer. Because I didn't aware of BitmapData.Stride attribute. Someone has a answer here.

My modified save function is here:

    private static void MySaveBMP(byte[] buffer, int width, int height)
    {
        Bitmap b = new Bitmap(width, height, PixelFormat.Format24bppRgb);

        Rectangle BoundsRect = new Rectangle(0, 0, width, height);
        BitmapData bmpData = b.LockBits(BoundsRect,
                                        ImageLockMode.WriteOnly,
                                        b.PixelFormat);

        IntPtr ptr = bmpData.Scan0;

        // add back dummy bytes between lines, make each line be a multiple of 4 bytes
        int skipByte = bmpData.Stride - width*3;
        byte[] newBuff = new byte[buffer.Length + skipByte*height];
        for (int j = 0; j < height; j++)
        {
            Buffer.BlockCopy(buffer, j * width * 3, newBuff, j * (width * 3 + skipByte), width * 3);
        }

        // fill in rgbValues
        Marshal.Copy(newBuff, 0, ptr, newBuff.Length);
        b.UnlockBits(bmpData);
        b.Save(@"D:\myPic.bmp", ImageFormat.Bmp);        
    }

Another solution is that, I change the PixelFormat to Format32bppPArgb, and change bytesPerPixel to 4. So I don't need to bother with the 4-bytes scan line format.

Dia
  • 851
  • 1
  • 15
  • 35
  • Yes, `Stride` is a real oddity to figure out. Basically it's usually aligned to 4 bytes. Though never _count_ on that, and always just request the stride from the bitmapdata object. I've worked with indexed images, and on 4-bit and 1-bit images they do not follow that rule. – Nyerguds Jan 28 '18 at 00:40