Exploration of BMP file structure

Source: Internet
Author: User
Tags bmp image fread textout

I. File Format

Bmp files are commonly used bitmap files, which are widely used in games and other fields. There are also a bunch of ready-made APIs for processing bmp files. However, how can we resolve such files on our own? In order to eliminate boredom, I spent a few days studying it and recording it as study notes.

 

First, the content of the entire bmp file can be divided into three to four parts. The reason why there are 3 to 4 blocks instead of fixed values is that there may be a color palette or some masks for bmp. The details will be discussed later.

 

The first part is the bmp file header used to describe the entire bmp file. The structure is as follows:

 

Typedef struct tagBITMAPFILEHEADER {WORD bfType; DWORD bfSize; WORD bfReserved1; WORD bfReserved2; DWORD bfOffBits;} BITMAPFILEHEADER, * PBITMAPFILEHEADER;

 

This information is quite useful if you want to parse the bmp file directly. The first bfType indicates the file type. If it is a bmp file, the value of this position must be "BM", that is, 0x4D42. The second bfSize indicates the number of bytes of the entire file. The third and fourth items are retained, which is meaningless at present. The last one is very important, indicating the offset of the bitmap data information from the file header, in bytes.

The second part is the bitmap information header, which is used to describe the entire bitmap file. Explain the important data below

Typedef struct tagBITMAPINFOHEADER {

 

DWORD biSize; // indicates the size of the structure.

 

LONG biWidth; // The width of the bitmap.

 

LONG biHeight; // The height of the bitmap.

 

WORD biPlanes; // It will always be 1. As it has never been used, no research has been conducted.

 

// Specifies the number of planes for the target device. This value must be set to 1.

WORD biBitCount; // bitmap bits are divided into 1 4 8 16 24 32 this article does not study 1 4

 

DWORD biCompression; // This indicates the compression type, but it has another effect. Please explain later.

 

DWORD biSizeImage; // indicates the size of the bitmap data area in bytes.

 

LONG biXPelsPerMeter;

 

LONG biYPelsPerMeter;

 

DWORD biClrUsed;

 

DWORD biClrImportant;

 

} BITMAPINFOHEADER, * PBITMAPINFOHEADER;

 

The third part is the color palette information or mask. If it is an 8-Bit Bitmap, the color palette is stored. If it is a 16-Bit Bitmap, the 32-Bit Bitmap stores the RGB color mask, which is stored in DWORD size.

 

The last part is the data entity of the bitmap.

 

The above file information can be found in any article on the bmp file structure, so this article is just a little bit.

 

 

2. 4-byte Problems

 

Read data. Bmp files have an important feature, that is, for the data area, each row of data must be fully 4 bytes. If not, redundant data is used for completion. This feature directly affects the method for reading bitmap data, because in our opinion (x, y) the data must be at the position y * width + x, but because of the redundant information, the width must be processed with the redundant amount of width + row, because bitmap files have different digits, such calculations are also different.

 

The following lists the general formulas used to calculate the offset.

 

First, read the bitmap information into a buffer of UCHAR:

 

8 digits:

 

Int pitch;

 

If (width % 4 = 0 ){

 

Pitch = width;

 

} Else {

 

Pitch = width + 4-width % 4;

 

}

 

Index = buffer [y * pitch + x]; because the eight-Bit Bitmap data area stores the palette index value, you only need to read this index

 

16-bit

 

Int pitch = width + width % 2;

 

Buffer [(y * pitch + x) * 2]

 

Buffer [(I * pitch + j) * 2 + 1]

 

The two UCHAR files store the color information at (x, y ).

 

24-bit

 

Int pitch = width % 4;

 

Buffer [(y * width + x) * 3 + y * pitch];

 

Buffer [(y * width + x) * 3 + y * pitch + 1];

 

Buffer [(y * width + x) * 3 + y * pitch + 2];

 

32-bit

 

