8

I have a byte[] array received in TCP Client.The array contains a 24-bit RGB Bitmap file.How to create that bitmap file with given Width ,Height and data?

In C++ I use this

int WriteBitmapFile(const char *filename, int width, int height, unsigned char *imageData)
{
FILE             *filePtr;        // file pointer
BITMAPFILEHEADER bitmapFileHeader;    // bitmap file header
BITMAPINFOHEADER bitmapInfoHeader;    // bitmap info header
DWORD                 imageIdx;    // used for swapping RGB->BGR
unsigned char     tempRGB;            // used for swapping

// open file for writing binary mode
filePtr = fopen(filename, "wb");
if (!filePtr)
    return 0;

// define the bitmap file header
bitmapFileHeader.bfSize = sizeof(BITMAPFILEHEADER);
bitmapFileHeader.bfType = 0x4D42;
bitmapFileHeader.bfReserved1 = 0;
bitmapFileHeader.bfReserved2 = 0;
bitmapFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

// define the bitmap information header
bitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfoHeader.biPlanes = 1;
bitmapInfoHeader.biBitCount = 32;                        // 24-bit
bitmapInfoHeader.biCompression = BI_RGB;                // no compression
bitmapInfoHeader.biSizeImage = width * abs(height) * 4;    // width * height * (RGB bytes)
bitmapInfoHeader.biXPelsPerMeter = 0;
bitmapInfoHeader.biYPelsPerMeter = 0;
bitmapInfoHeader.biClrUsed = 0;
bitmapInfoHeader.biClrImportant = 0;
bitmapInfoHeader.biWidth = width;                        // bitmap width
bitmapInfoHeader.biHeight = height;                    // bitmap height

// switch the image data from RGB to BGR
for(imageIdx = 0; imageIdx < bitmapInfoHeader.biSizeImage; imageIdx+=4)
{
    tempRGB = imageData[imageIdx];
    imageData[imageIdx] = imageData[imageIdx + 2];
    imageData[imageIdx + 2] = tempRGB;
}

// write the bitmap file header
fwrite(&bitmapFileHeader, 1, sizeof(BITMAPFILEHEADER), filePtr);

// write the bitmap info header
fwrite(&bitmapInfoHeader, 1, sizeof(BITMAPINFOHEADER), filePtr);

// write the image data
fwrite(imageData, 1, bitmapInfoHeader.biSizeImage, filePtr);

// close our file
fclose(filePtr);

// Success
return 1;
}

How could I do that in C#?

Treb
  • 19,903
  • 7
  • 54
  • 87
Ivan Prodanov
  • 34,634
  • 78
  • 176
  • 248

4 Answers4

12

If the array actually contains a bitmap file, then you can just save the bytes as a file:

File.WriteAllBytes(fileName, imageData);

If the array contains only raw pixel data, you can create a Bitmap object using the data:

unsafe {
   fixed (byte* ptr = imageData) {
      using (Bitmap image = new Bitmap(width, height, stride, PixelFormat.Format24bppRgb, new IntPtr(ptr))) {
         image.Save(fileName);
      }
   }
}

The stride value is the number of bytes between the scan lines. If there is no padding between the scan lines, it's width * 3 for a 24bpp format.

This method uses the data in the array without creating another copy of the entire image in memory (which is why it needs the stride value).

