A bitmap resource in the executable is just the bitmap file with the BITMAPFILEHEADER stripped off.
See also: https://blogs.msdn.microsoft.com/oldnewthing/20091211-00/?p=15693
So what you need to do is create this header yourself and write it to the file first. It's relatively easy for the most common 24 bpp bitmaps but a little bit more involved if you want to support other bit depths too.
Next write the data obtained via FindResource() / LoadResource() / LockResource() calls.
Code example (tested with 1 bpp, 4 bpp, 8 bpp and 24 bpp bitmaps):
int main()
{
// Obtain a handle to the current executable
HMODULE hInst = ::GetModuleHandle( nullptr );
// Locate and load a bitmap resource of the executable
if( HRSRC hr = ::FindResource( hInst, MAKEINTRESOURCE( IDB_BITMAP1 ), RT_BITMAP ) )
if( HGLOBAL hg = ::LoadResource( hInst, hr ) )
if( auto pData = reinterpret_cast<const char*>( ::LockResource( hg ) ) )
{
DWORD resourceSize = ::SizeofResource( hInst, hr );
// Check if we safely read the complete BITMAPINFOHEADER
// (to prevent a GPF in case the resource data is corrupt).
if( resourceSize >= sizeof( BITMAPINFOHEADER ) )
{
auto& bmih = reinterpret_cast<const BITMAPINFOHEADER&>( *pData );
// For simplicitly we can only save uncompressed bitmaps.
if( bmih.biCompression == BI_RGB )
{
// Calculate the size of the bitmap pixels in bytes.
// We use this to calculate BITMAPFILEHEADER::bfOffBits correctly.
// This is much easier than calculating the size of the color table.
DWORD widthBytes = ( bmih.biBitCount * bmih.biWidth + 31 ) / 32 * 4;
DWORD heightAbs = abs( bmih.biHeight ); // height can be negative for a top-down bitmap!
DWORD pixelSizeBytes = widthBytes * heightAbs;
// Create the bitmap file header.
BITMAPFILEHEADER bfh = { 0 };
bfh.bfType = 0x4D42; // magic bytes: "BM"
bfh.bfSize = sizeof( bfh ) + resourceSize; // total file size
bfh.bfOffBits = bfh.bfSize - pixelSizeBytes; // offset to bitmap pixels
// Write file header and bitmap resource data to file.
std::ofstream of( "mybitmap1.bmp", std::ios::binary );
of.write( reinterpret_cast<const char*>( &bfh ), sizeof( bfh ) );
of.write( pData, resourceSize );
}
}
}
return 0;
}
Edit:
My original answer missed one crucial bit (literally), that is the ios::binary
flag for the ofstream constructor. That's why the code didn't work for Arthur G.
So why did it actually work for me even without the flag? Funny thing is that it only worked because my test bitmap didn't have any bytes with the value of 10 (never trust a programmer testing his own code)!
One thing that may happen by default is that line endings will be converted according to the platform default. That is, '\n' (ASCII code = 10) will be converted to "\r\n" on the Windows platform. Of course this will totally mess up the bitmap data as any real bitmap will most likely contain this value somewhere.
So we have to tell ofstream explicitly that our data should not be messed with, which is exactly what the ios::binary flag does.
Edit 2:
Just for fun I extended my example to work correctly for 1-bit, 4-bit and 8-bit per pixel bitmaps too.
This is a little bit more involved because for these bit depths the pixel array doesn't start immediately after the BITMAPINFOHEADER. Before the pixel array comes the color table, whose size must be added to BITMAPFILEHEADER::bfOffBits
.
According to MSDN, matters are further complicated because even if the bit depth is 16 or greater, there can be an optional color table ("to optimize performance of the system color palettes" - whatever that means)!
See https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx (under "biClrUsed")
So instead of wrapping my head around all the dirty details of when and how a color table is used, I simply calculate the BITMAPFILEHEADER::bfOffBits
by subtracting the size of the pixel array from the total resource size (which is already known).
Calculating the size of the pixel array is fairly straightforward but you must take care that the width in bytes is rounded up to the next multiple of 4 (one DWORD). Otherwise you will get errors when saving bitmaps whose width in bytes is not a multiple of 4.
Bonus read
Wikipedia has quite an in-depth description of the bitmap file format with a nice diagram of the structure:
https://en.wikipedia.org/wiki/BMP_file_format