Creating Bitmap Files from Raw Pixel Data in C++

Creating the byte array

This post describes a means of taking data in the form of raw pixels containing RGB values as well as the image height, width and the number of bits per pixel (24 in this case) and converting this into a bitmap (BMP) file.

Example Visual Studio 2010 project is downloadable from here:

www.technical-recipes.com/Downloads/BitmapRawPixels.zip

In this this post I originally described how I needed a means of representing binary array values (inkjet printhead nozzles turned ON/OFF) as a set of colored ‘squares’ or pixels, so that nozzles switched ON/OFF could be represented by two different colours – black or white. The bitmap width was always going to be 128, while the bitmap length (“SizeValue”) was one of a set of discrete set of values in the range { 1000, 1250, 1251, 1350 }. For this example I did not have to worry about padding additional values

Since then I have made some improvements to the original code sample, specifically if the image data happens to be not DWORD-aligned, then a new data array is created and padded such that there will be enough bytes to reach the next DWORD.

The original task was to create a one-dimensional array of bytes representing pixel colour data from the two-dimensional adjacency matrix provided.  Each pixel value was then set to black or white, depending on the matrix array value:

BYTE* buf = new BYTE[ 128 * 3 * SizeValue ];

int c = 0;

for ( int i = 0; i <; SizeValue; i++ )
{
    for ( int j = 0; j <; 128; j++ )
    {
        unsigned char val = pmatrix[ i ][ j ] == 0 ? 0xFF : 0x00;                       

        buf[ c + 0 ] = (BYTE) val;
        buf[ c + 1 ] = (BYTE) val;
        buf[ c + 2 ] = (BYTE) val;

        c += 3;
    }
}

SaveBitmapToFile( (BYTE*) buf,
                            128,
                            SizeValue,
                            24,
                            "C:\\MyFolder\\image_created.bmp" );

delete [] buf;

Saving the byte data as a bitmap file

Writing the array data as a bitmap file is accomplished by the SaveBitmapToFile module. This module essentially:

i. initializes a BITMAPINFOHEADER data structure with bitmap parameters (header size, padding, height, width, etc)

ii. initializes a BITMAPFILEHEADER structure in the appropriate way

iii. Creates a file handler and writes the file, bitmap info and pixel data into it to create the new bitmap file representation:

Complete code listing

Here is the complete code listing to enable the user to:

1. Open an input bitmap file
2. Obtain the raw BYTE array, image height and image width from the input bitmap file
3. Create a new raw BYTE array, which will have space for additional padding if required
4. Save the new raw BYTE array to the new bitmap file.

#include <Windows.h>
#include <algorithm>
#include <memory>

