views:

5934

answers:

3

Hello,

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#?

+3  A: 

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
+3  A: 

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
Couldn't you just dump the byte[] right to disk?
Jon B
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.
BeowulfOF
This would require the bitmap header information to already exist in his byte array - it doesn't appear that it does atm.
Reed Copsey
@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
@Reed: I've updated it to use the solution linked in.
Samuel
@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
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.
John
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
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
It seems that he might not know the stride, so he may be screwed unless it's the default.
Samuel
+3  A: 

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
"new Bitmap(width, height, stride"What is stride? I misunderstood.
John
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
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.pngMaybe the problem isn't in strides?
John
Note - it should be something like this -> http://img26.imageshack.us/img26/4329/6e5647.png ,but its not :(
John
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
Its 32 bit,but its reversed :(http://stackoverflow.com/questions/743037/i-get-the-image-reversed-in-c
John