Exploration of BMP file structure

xiaoxiao2021-03-05  51

First, file format

The BMP file is a very common bitmap file, both a game or other use. The process of the BMP file also has a bunch of ready-made APIs for calling, however, how to parse such files inside, how to parse such files? In order to eliminate boring, I used it for a few days to study, and as a study notes, record.

First, the content of the entire BMP file can be divided into 3 to 4. The reason why it is divided into 3 to 4 instead of a fixed value because there may be a palette or some mask for BMP. Some discussions are told.

The first piece is the BMP file header 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 is used to represent a file type. If it is a BMP file, then the value of this location must be "BM" is 0x4D42. The second BFSIZE represents the number of bytes of the entire file. The third fourth is retained, and it is inconsistent, the last is quite important, indicating that the data information of the bitmap is offset from the file header, in bytes.

The second block is a bitmap information head, ie BitmapInfoHeader, is used to describe the entire bitmap file. The following is an important data to explain

Typedef struct tagbitmapinfoheader {

DWORD Bisize; // Indicates the size of this structure

Long biwidth; // bit map width

Long biheight; // 位 位 位

Word biplanes; // is always 1, because it is not used, did not do research with MSDN explanation

// Specifies the Number of Planes for the Target Device. This value must be set to 1.

Word bibitcount; // bitmap score is divided into 1 4 8 16 24 32 This article does not study 1 4

DWORD BICOMPRESSION; // This thought the compression type, but there is another action, later explained

DWORD BISIZEIMAGE; / / Indicates the size of the bitmap data area in bytes

Long Bixpelspermeter;

Long biypelspermeter;

DWORD BICLRUSED;

DWORD BICLRIMPORTANT;

} BitmapInfoHeader, * PbitmapInfoHeader;

The third piece is a palette information or a mask portion. If the 8-bit bit map stores the palette; 16 and 32-bit bitmap stores the mask of the RGB color, which is stored in DWORD size.

The last piece is a bitmap data entity.

The above file information can be found in any of the BMP file structure, so this article is just slightly brought.

Second, 4 bytes to its problem

About data reading. The BMP file has an important feature, that is, for the data area, the data of each line must be full 4 bytes, and if not full, use redundant data to make up. This feature directly affects our method of reading bitmap data, because the data in our opinion (x, y) should be in Y * Width X, but because there will be redundant information, Width must be used with width The redundancy amount of the line is handled, and since the bitmap file has a different number of bits, this calculation is not the same.

The general formulas of calculating the offset are listed below. First read the bitmap information into a uchar's buffer:

8-bit:

Int pitch;

IF (width% 4 == 0) {

Pitch = width;

} else {

Pitch = width 4-width% 4;

}

Index = Buffer [Y * PITCH X]; because the data area of ​​the 8-bit bit map is stored in the palette index, just read this index

16-bit

INT pitch = width width% 2;

Buffer [(Y * pitch x) * 2]

Buffer [(i * pitch j) * 2 1]

In both UCHAR, the color information is stored in (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

Since a pixel is 4 bytes, no need to make up

Although the calculation is cumbersome, these calculations are must, otherwise when your bitmap is not a multiple of 4, then Y * Width X brings you a distorted picture, of course if you want to do it Such a rotation is not bad, at least I did not think about (I don't know this feature), turned a 16-bit picture of a 1-byte picture of a pixel, and a twisted rhombus.

Third, with data separation RGB components.

Since my test code has used GDI, I have to say that the value of a point that is separated into RGB separation in 24-bit mode, which is not an easy task. One of the places of bitmap is to have too many formats, so we still have to discuss again.

8-bit

With the operation mentioned in the second part we got an index, the range of this value is 0 to 255 a total of 256 exactly the number of color of the palette.

The size of the 256 RGBQUAD before the data information in the 8-bit BMP picture is the palette information. However, if you want to organize a palette, you have to convert because there is RGBQUAD information R b two in the order in the palette. Because I don't need a palette setting, I read the RGBQUAD array, and get the RGB value by 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 are 555 565 different formats in processing, and there is a distinction between the so-called compression type.

Previous BitmapInfoHeader mentioned a BiCompression

Now we are discussed in two situations: BI_RGB and BI_BITFIELDS

When he is equal to Bi_RGB, only 555 format is available, so you can rest assured that the following data separation is carried out:

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 not to be dazzled by this expression, I think there is since you are watching this article, you have the ability to read this code, otherwise you can only say that you haven't read this point, you need to learn the basic grammar. .

One thing worth reminding is because there is more bit operation, so in the top one of the previous operations, I have never caused an error because of the generation of a element in Buffer. It is a UCHAR but the right shift operation will automatically grow to two bytes, so 1 byte data is required to intercept the low position with the operation. Bi_bitfields are now discussed now.

In this mode, there may be both 555 or 565.

555 format xrrrrrrrrrrrrrrrrrrrggggggbbbb

565 format rrrrrggggggbbbbb

Obviously different format processing is different, so we must first judge to the format.

When BitmapInfoHeader's BiCompression is bi_bitfields, there is a description of a RGB mask in front of the bitmap data area. We only need to read the Mask's mask of the R or G to determine the format.

Taking the red mask as an example 011110000000000 is 555 format 111100000000000 is 565 format.

The following is data separation when 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 RGB's own components, but there is a new problem, that is because the two bytes represent 3 color 555 lower colors up to 0x1f 565 format, the maximum green component is 0x3f. So we need a conversion color = color * 255 / maximum number of colors

Such as RGB (R * 0xFF / 0X1F, G * 0xFF / 0x1f, B * 0xFF / 0x1f)

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];

Fourth, the remaining problem

When the data is taken, the color is also separated, but the bitmap you draw is reversed, because some bitmaps are indeed flipped. Biheight by BitmapInfoHeader can judge whether it is normal or flip, when Biheight> 0 is reversed, it is normal when it is less than 0, but the test writes now to see the file is reversed.

V. Related Test Codes:

The purpose of using the MFC is just to achieve self-selection bitmap files.

Void CBMPTestView :: Ondraw (CDC * PDC)

{

CBMPTESTDOC * PDOC = getDocument ();

Ask_VALID (PDOC);

// Todo: Add a drawing code to this unit 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, "None Bitmap File Please select 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.bmiheter.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 means the picture is reversed

For (int i = 0; i

For (int J = 0; j

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

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 means the picture is reversed

IF (Info.Bmiheader.Bicompression == Bi_RGB) {

// This mode is only 555

For (int i = 0; i

For (int J = 0; 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 exists after BitmapInfoHeader, there is RGB mask for each mask 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

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

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

// 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 exists after BitmapInfoHeader, there is RGB mask for each mask 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

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

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 means the picture is reversed

For (int i = 0; i

INT REALPITCH = I * PITCH;

For (int J = 0; 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

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 Bit Diagram");

} else if (info.bmiheader.bibitcount == 32) {

// b G r a

IF (Height> 0) {

// HEIGHT> 0 means the picture is reversed

For (int i = 0; i <0-height; i ) {

For (int J = 0; 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

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 Bit));

}

DELETE BUFFER;

Fclose (fp);

}

转载请注明原文地址:https://www.9cbs.com/read-36685.html

New Post(0)