// Save the bitmap to a bmp file  
void SaveBitmapToFile( BYTE* pBitmapBits,  
                       LONG lWidth,  
                       LONG lHeight,  
                       WORD wBitsPerPixel, 
					   const unsigned long& padding_size,
                       LPCTSTR lpszFileName )  
{  
    // Some basic bitmap parameters  
    unsigned long headers_size = sizeof( BITMAPFILEHEADER ) +  
                                 sizeof( BITMAPINFOHEADER );  

    unsigned long pixel_data_size = lHeight * ( ( lWidth * ( wBitsPerPixel / 8 ) ) + padding_size );  
      
    BITMAPINFOHEADER bmpInfoHeader = {0};  
      
    // Set the size  
    bmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);  
      
    // Bit count  
    bmpInfoHeader.biBitCount = wBitsPerPixel;  
      
    // Use all colors  
    bmpInfoHeader.biClrImportant = 0;  
      
    // Use as many colors according to bits per pixel  
    bmpInfoHeader.biClrUsed = 0;  
      
    // Store as un Compressed  
    bmpInfoHeader.biCompression = BI_RGB;  
      
    // Set the height in pixels  
    bmpInfoHeader.biHeight = lHeight;  
      
    // Width of the Image in pixels  
    bmpInfoHeader.biWidth = lWidth;  
      
    // Default number of planes  
    bmpInfoHeader.biPlanes = 1;  
      
    // Calculate the image size in bytes  
    bmpInfoHeader.biSizeImage = pixel_data_size;  
      
    BITMAPFILEHEADER bfh = {0};  
      
    // This value should be values of BM letters i.e 0x4D42  
    // 0x4D = M 0×42 = B storing in reverse order to match with endian  
    bfh.bfType = 0x4D42;  
    //bfh.bfType = 'B'+('M' << 8); 
	
    // <<8 used to shift ‘M’ to end  */  
      
    // Offset to the RGBQUAD  
    bfh.bfOffBits = headers_size;  
      
    // Total size of image including size of headers  
    bfh.bfSize =  headers_size + pixel_data_size;  
      
    // Create the file in disk to write  
    HANDLE hFile = CreateFile( lpszFileName,  
                                GENERIC_WRITE,  
                                0,  
                                NULL,  
                                CREATE_ALWAYS,  
                                FILE_ATTRIBUTE_NORMAL,  
                                NULL );  
      
    // Return if error opening file  
    if( !hFile ) return;  
      
    DWORD dwWritten = 0;  
      
    // Write the File header  
    WriteFile( hFile,  
                &bfh,  
                sizeof(bfh),  
                &dwWritten ,  
                NULL );  
      
    // Write the bitmap info header  
    WriteFile( hFile,  
                &bmpInfoHeader,  
                sizeof(bmpInfoHeader),  
                &dwWritten,  
                NULL );  
      
    // Write the RGB Data  
    WriteFile( hFile,  
                pBitmapBits,  
                bmpInfoHeader.biSizeImage,  
                &dwWritten,  
                NULL );  
      
    // Close the file handle  
    CloseHandle( hFile );  
}  

BYTE* LoadBMP ( int* width, int* height, unsigned long* size, LPCTSTR bmpfile )
{
	BITMAPFILEHEADER bmpheader;
	BITMAPINFOHEADER bmpinfo;
	// value to be used in ReadFile funcs
	DWORD bytesread;
	// open file to read from
	HANDLE file = CreateFile ( bmpfile , 
		                       GENERIC_READ, 
							   FILE_SHARE_READ,
							   NULL, 
							   OPEN_EXISTING, 
							   FILE_FLAG_SEQUENTIAL_SCAN, 
							   NULL );
	if ( NULL == file )
		return NULL;
	
	if ( ReadFile ( file, &bmpheader, sizeof ( BITMAPFILEHEADER ), &bytesread, NULL ) == false )
	{
		CloseHandle ( file );
		return NULL;
	}

	// Read bitmap info
	if ( ReadFile ( file, &bmpinfo, sizeof ( BITMAPINFOHEADER ), &bytesread, NULL ) == false )
	{
		CloseHandle ( file );
		return NULL;
	}
	
	// check if file is actually a bmp
	if ( bmpheader.bfType != 'MB' )
	{
		CloseHandle ( file );
		return NULL;
	}

	// get image measurements
	*width   = bmpinfo.biWidth;
	*height  = abs ( bmpinfo.biHeight );

	// Check if bmp iuncompressed
	if ( bmpinfo.biCompression != BI_RGB )
	{
		CloseHandle ( file );
		return NULL;
	}

	// Check if we have 24 bit bmp
	if ( bmpinfo.biBitCount != 24 )
	{
		CloseHandle ( file );
		return NULL;
	}
	
	// create buffer to hold the data
	*size = bmpheader.bfSize - bmpheader.bfOffBits;
	BYTE* Buffer = new BYTE[ *size ];
	// move file pointer to start of bitmap data
	SetFilePointer ( file, bmpheader.bfOffBits, NULL, FILE_BEGIN );
	// read bmp data
	if ( ReadFile ( file, Buffer, *size, &bytesread, NULL ) == false )
	{
		delete [] Buffer;
		CloseHandle ( file );
		return NULL;
	}

	// everything successful here: close file and return buffer
	
	CloseHandle ( file );

	return Buffer;
}

