C++: What's the Simplest Way to Read and Write Bmp Files Using C++ on Windows

C++: What's the simplest way to read and write BMP files using C++ on Windows?

When developing just for Windows I usually just use the ATL CImage class

reading/writing bmp files in c

I'm going to guess that you should be opening your files in binary mode.

Writing BMP image in pure c/c++ without other libraries

Without the use of any other library you can look at the BMP file format. I've implemented it in the past and it can be done without too much work.

Bitmap-File Structures

Each bitmap file contains a
bitmap-file header, a
bitmap-information header, a color
table, and an array of bytes that
defines the bitmap bits. The file has
the following form:

BITMAPFILEHEADER bmfh;

BITMAPINFOHEADER bmih;

RGBQUAD aColors[];

BYTE aBitmapBits[];

... see the file format for more details

Read bitmap file into structure

»This is how you manually load a .BMP file

The bitmap file format:

  • Bitmap file header
  • Bitmap info header
  • Palette data
  • Bitmap data

So on with the code part. This is our struct we need to create to hold the bitmap file header.

#pragma pack(push, 1)

typedef struct tagBITMAPFILEHEADER
{
WORD bfType; //specifies the file type
DWORD bfSize; //specifies the size in bytes of the bitmap file
WORD bfReserved1; //reserved; must be 0
WORD bfReserved2; //reserved; must be 0
DWORD bfOffBits; //specifies the offset in bytes from the bitmapfileheader to the bitmap bits
}BITMAPFILEHEADER;

#pragma pack(pop)

The bftype field checks to see if you are in fact loading a .BMP file, and if you are, the field should be 0x4D42.

Now we need to create our bitmapinfoheader struct. This holds info about our bitmap.

#pragma pack(push, 1)

typedef struct tagBITMAPINFOHEADER
{
DWORD biSize; //specifies the number of bytes required by the struct
LONG biWidth; //specifies width in pixels
LONG biHeight; //specifies height in pixels
WORD biPlanes; //specifies the number of color planes, must be 1
WORD biBitCount; //specifies the number of bits per pixel
DWORD biCompression; //specifies the type of compression
DWORD biSizeImage; //size of image in bytes
LONG biXPelsPerMeter; //number of pixels per meter in x axis
LONG biYPelsPerMeter; //number of pixels per meter in y axis
DWORD biClrUsed; //number of colors used by the bitmap
DWORD biClrImportant; //number of colors that are important
}BITMAPINFOHEADER;

#pragma pack(pop)

Now on to loading our bitmap.

unsigned char *LoadBitmapFile(char *filename, BITMAPINFOHEADER *bitmapInfoHeader)
{
FILE *filePtr; //our file pointer
BITMAPFILEHEADER bitmapFileHeader; //our bitmap file header
unsigned char *bitmapImage; //store image data
int imageIdx=0; //image index counter
unsigned char tempRGB; //our swap variable

//open file in read binary mode
filePtr = fopen(filename,"rb");
if (filePtr == NULL)
return NULL;

//read the bitmap file header
fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER),1,filePtr);

//verify that this is a .BMP file by checking bitmap id
if (bitmapFileHeader.bfType !=0x4D42)
{
fclose(filePtr);
return NULL;
}

//read the bitmap info header
fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER),1,filePtr);

//move file pointer to the beginning of bitmap data
fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);

//allocate enough memory for the bitmap image data
bitmapImage = (unsigned char*)malloc(bitmapInfoHeader->biSizeImage);

//verify memory allocation
if (!bitmapImage)
{
free(bitmapImage);
fclose(filePtr);
return NULL;
}

//read in the bitmap image data
fread(bitmapImage,bitmapInfoHeader->biSizeImage,1,filePtr);

//make sure bitmap image data was read
if (bitmapImage == NULL)
{
fclose(filePtr);
return NULL;
}

//swap the R and B values to get RGB (bitmap is BGR)
for (imageIdx = 0;imageIdx < bitmapInfoHeader->biSizeImage;imageIdx+=3)
{
tempRGB = bitmapImage[imageIdx];
bitmapImage[imageIdx] = bitmapImage[imageIdx + 2];
bitmapImage[imageIdx + 2] = tempRGB;
}

//close file and return bitmap image data
fclose(filePtr);
return bitmapImage;
}

Now to make use of all of this:

BITMAPINFOHEADER bitmapInfoHeader;
unsigned char *bitmapData;
// ...
bitmapData = LoadBitmapFile("mypic.bmp",&bitmapInfoHeader);
//now do what you want with it, later on I will show you how to display it in a normal window

