Problems writing a BMP exporter

This topic is 1007 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

I am having some troubles when generating a .bmp file, from the code below i expected it to come out in shades of red, but as you'll see from the included picture it becomes both blue and green instead. Any ideas why this is happening?

struct Pixel
{
unsigned char r;
unsigned char g;
unsigned char b;
};


The main function generates the vector that holds all the pixel data, the color depends on the height of the image.

int main()
{
std::vector<Pixel> ImageData;
unsigned int Width = 640;
unsigned int Height = 480;

for (unsigned int i = 0; i < Height; i++)
{
for (unsigned int j = 0; j < Width; j++)
{
Pixel temp;
temp.r = i % 255;
temp.g = 0;
temp.b = 0;
ImageData.push_back(temp);
}
}

WriteBitMap("raytracer.bmp", Width, Height, ImageData);

return 0;
}


Writes the 2 headers needed for BMP files and then the image data.
bool WriteBitMap(std::string Filename, const unsigned int Width, const unsigned int Height, std::vector<Pixel> ImageData)
{

fh.bmtype[0] = 'B';
fh.bmtype[1] = 'M';

ih.iWidth = Width;
ih.iHeight = Height;
ih.iPlanes = 1;
ih.iBitCount = 24;
ih.Compression = 0;

//Open file for writing

std::ofstream file;
file.open(Filename, std::ios::out);

for (auto it = ImageData.begin(); it != ImageData.end(); ++it)
{
char red = it->r;
char green = 0;
char blue = 0;
file.write(&blue, sizeof(char));
file.write(&green, sizeof(char));
file.write(&red, sizeof(char));
}

file.close();
return false;
}


Edit:

See solution further down

File needed to be opened in binary mode.

Edited by frob
Mod note, please don't mark as solved.

Share on other sites

To add on on the upside down:

If you store height as negative the bitmap data is top down instead.

Also, for iFileSize you must use the size calculated with scanline size as Gl2eenDl2agon described.

Share on other sites

First:

Bitmaps are stored BGR and not RGB.

I do store them that way:

for (auto it = ImageData.begin(); it != ImageData.end(); ++it)
{
char red = it->r;
char green = 0;
char blue = 0;
file.write(&blue, sizeof(char));
file.write(&green, sizeof(char));
file.write(&red, sizeof(char));
}


Second:

Bitmaps are 4 byte aligned. This means that each horizontal line comes out to a multiple of 4 bytes.

Typically this is called "scanline size". This means that ( y * w + x ) *3 doesn't work.

You will have to find the scanline size by finding the smallest multiple of 4 larger than the width in bytes.

Like for( int scansize = 0; scansize < w_bytes; scansize += 4); will work (but not the best way).

You will need to allocate a buffer of (height * scanline_size) bytes and find the offset for each pixel by ( y * scaline_size + x * bytes_per_pixel).

In other words a generic 2d array wont work unless you can be sure that the scanline size is the same as (width * bytes per pixel).

I thought using a width of 640 would be aligned since:

640 pixels * 3 chars = 1920bytes

1920 bytes % 4 = 0


Isnt this correct?

Edit: If I change to constant values it works, and generates a red picture as expected

   temp.r = i % 255;


to

   temp.r = 255;

Edited by bjornp

Share on other sites

A small thing:

//temp.r = i % 255;
temp.r = i % 256;



Sorry I don't have the time to have a good look at your code, but I wrote a "gdi memory bitmap" class a while ago, and it has a method which saves it to disk. You can find the code in this blog post, and below is the relevant method. I'm sorry for just posting code without useful answers, but I hope it helps.


/////////////////////////////////////////////////////////////////
// Desc: Save bitmap represented by the specified BITMAPINFO struct to a file.
BOOL CMemoryBitmap::SaveBMPToFile( const PBITMAPINFO pbmi, LPCTSTR sFileName, VOID* pPixels )
{
FILE* pFile = _tfopen( sFileName, TEXT("wb") );
if( pFile == NULL )
return FALSE;

if( nHeight < 0 )
nHeight = -nHeight;

INT rowSize = DWordAlign( rowSizeUnaligned );

bmfh.bfType = 0x4d42;    // 0x42 = "B" 0x4d = "M", says MSDN.

// compute size of bitmap file.
bmfh.bfSize =    sizeof( BITMAPFILEHEADER ) +
sizeof( BITMAPINFO ) +
rowSize;
bmfh.bfReserved1 = 0;
bmfh.bfReserved2 = 0;
bmfh.bfOffBits = sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFO );

if( 0 == fwrite( (VOID*)&bmfh, sizeof( BITMAPFILEHEADER ), 1, pFile ) )
{
//g_Log( "Failed to write file info header!" );
fclose( pFile );
return FALSE;
}

// NOTE: Some programs have problems loading bitmaps whose height is negative.
// Therefore, this function will save the height as positive, causing the output
// file to be vertically flipped.
if( oldHeight < 0 )

if( 0 == fwrite( (VOID*)pbmi, sizeof( BITMAPINFO ), 1, pFile ) )

// Now write the pixels:
INT paddingBytes = rowSize - rowSizeUnaligned;
DWORD dwZero = 0;

BYTE* pBytes = (BYTE*)pPixels;
for( INT y=0; y<nHeight; y++ )
{
fwrite( pBytes, rowSize - paddingBytes, 1, pFile );
fwrite( &dwZero, 1, paddingBytes, pFile );
pBytes += rowSize;
}
fclose( pFile );

return TRUE;
}



If I remember correctly, this code works with both 24- and 32-bit bitmaps, although you should always use 32-bit bitmaps since they are A LOT faster even if you don't need the alpha channel.

Edited by Amr0

Share on other sites

The problem was a simple one,

file.open(Filename, std::ios::out)


needed to be

file.open(Filename, std::ios::out | std::ios::binary);


since it needs to write the data in binary mode.

Share on other sites

Please don't edit discussion topics as "solved".  As these are discussion boards rather than StackExchange-style question/answer boards, there may be other solutions or discussion that can follow that won't happen if you edit topics that way.

Share on other sites

If I remember correctly, this code works with both 24- and 32-bit bitmaps, although you should always use 32-bit bitmaps since they are A LOT faster even if you don't need the alpha channel.

I still have to argue with people who think that bitmaps can't have transparency.

There are alot of features of bitmaps that nobody supports though.

For example Microsoft allows for PNG and JPEG compression, but most of their software doesn't support it in a file (its used for GDI only). Its kind of funny how those two modes work, they have basically an entire complete PNG or JPEG file where the pixel data should be. (you can read in a JPEG file, stick it inside of a bitmap container in memory, and send it to GDI and it will render it)

Share on other sites

Its kind of funny how those two modes work, they have basically an entire complete PNG or JPEG file where the pixel data should be. (you can read in a JPEG file, stick it inside of a bitmap container in memory, and send it to GDI and it will render it)

Most likely to get around patent issues.

1. 1
2. 2
3. 3
Rutin
19
4. 4
5. 5

• 10
• 14
• 30
• 13
• 11
• Forum Statistics

• Total Topics
631782
• Total Posts
3002338
×