Michael Ivanov Michael Ivanov - 1 month ago 19
C# Question

Generate 16-bit grayscale BitmapData and save to file

I am trying to generate 16bit grayscale Bitmap in C# from a random data.But it crashed on Marshal.Copy.

Here is my code:

Bitmap b16bpp;
private void GenerateDummy16bitImage()
{

b16bpp = new Bitmap(IMAGE_WIDTH, IMAGE_HEIGHT, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);

var rect = new Rectangle(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
var bitmapData = b16bpp.LockBits(rect, ImageLockMode.WriteOnly, b16bpp.PixelFormat);
// Calculate the number of bytes required and allocate them.
var numberOfBytes = bitmapData.Stride * IMAGE_HEIGHT * 2;
var bitmapBytes = new short[numberOfBytes];
// Fill the bitmap bytes with random data.
var random = new Random();
for (int x = 0; x < IMAGE_WIDTH; x++)
{
for (int y = 0; y < IMAGE_HEIGHT; y++)
{

var i = ((y * IMAGE_WIDTH) + x) * 2; // 16bpp

// Generate the next random pixel color value.
var value = (short)random.Next(5);

bitmapBytes[i] = value; // BLUE
bitmapBytes[i + 1] = value; // GREEN
bitmapBytes[i + 2] = value; // RED
// bitmapBytes[i + 3] = 0xFF; // ALPHA
}
}
// Copy the randomized bits to the bitmap pointer.
var ptr = bitmapData.Scan0;
Marshal.Copy(bitmapBytes, 0, ptr, numberOfBytes);//crashes here

// Unlock the bitmap, we're all done.
b16bpp.UnlockBits(bitmapData);

b16bpp.Save("random.bmp", ImageFormat.Bmp);
Debug.WriteLine("saved");
}


The exception is:

An unhandled exception of type 'System.AccessViolationException' occurred in mscorlib.dll

This is not my code.I found it in relation to 32bit Bitmaps and modified.But I guess I have missed something as I am pretty new to C#.

Basically,all I need is to wrap into BitmapData an arrays of shorts.

Answer

I have corrected some of your mistakes (mostly wrong sizes). But it will still crash on b16bpp.Save(), because GDI+ does not support saving 16bit grayscale images.

Bitmap b16bpp;
private void GenerateDummy16bitImage()
{

    b16bpp = new Bitmap(IMAGE_WIDTH, IMAGE_HEIGHT, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);

    var rect = new Rectangle(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
    var bitmapData = b16bpp.LockBits(rect, ImageLockMode.WriteOnly, b16bpp.PixelFormat);
    // Calculate the number of bytes required and allocate them.
    var numberOfBytes = bitmapData.Stride * IMAGE_HEIGHT;
    var bitmapBytes = new short[IMAGE_WIDTH * IMAGE_HEIGHT];
    // Fill the bitmap bytes with random data.
    var random = new Random();
    for (int x = 0; x < IMAGE_WIDTH; x++)
    {
        for (int y = 0; y < IMAGE_HEIGHT; y++)
        {

            var i = ((y * IMAGE_WIDTH) + x); // 16bpp

            // Generate the next random pixel color value.
            var value = (short)random.Next(5);

            bitmapBytes[i] = value;         // GRAY
        }
    }
    // Copy the randomized bits to the bitmap pointer.
    var ptr = bitmapData.Scan0;
    Marshal.Copy(bitmapBytes, 0, ptr, bitmapBytes.Length);//crashes here

    // Unlock the bitmap, we're all done.
    b16bpp.UnlockBits(bitmapData);

    b16bpp.Save("random.bmp", ImageFormat.Bmp);
    Debug.WriteLine("saved");
}

Explanation of my changes:

  • bitmapData.Stride is already IMAGE_WIDTH * BytesPerPixel so you don't need to multiply by 2
  • as you declared bitmapBytes as short[] it has to have the size of the image in pixels not in bytes
  • that means you also do not need to multiply i by 2
  • since you have a grayscale image it does not have a blue, green and red channel, but one single 16bit gray channel
  • Marshal.Copy takes the length in "array units" not in bytes

All in all you tried to copy an array 8 times to large into the bitmap.