Because a pixel is 4 bytes, it does not need to be completed.

 

 

 

Although computation is cumbersome, these computation operations are necessary. Otherwise, when the number of elephant prime numbers in each row of your bitmap is not a multiple of 4, then, y * width + x brings you a distorted image. Of course, if you want to perform such a rotation, it would be nice, at least because I didn't consider it at the beginning (I don't know this feature), a 16-bit image with one byte less per line of pixels turned into a distorted diamond.

 

 

 

3. Data separation RGB components.

 

Since my test code uses GDI, it is not easy to separate the values of a certain vertex into RGB values in 24-Bit mode. One of the most troublesome aspects of bitmap is that there are too many formats, so we should discuss them again.

 

8-digit

 

Through the operation mentioned in the second part, we get an index. The value range is 0 ~ 255 a total of 256 colors are exactly the number of colors in the palette.

 

In an 8-bit bmp image, the size of the first 256 RGBQUAD is the information of the color palette. However, to organize it into a color palette, it must be converted because the RGBQUAD information r B is reversed with the order in the color palette. Because I do not need to set the color palette, I read the bytes into the RGBQUAD array and obtain the RGB value through the following expression:

 

UCHAR r = quad [index]. rgbRed;

 

UCHAR g = quad [index]. rgbGreen;

 

UCHAR B = quad [index]. rgbBlue;

 

16-bit

 

This is the most troublesome one. Because there is a difference between 555 and 565 formats, and there is also a difference between the so-called compression types.

 

The previous bitmapinfoheader mentioned a biCompression.

 

We can discuss BI_RGB and BI_BITFIELDS in two ways.

 

When he is equal to BI_RGB, only the 555 format is available, so you can proceed with the following data separation with confidence:

 

UCHAR B = buffer [(I * pitch + j) * 2] & 0x1F;

 

UCHAR g = (buffer [(I * pitch + j) * 2 + 1] <6) & 0xFF)> 3) + (buffer [(I * pitch + j) * 2]> 5 );

 

UCHAR r = (buffer [(I * pitch + j) * 2 + 1] <1)> 3;

 

 

 

I hope you will not be dazzled by this expression. I think that since you are reading this article, you will be able to read this code. Otherwise, you can only say that you have not read this code yet, you need to learn the basic syntax.

One thing worth noting is that there are many bit operations, so a pair of parentheses are added to the previous operation during processing. I used to cause errors because I did not add them, in addition, although one element in the buffer represents a UCHAR, the right-shift operation will automatically increase to two bytes, so we need to intercept the low-level 1-byte data at one operation.

 

Now we will discuss BI_BITFIELDS.

 

In this mode, there can be 555 or 565.

 

555 format xrrrrrgggggbbbbb

 

565 format: rrrrrggggggbbbbb

 

Obviously, different formats are processed differently. Therefore, we need to first determine the format that actually belongs.

When the biCompression of Bitmapinfoheader is BI_BITFIELDS, there is an RGB mask in front of the bitmap data area that describes three DWORD values. We only need to read the R or G mask, to determine the format.

 

Take the red mask as an example. When the mask is 0111110000000000, the 555 format is 1111100000000000.

 

Data separation in 565 format:

 

UCHAR B = buffer [(I * pitch + j) * 2] & 0x1F;

 

UCHAR g = (buffer [(I * pitch + j) * 2 + 1] <5) & 0xFF)> 2) + (buffer [(I * pitch + j) * 2]> 5 );

 

UCHAR r = buffer [(I * pitch + j) * 2 + 1]> 3;

 

 

 

Now we get the respective components of RGB, but there is a new problem, that is, because the two bytes indicate the maximum green weight of each color in 3 colors 555 to 0x1F 565 format, that is, 0x3F. So we need a conversion color = color * 255/Maximum number of colors.

 

For example, RGB (r * 0xFF/0x1F, g * 0xFF/0x3F, B * 0xFF/0x1F) under 565)

 