If the bitmap data is stored upside down in the array, the stride value should be negative, and the pointer should be the start of the last scan line in memory (ptr + stride * (height - 1)).

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • "new Bitmap(width, height, stride" What is stride? I misunderstood. – Ivan Prodanov Apr 12 '09 at 21:05
  • The stride value is the number of bytes from the start address of a scan line to the start address of the next scan line. For 24 bit RGB data with no extra padding the stride value is width * 3. – Guffa Apr 12 '09 at 21:20
  • With 600 the image isn't clear at all. With 800 stride ,The image is more clear,but still not the right stride. http://img26.imageshack.us/img26/2024/testfak.png Maybe the problem isn't in strides? – Ivan Prodanov Apr 12 '09 at 21:22
  • Note - it should be something like this -> http://img26.imageshack.us/img26/4329/6e5647.png ,but its not :( – Ivan Prodanov Apr 12 '09 at 21:24
  • The stride seems to be slightly off. If width * 4 is closer than 3 the data may actually be 32bppRgb. The scan lines might be padded to an even four byte boundary. Also as the image is upside down the stride should be negative and you need to point to the last scan line in memory. – Guffa Apr 12 '09 at 21:56
  • Its 32 bit,but its reversed :( http://stackoverflow.com/questions/743037/i-get-the-image-reversed-in-c – Ivan Prodanov Apr 13 '09 at 04:27
10

I can't test it using the stream you will be receiving, but this should work.

int WriteBitmapFile(string filename, int width, int height, byte[] imageData)
{
  using (var stream = new MemoryStream(imageData))
  using (var bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb))
  {
    BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0,
                                                    bmp.Width,
                                                    bmp.Height),
                                      ImageLockMode.WriteOnly,
                                      bmp.PixelFormat);

    Marshal.Copy(imageData, 0, bmpData.Scan0, imageData.Length);

    bmp.UnlockBits(bmpData);

    bmp.Save(filename);
  }

  return 1;
}
Samuel
  • 37,778
  • 11
  • 85
  • 87
  • The first using-Statement ist totally useless and just slows down the whole process, as the MemoryStream will be destroyed at the end of the function also without this using. The GarbageCollector does this for you. Using without brackets just slows down. – Oliver Friedrich Apr 12 '09 at 19:41
  • 1
    This would require the bitmap header information to already exist in his byte array - it doesn't appear that it does atm. – Reed Copsey Apr 12 '09 at 19:41
  • 2
    @BeowulfOF: The using on MemoryStream is not useless. You should always close (or Dispose) a memory stream. The MemoryStream will be available for cleanup after this function ends, but is not guaranteed to be disposed of at any specific time, so could stay open for a long time. – Reed Copsey Apr 12 '09 at 19:43
  • 3
    @BeowulfOF: Please refrain from commenting if you have no idea what you are talking about. The overhead from using statements is almost *nothing* and you are supposed to dispose of all IDisposable objects unless you need them to survive. People like you are the reason why we still need finalizers. – Samuel Apr 12 '09 at 19:53
  • I get exception on line "Marshal.Copy(imageData, 0, bmpData.Scan0, imageData.Length);" Memory is corrupt.ImgData has the length of 64 * 200 and the imageData.Length is exactly 64 * 200.Height is 64 and Width is 200. – Ivan Prodanov Apr 12 '09 at 20:13
  • WriteBitmapFile("test.bmp", 64, 200, new byte[64 * 200]); works for me. It's hard to tell what's wrong without a sample stream. – Samuel Apr 12 '09 at 20:21
  • Copying the data like that only works if there is no extra padding between scan lines (bmpData.Stride == width * 3) and the bitmap is not stored upside down in memory (bmpData.Stride > 0). Otherwise you get a distorted image or a memory access exception. – Guffa Apr 12 '09 at 21:16
  • It seems that he might not know the stride, so he may be screwed unless it's the default. – Samuel Apr 12 '09 at 21:27
6

I'd recommend making a Bitmap in C#, and letting it save itself.

For an example, see this post. (Particularly, the last response is correct.)

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
2

this is one way of doing it, here i have created a custom Event args that contains the size at which the image was stored as a byte array. You may not need to bother with this, this was code i created to retreive images from a byte array that a gige camera was storing to so for me this made sence.

public Bitmap ShowImage(byte[] sender, EventImageParams e)
    {
        Bitmap bitmap = new Bitmap(e.width, e.height, PixelFormat.Format24bppRgb);
        BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                                            ImageLockMode.ReadWrite, bitmap.PixelFormat);
        IntPtr pNative = bmData.Scan0;

        Marshal.Copy(sender, 0, pNative, (e.width  * e.height * 3));
     //    
        bitmap.UnlockBits(bmData);

        return bitmap;
    }
Colm Clarke
  • 480
  • 1
  • 7
  • 23
  • Hi Colm, since this is your custom code, can you please tell me how did you declare EventImageParams. I would like to see the code for "EventImageParams"... Can you please add this also to your answer. – dovla110010101 Feb 08 '16 at 08:58