Later on I'll put up Writing to a .BMP, and how to load a targa file, and how to display them.«

Quoted from: http://www.vbforums.com/showthread.php?261522-C-C-Loading-Bitmap-Files-%28Manually%29 (User: BeholderOf). (Some minor corrections done)

C++: Write BMP image format error on WINDOWS

Check the header

The header must start with the following two signature bytes: 0x42 0x4D. If it's something different a third party application will think that this file doesn't contain a bmp picture despite the .bmp file extension.

The size and the way pixels are stored is also a little bit more complex than what you expect: you assume that the number of bits per pixels is 24 and no no compression is used. This is not guaranteed. If it's not the case, you might read more data than available, and corrupt the file when writing it back.

Furthermore, the size of the header depends also on the BMP version you are using, which you can detect using the 4 byte integer at offset 14.

Improve your code

When you load a file, check the signature, the bmp version, the number of bits per pixel and the compression. For debugging purpose, consider dumping the header to check it manually:

for (int i=0; i<54; i++) 
cout << hex << image.header[i] << " ";`
cout <<endl;

Furthermore, when you fread() check that the number of bytes read correspond to the size you wanted to read, so to be sure that you're not working with uninitialized buffer data.

Edit:

Having checked the dump, it appears that the format is as expected. But verifying the padded size in the header with the padded size that you have calculated it appears that the error is here:

image.row_padded = (image.width * 3 + 3) & (~3);     // ok size of a single row rounded up to multiple of 4
image.pixels = new unsigned char[image.row_padded]; // oops ! A little short ?

In fact you read row by row, but you only keep the last one in memory ! This is different of your first version, where you did read the full pixels of the picture.

Similarly, you write the last row repeated height time.

Reconsider your padding, working with the total padded size.

image.row_padded = (image.width * 3 + 3) & (~3);     // ok size of a single row rounded up to multiple of 4
image.size_padded = image.row_padded * image.height; // padded full size
image.pixels = new unsigned char[image.size_padded]; // yeah !
if (fread(image.pixels, sizeof(unsigned char), image.size_padded, f) != image.size_padded) {
cout << "Error: all bytes couldn't be read"<<endl;
}
else {
... // process the pixels as expected
}
...

Creating a BMP file (bitmap) in C

Your pixel offset (bytes 10..13) is zero, but the pixel data don't actually start at the beginning of the file, they start at byte 54.

Also:

  • Your comment on byte 34 says "bits" but means "bytes", but of course that doesn't matter.

  • Your horizontal and vertical resolutions have the wrong byte order, but I very much doubt that that matters.

If I were doing this I'd define structs for the header data (indeed, if you're on Windows, Microsoft have already done this) and use a macro or something for putting bytes into the right order portably.

Whether you "have to reverse the order of the bytes" depends on the endianity of the processor you're using. Writing separate bytes separately, as you're doing, is an effective way to avoid having to worry about this.

How to read BMP file in C?

Structure packing and alignment padding are implementation defined, and byte order is platform defined.

If the byte order for your platform is the same as that defined for BMP (little-endian) then you can use whatever compiler extensions your toolchain supports for structure packing. For example in GCC:

typedef struct BmpFileHeader {
char bfType[2];
unsigned int bfSize;
unsigned short int __bfReserved1;
unsigned short int __bfReserved2;
unsigned long int bfOffBits;
} __attribute__ ((packed)) BMPFILEHEADER;

typedef struct BmpImageHeader {
unsigned int biSize;
int biWidth;
int biHeight;
unsigned short int biPlanes;
unsigned short int biBitCount;
unsigned int biCompression;
unsigned int biSizeImage;
int biXPelsPerMeter;
int biYPelPerMeter;
unsigned int biClrUsed;
unsigned int biClrImportant;
} __attribute__ ((packed)) BMPIMAGEHEADER;

Byte order for BMP is little-endian for for integer values; so for x86 and most ARM platforms you may not need to worry about byte-order. Pixel byte order is somewhat less straightforward.

However for a truly portable solution you would have to read the data byte-by-byte and load each member of the structure individually - so called deserialisation.

You would also do well to ensure compliance with the header structure by using stdint.h data types uint8_t, uint16_t, uint32_t, int32_t etc.

C++: What's the simplest way to read and write BMP files using C++ on Windows?

When developing just for Windows I usually just use the ATL CImage class



Related Topics



Leave a reply



Submit