24-bit

 

UCHAR B = buffer [(I * width + j) * 3 + realPitch];

 

UCHAR g = buffer [(I * width + j) * 3 + 1 + realPitch];

 

UCHAR r = buffer [(I * width + j) * 3 + 2 + realPitch];

 

32-bit

 

UCHAR B = buffer [(I * width + j) * 4];

 

UCHAR g = buffer [(I * width + j) * 4 + 1];

 

UCHAR r = buffer [(I * width + j) * 4 + 2];

 

 

 

Iv. Remaining Issues

When the data is obtained, the color is also separated, but the bitmap you plot may be reversed, because some bitmaps are indeed flipped. The biHeight of bitmapinfoheader can be used to determine whether it is normal or flipped. When biHeight is greater than 0, it is normal when it is smaller than 0, but the files written in the test are all reversed.

 

 

 

V. Test code:

The objective of using MFC is to parse bitmap files by yourself

 

Void CBmpTestView: OnDraw (CDC * pDC)

 

{

 

CBmpTestDoc * pDoc = GetDocument ();

 

ASSERT_VALID (pDoc );

 

 

// TODO: add the drawing code for the local data here

 

 

If (filename = ""){

 

Return;

 

}

 

FILE * fp = fopen (filename, "r ");

 

If (fp = NULL ){

 

PDC-> TextOut (100,200, "no file found ");

 

Return;

 

}

 

BITMAPFILEHEADER fileheader;

 

BITMAPINFO info;

 

 

Fread (& fileheader, sizeof (fileheader), 1, fp );

 

If (fileheader. bfType! = 0x4D42 ){

 

PDC-> TextOut (100,200, "select a bitmap file if there is no bitmap file ");

 

Fclose (fp );

 

Return;

 

}

 

Fread (& info. bmiHeader, sizeof (BITMAPINFOHEADER), 1, fp );

 

Long width = info. bmiHeader. biWidth;

 

Long height = info. bmiHeader. biHeight;

 

UCHAR * buffer = new UCHAR [info. bmiHeader. biSizeImage];

 

Fseek (fp, fileheader. bfOffBits, 0 );

 

Fread (buffer, info. bmiHeader. biSizeImage, 1, fp );

 

 

 

If (info. bmiHeader. biBitCount = 8 ){

 

Int pitch;

 

If (width % 4 = 0 ){

 

Pitch = width;

 

} Else {

 

Pitch = width + 4-width % 4;

 

}

 

RGBQUAD quad [256];

 

Fseek (fp, fileheader. bfOffBits-sizeof (RGBQUAD) * 256, 0 );

 

Fread (quad, sizeof (RGBQUAD) * 256, 1, fp );

 

If (height> 0 ){

 

// Height> 0 indicates that the image is reversed.

 

For (int I = 0; I

 

For (int j = 0; j <width; j ++ ){

 

Int index = buffer [I * pitch + j];

 

UCHAR r = quad [index]. rgbRed;

 

UCHAR g = quad [index]. rgbGreen;

 

UCHAR B = quad [index]. rgbBlue;

 

PDC-> SetPixel (j, height-I, RGB (r, g, B ));

 

}

 

}

 

} Else {

 

For (int I = 0; I <0-height; I ++ ){

 

For (int j = 0; j <width; j ++ ){

 

Int index = buffer [I * pitch + j];

 

UCHAR r = quad [index]. rgbRed;

 

UCHAR g = quad [index]. rgbGreen;

 

UCHAR B = quad [index]. rgbBlue;

 

PDC-> SetPixel (j, I, RGB (r, g, B ));

 

}

 

}

 

}

 

} Else if (info. bmiHeader. biBitCount = 16 ){

 

Int pitch = width + width % 2;

 

If (height> 0 ){

 

// Height> 0 indicates that the image is reversed.

 

If (info. bmiHeader. biCompression = BI_RGB ){

 

// This mode is only 555

 

For (int I = 0; I

 

For (int j = 0; j <width; j ++ ){

 

// 5 5 5 format

 

UCHAR B = buffer [(I * pitch + j) * 2] & 0x1F;

 

UCHAR g = (buffer [(I * pitch + j) * 2 + 1] <6) & 0xFF)> 3) + (buffer [(I * pitch + j) * 2]> 5 );

 

UCHAR r = (buffer [(I * pitch + j) * 2 + 1] <1)> 3;

 

PDC-> SetPixel (j, height-I, RGB (r * 0xFF)/0x1F, (g * 0xFF)/0x1F, (B * 0xFF)/0x1F ));

 

}

 

}

 

} Else if (info. bmiHeader. biCompression = BI_BITFIELDS ){

 

// This mode has an RGB mask after bitmapinfoheader. Each mask is 1 DWORD.

 

Fseek (fp, fileheader. bfOffBits-sizeof (DWORD) * 3, 0 );

 

DWORD rMask;

 

Fread (& rMask, sizeof (DWORD), 1, fp );

 

If (rMask = 0x7C00 ){

 

// 5 5 5 format

 

MessageBeep (0 );

 

For (int I = 0; I

 

For (int j = 0; j <width; j ++ ){

 

UCHAR B = buffer [(I * pitch + j) * 2] & 0x1F;

 

UCHAR g = (buffer [(I * pitch + j) * 2 + 1] <6) & 0xFF)> 3) + (buffer [(I * pitch + j) * 2]> 5 );

 

UCHAR r = (buffer [(I * pitch + j) * 2 + 1] <1)> 3;

 

PDC-> SetPixel (j, height-I, RGB (r * 0xFF)/0x1F, (g * 0xFF)/0x1F, (B * 0xFF)/0x1F ));

 

}

 

}

 

} Else if (rMask = 0xF800 ){

 

// 5 6 5 format

 

For (int I = 0; I

 

For (int j = 0; j <width; j ++ ){

 

UCHAR B = buffer [(I * pitch + j) * 2] & 0x1F;

 

UCHAR g = (buffer [(I * pitch + j) * 2 + 1] <5) & 0xFF)> 2) + (buffer [(I * pitch + j) * 2]> 5 );

 

UCHAR r = buffer [(I * pitch + j) * 2 + 1]> 3;

 

PDC-> SetPixel (j, height-I, RGB (r * 0xFF/0x1F, g * 0xFF/0x3F, B * 0xFF/0x1F ));

 

}

 

}

 

}

 

}

 

} Else {

 

If (info. bmiHeader. biCompression = BI_RGB ){

 

// This mode is only 555

 

For (int I = 0; I <0-height; I ++ ){

 

For (int j = 0; j <width; j ++ ){

 

// 5 5 5 format

 

UCHAR B = buffer [(I * pitch + j) * 2] & 0x1F;

 

UCHAR g = (buffer [(I * pitch + j) * 2 + 1] <6) & 0xFF)> 3) + (buffer [(I * pitch + j) * 2]> 5 );

 

UCHAR r = (buffer [(I * pitch + j) * 2 + 1] <1)> 3;

 

PDC-> SetPixel (j, I, RGB (r * 0xFF)/0x1F, (g * 0xFF)/0x1F, (B * 0xFF)/0x1F ));

 

}

 

}

 

} Else if (info. bmiHeader. biCompression = BI_BITFIELDS ){

 

// This mode has an RGB mask after bitmapinfoheader. Each mask is 1 DWORD.

 

Fseek (fp, fileheader. bfOffBits-sizeof (DWORD) * 3, 0 );

 

DWORD rMask;

 

Fread (& rMask, sizeof (DWORD), 1, fp );

 

If (rMask = 0x7C00 ){

 

// 5 5 5 format

 

MessageBeep (0 );

 

For (int I = 0; I <0-height; I ++ ){

 

For (int j = 0; j <width; j ++ ){

 

UCHAR B = buffer [(I * pitch + j) * 2] & 0x1F;

 

UCHAR g = (buffer [(I * pitch + j) * 2 + 1] <6) & 0xFF)> 3) + (buffer [(I * pitch + j) * 2]> 5 );

 

UCHAR r = (buffer [(I * pitch + j) * 2 + 1] <1)> 3;

 

PDC-> SetPixel (j, I, RGB (r * 0xFF)/0x1F, (g * 0xFF)/0x1F, (B * 0xFF)/0x1F ));

 

}

 

}

 

} Else if (rMask = 0xF800 ){

 

// 5 6 5 format

 

For (int I = 0; I <0-height; I ++ ){

 

For (int j = 0; j <width; j ++ ){

 

UCHAR B = buffer [(I * pitch + j) * 2] & 0x1F;

 

UCHAR g = (buffer [(I * pitch + j) * 2 + 1] <5) & 0xFF)> 2) + (buffer [(I * pitch + j) * 2]> 5 );

 

UCHAR r = buffer [(I * pitch + j) * 2 + 1]> 3;

 

PDC-> SetPixel (j, I, RGB (r * 0xFF/0x1F, g * 0xFF/0x3F, B * 0xFF/0x1F ));

 

}

 

}

 

}

 

}

 

}

 