std::unique_ptr<BYTE[]> CreateNewBuffer( unsigned long& padding,
										 BYTE* pmatrix, 
										 const int& width,
									     const int& height )
{
	padding = ( 4 - ( ( width * 3 ) % 4 ) ) % 4;  
	int scanlinebytes = width * 3;
	int total_scanlinebytes = scanlinebytes + padding;
	long newsize = height * total_scanlinebytes;
	std::unique_ptr<BYTE[]> newbuf( new BYTE[ newsize ] );

	// Fill new array with original buffer, pad remaining with zeros
	std::fill( &newbuf[ 0 ], &newbuf[ newsize ], 0 );
	long bufpos = 0;   
	long newpos = 0;
	for ( int y = 0; y < height; y++ )
	{
		for ( int x = 0; x < 3 * width; x+=3 )
		{
			// Determine positions in original and padded buffers
			bufpos = y * 3 * width + ( 3 * width - x );     
			newpos = ( height - y - 1 ) * total_scanlinebytes + x; 
			
			// Swap R&B, G remains, swap B&R
			newbuf[ newpos ] = pmatrix[ bufpos + 2 ]; 
			newbuf[ newpos + 1 ] = pmatrix[ bufpos + 1 ];  
			newbuf[ newpos + 2 ] = pmatrix[ bufpos ];       
		}
	}

	return newbuf;
}

int main()
{
	int imageWidth = 0;
	int imageHeight = 0;
	unsigned long imageSize = 0;
	unsigned long padding = 0;
	
	// Load the bitmap file, amd put its data part into the BYTE array
	BYTE* bytes = LoadBMP( &imageWidth, &imageHeight, &imageSize, "C:\\MyStuff\\shaunak.BMP" );
	std::reverse( bytes, bytes + imageSize );

	// Determine amount of padding required, if any, and create a new BYTE array from this
	std::unique_ptr<BYTE[]> newbuf2 = CreateNewBuffer( padding, bytes, imageWidth, imageHeight );
	
	// Use the new array data to create the new bitmap file
	SaveBitmapToFile( (BYTE*) &newbuf2[ 0 ],  
					  imageWidth,  
					  imageHeight,  
					  24,  
					  padding,
					  "C:\\MyStuff\\new_image.bmp" );  

	return 0;
}

Example Usage

The program works by first loading the bitmap data from the bitmap file provided eg shaunak.BMP:

shaunak

The program extracts the raw data from this and puts it into the buffer, example output of this buffer data:

Buffer

It then creates the new BYTE array with any additional padding, if necessary, and the BYTE data is saved as a new bitmap file, as mentioned previously. Here it is saved as “new_image.bmp” and as expected is a duplicate of the original bitmap file:

new_image

Here is another example of its usage. Instead of obtaining raw bitmap data from an existing bitmap file, I create my own bitmap, which in this case is a simple blue square. I then call

SaveBitmapToFile

to convert this raw data to the bitmap file:

int main()
{
	BYTE* buf = new BYTE[ 128 * 3 * 128 ];
	int c = 0;
 
	for ( int i = 0; i < 128; i++ )
	{
		for ( int j = 0; j < 128; j++ )
		{
			buf[ c + 0 ] = (BYTE) 255;
			buf[ c + 1 ] = (BYTE) 0;
			buf[ c + 2 ] = (BYTE) 0;
 
			c += 3;
		}
	}
 
	SaveBitmapToFile( (BYTE*) buf,
					128,
					128,
					24,
					0,
					"C:\\MyFolder\\bluesquare.bmp" );
 
	delete [] buf;
	return 0;
}

And this is the bitmap image “bluesquare.bmp” created from the raw data:

bluesquare

`