// PDC-> TextOut (100,200, "16 bitmap ");

 

} Else if (info. bmiHeader. biBitCount = 24 ){

 

Int pitch = width % 4;

 

// B g r

 

If (height> 0 ){

 

// Height> 0 indicates that the image is reversed.

 

For (int I = 0; I

 

Int realPitch = I * pitch;

 

For (int j = 0; j <width; j ++ ){

 

UCHAR B = buffer [(I * width + j) * 3 + realPitch];

 

UCHAR g = buffer [(I * width + j) * 3 + 1 + realPitch];

 

UCHAR r = buffer [(I * width + j) * 3 + 2 + realPitch];

 

PDC-> SetPixel (j, height-I, RGB (r, g, B ));

 

}

 

}

 

} Else {

 

For (int I = 0; I <0-height; I ++ ){

 

Int realPitch = I * pitch;

 

For (int j = 0; j <width; j ++ ){

 

UCHAR B = buffer [(I * width + j) * 3 + realPitch];

 

UCHAR g = buffer [(I * width + j) * 3 + 1 + realPitch];

 

UCHAR r = buffer [(I * width + j) * 3 + 2 + realPitch];

 

PDC-> SetPixel (j, I, RGB (r, g, B ));

 

}

 

}

 

}

 

 

// PDC-> TextOut (100,200, "24 bitmap ");

 

 

 

} Else if (info. bmiHeader. biBitCount = 32 ){

 

// B g r

 

If (height> 0 ){

 

// Height> 0 indicates that the image is reversed.

 

For (int I = 0; I <0-height; I ++ ){

 

For (int j = 0; j <width; j ++ ){

 

UCHAR B = buffer [(I * width + j) * 4];

 

UCHAR g = buffer [(I * width + j) * 4 + 1];

 

UCHAR r = buffer [(I * width + j) * 4 + 2];

 

PDC-> SetPixel (j, height-I, RGB (r, g, B ));

 

}

 

}

 

} Else {

 

For (int I = 0; I

 

For (int j = 0; j <width; j ++ ){

 

UCHAR B = buffer [(I * width + j) * 4];

 

UCHAR g = buffer [(I * width + j) * 4 + 1];

 

UCHAR r = buffer [(I * width + j) * 4 + 2];

 

PDC-> SetPixel (j, I, RGB (r, g, B ));

 

}

 

}

 

}

 

// PDC-> TextOut (100,200, "32 bitmap ");

 

}

 

Delete buffer;

 

Fclose (fp );

 

}

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.