Bmp file format

Posted by Crimpage on Fri, 29 Oct 2021 06:16:57 +0200

1. Concept of bitmap and palette

Nowadays, Windows (3.x and 95, NT) series has become the operating system used by most users. An important factor in its success over DOS is its beautiful visual interface. For example, you can lay your favorite wallpaper on the desktop. So how does Windows display images? This is about bitmaps. We know that the ordinary display screen is composed of many points, which we call pixels. Scanning method is adopted for display: the electron gun scans one line from left to right each time, colors each pixel, and then scans several lines from top to bottom to sweep a screen. To prevent flickering, repeat the above process dozens of times per second. For example, we often say that the screen resolution is 640 * 480 and the refresh rate is 70Hz, which means that 640 pixels are scanned in each line, a total of 480 lines, and the screen is scanned 70 times per second. We call this kind of display bit mapping device. Bit mapping refers to a two-dimensional pixel matrix, and Bitmap is an image displayed and stored by bit mapping method. For example, figure 1 below is an ordinary black-and-white Bitmap, and Figure 2 is an enlarged one. Each square in the figure represents a pixel. We can see that the whole skeleton is composed of such black and white dots.

Color map

Let's talk about the concept of ternary RGB. We know that all colors in nature can be composed of red, green and blue (R, G, B). Some colors contain more red components, such as crimson; Some contain less red components, such as light red. The number of red components can be divided into 256 levels from 0 to 255. Level 0 means no red components and level 255 means 100% red components. Similarly, green and blue are divided into 256 levels. This concept of grading is called quantification. In this way, according to different combinations of red, green and blue, we can express 256 * 256 * 256, about 16 million colors. So many colors are enough for our human eyes.
  
The following table shows the RGB combination values of some common colors.
Color R G B
Red 255 0 0
Blue 0 0 25
Green 0 255 0
Yellow 255 255 0
Purple 255 0 255
Green 0 255 255
White 255 255 255
Black 0 0 0

You probably know that when each pixel in a picture is given a different RGB value, it can show a colorful color, which forms a color map. Yes, it is, but there are still some differences in practice.
  
Let's take a look at the following example
There is a color map with 200 pixels in length and width and 16 colors. Each pixel is represented by three components R, G and B. because each component has 256 levels and needs to be represented by 8 bits, i.e. one byte, each pixel needs 3 bytes. The whole image needs 200 * 200 * 3, about 120k bytes, which is not a small number! If we use the following method, we can save a lot. Because it is a 16 color graph, that is, there are only 16 colors at most in this graph, we can use a table: each row in the table records the R, G and b values of one color. In this way, when we represent the color of a pixel, we only need to indicate the row in which the color is, that is, the index value of the color in the table. For example, if row 0 of the table is 255, 0, 0 (red), when a pixel is red, you only need to indicate 0. Let's calculate again: 16 states can be represented by 4 bits, so a pixel needs half a byte. The whole image needs 200 * 200 * 0.5, about 20k bytes, and the bytes occupied by the table are 3 * 16 = 48 bytes. The number of bytes occupied by the whole image is about 1 / 6 of the previous one, which saves a lot.

This RGB table is what we often call the palette. Another name is the color lookup table LUT(LookUp Table), which seems to be more accurate. Palette technology is used in Windows bitmap. In fact, it is not only windows bitmap, but also many image file formats such as pcx, tif, gif, etc. Therefore, it is very important to master the concept of palette

There is a graph with 256 * 256 * 256 colors, that is, it contains all the colors in the R, G and B color representation methods mentioned above. This graph is called true color graph. True color map does not mean that a map contains all colors, but that it has the ability to display all colors, that is, it can contain all colors at most. When representing a true color map, each pixel is directly represented by three component bytes of R, G and B instead of palette technology. The reason is obvious: if you use palette, you also need 24 bits to represent a pixel, because the index of each color needs 24 bits (because there are a total of 24 colors to the power of 2, that is, the palette has 24 lines to the power of 2), just like the number of bytes directly represented by the three components of R, G and B, it is not cheap, but also adds a large color palette of 256 * 256 * 256 * 3 bytes. Therefore, the true color map is directly represented by the three components of R, G and B, which is also called 24 bit color map.

2. Bmp file format

After introducing the concepts of bitmap and palette, let's take a look at the format of Windows bitmap file (. bmp file). bmp file is generally divided into four parts, as shown in Figure 3.
  



Figure 3. Schematic diagram of windows bitmap file structure (right)

Bitmap part I

The first part is the bitmap file header BITMAPFILEHEADER, which is a structure, which is defined as follows:

typedef struct tagBITMAPFILEHEADER{
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

This structure has a fixed length of 14 bytes (WORD is an unsigned 16 bit integer and DWORD is an unsigned 32-bit integer). The description of each field is as follows:
 

bfType
 Specify the file type, which must be 0 x424D,That is, string"BM",That means all.bmp The first two bytes of the file are"BM"
bfSize
 Specify the file size, including these 14 bytes
bfReserved1,bfReserved2
 For reserved words, do not consider
bfOffBits
 Is the number of offset bytes from the file header to the actual bitmap data, that is, the sum of the lengths of the first three parts in Figure 3.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Bitmap Part II

The second part is the bitmap header BITMAPINFOHEADER, which is also a structure, which is defined as follows

    typedef struct tagBITMAPINFOHEADER{
      DWORD biSize;
      LONG biWidth;
      LONG biHeight;
      WORD biPlanes;
      WORD biBitCount;
      DWORD biCompression;
      DWORD biSizeImage;
      LONG biXPelsPerMeter;
      LONG biYPelsPerMeter;
      DWORD biClrUsed;
      DWORD biClrImportant;
      } BITMAPINFOHEADER; 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

The length of this structure is 40 bytes (WORD is an unsigned 16 bit integer, DWORD is an unsigned 32-bit integer, and LONG is a 32-bit integer). The description of each field is as follows:

biSize:           Specify the length of this structure, 40
biWidth:          Specifies the width of the image in pixels
biHeight:         Specifies the height of the image in pixels
biPlanes:         Must be 1, no consideration
biBitCount:       Specifies the number of digits to use to represent the color. The common value is 1(Black and white dichroic diagram),4(16 Color map),8(256 Color map),24(True color map),new.bmp The format supports 32 bitmaps
biCompression:    Specifies whether the bitmap is compressed. Valid values are BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDS(It's all some Windows Defined constants). To illustrate, Windows Bitmaps can take RLE4,and RLE8 Compression format, but not much used. We will only discuss the first uncompressed case in the future, that is BI_RGB. 
biSizeImage:      Specifies the number of bytes occupied by the actual bitmap data, which can also be calculated from the following formula: biSizeImage=biWidth'*biHeight. For example, if biWidth=240,be biWidth'=240;If biWidth=241,biWidth'=244)If biCompression by BI_RGB,Then the item may be zero
biXPelsPerMeter:  Specifies the horizontal resolution of the target device in pixels per meter. The concept of resolution will be described in detail in the printing section.
biYPelsPerMeter:  Specifies the vertical resolution of the target device in the same unit as above.
biClrUsed:        Specifies the number of colors actually used in this image. If the value is 0, the number of colors used is 2 biBitCount Power
biClrImportant:   Specifies the number of important colors in the image. If the value is zero, all colors are considered important.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Bitmap Part III

The third part is the palette. Of course, this is for bitmap files that need a palette. Some bitmaps, such as true color maps, as mentioned earlier, do not need a palette. BITMAPINFOHEADER is directly followed by bitmap data.
The palette is actually an array with biClrUsed elements (if the value is zero, there are elements to the power of biBitCount of 2). The type of each element in the array is an RGBQUAD structure, accounting for 4 bytes. Its definition is as follows:

  typedef struct tagRGBQUAD{
  BYTE rgbBlue;   //The blue component of the color
  BYTE rgbGreen;  //The green component of the color
  BYTE rgbRed;    //The red component of the color
  BYTE rgbReserved; //Reserved value
  } RGBQUAD;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Bitmap Part IV

The fourth part is the actual image data. For the bitmap using the palette, the image data is the index value of the pixel color in the palette. For the true color map, the image data is the actual R, G and b values. Here are 2-color, 16 color, 256 color bitmaps and true color bitmaps.

  • 2-color bitmap: 1 bit can represent the color of the pixel (generally 0 represents black and 1 represents white), so a byte can represent 8 pixels.
  • 16 color bitmap: 4 bits can represent the color of a pixel, so a byte can represent 2 pixels.
  • 256 color bitmap; A byte can represent just 1 pixel.
    True color map: only three bytes can represent one pixel.

Two points should be noted:
1. The number of bytes in each line must be an integral multiple of 4. If not, it needs to be supplemented. This was mentioned earlier when we introduced biSizeImage.
2. Generally speaking, the data of. BMP file is from bottom to top and from left to right. In other words, the first pixel to be read from the file is the first pixel on the left of the bottom line of the image, then the second pixel on the left... Then the first pixel on the left of the penultimate line, the second pixel on the left... And so on. Finally, the rightmost pixel on the top line is obtained.

3. C program that displays a bmp file

The following function LoadBmpFile reads data from a. bmp file (including BITMAPINFOHEADER, palette and actual image data) and stores it in a global memory handle hImgData, which will be used in future image processing programs. Fill in a global variable hbitmap of type hbitmap and a global variable hpalette of type hpalette at the same time. These two variables will be processed in WM_ The paint message is used to display the bit map. The two parameters of the function are the window handle used to display the bitmap and the. bmp file name (full path). When the function succeeds, it returns TRUE, otherwise it returns FALSE

BITMAPFILEHEADER bf;
BITMAPINFOHEADER bi;

BOOL LoadBmpFile(HWND hWnd,char* BmpFileName)
{
    HFILE hf;                //File handle
    LPBITMAPINFOHEADER lpImgData; //Pointer to BITMAPINFOHEADER structure
    LOGPALETTE *pPal;       //Pointer to the logical palette structure
    LPRGBQUAD lpRGB;        //Pointer to RGBQUAD structure
    HPALETTE hPrevPalette;  //Used to save the original palette in the device
    HDC hDc;                //device handle 
    HLOCAL hPal;            //Stores a local memory handle to the palette
    DWORD LineBytes;        //Bytes per line
    DWORD ImgSize;          //Number of bytes occupied by actual image data
    DWORD NumColors;        //The number of colors actually used, that is, the number of colors in the palette array
    DWORD i;

    if((hf=_lopen(BmpFileName,OF_READ))==HFILE_ERROR)
    {
         MessageBox (hWnd,"Filec:\\test.bmpnotfound!","ErrorMessage",
         MB_OK|MB_ICONEXCLAMATION);
         return FALSE;              //Error opening file, return
    }

  //Read the BITMAPFILEHEADER structure from the file and fill it in bf
  _lread(hf,(LPSTR)&bf,sizeof(BITMAPFILEHEADER));
  //Read the BITMAPINFOHEADER structure from the file and fill it in bi
  _lread(hf,(LPSTR)&bi,sizeof(BITMAPINFOHEADER));
  
  //LineBytes is the number of bytes per line
  LineBytes=(DWORD)WIDTHBYTES(bi.biWidth*bi.biBitCount);

  //ImgSize is the number of bytes occupied by the actual image data
  ImgSize=(DWORD)LineBytes*bi.biHeight;

  //NumColors is the number of colors actually used, that is, the number of colors in the palette array
  if(bi.biClrUsed!=0)
    NumColors=(DWORD)bi.biClrUsed;//If bi.biClrUsed is not zero, it is the number of colors actually used in this image
    else        //Otherwise, the number of colors used is the biBitCount power of 2.
    {
          switch(bi.biBitCount){
          case1:NumColors=2;break;
          case4:NumColors=16;break;
          case8:NumColors=256;break;
          case24:
            {
                NumColors=0;        //For true color maps, no palette is used
                break;
            }
          default://Do not process other color numbers, it is considered that there is an error
          {
MessageBox(hWnd,"Invalidcolornumbers!","ErrorMessage", MB_OK|MB_ICONEXCLAMATION);                                                       
          _lclose(hf);
        return FALSE;           //Close the file and return FALSE
          }
    }

  if(bf.bfOffBits!=(DWORD)(NumColors*sizeof(RGBQUAD)+sizeof(BITMAPFILEHEADER)
  +sizeof(BITMAPINFOHEADER)))
  {
            //The calculated offset is inconsistent with the actual offset. There must be an error in the number of colors
            MessageBox(hWnd,"Invalidcolornumbers!","ErrorMessage",
            MB_OK|MB_ICONEXCLAMATION);
            _lclose(hf);
            return FALSE;//Close the file and return FALSE
  }
      bf.bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+NumColors*sizeof(RGBQUAD)+ImageSize;
  //Allocate memory with the size of BITMAPINFOHEADER structure length + palette + actual bitmap data
  if((hImgData=GlobalAlloc(GHND,(DWORD)(sizeof(BITMAPINFOHEADER)+
  NumColors*sizeof(RGBQUAD)+ImgSize)))==NULL)
  {
          //Memory allocation error
          MessageBox(hWnd,"Errorallocmemory!","ErrorMessage",
          MB_OK|MB_ICONEXCLAMATION);
          _lclose(hf);
          return FALSE;//Close the file and return FALSE
  }

  //The pointer lpImgData points to the memory area
  lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData);

  //The file pointer is relocated to the beginning of BITMAPINFOHEADER
  _llseek(hf,sizeof(BITMAPFILEHEADER),SEEK_SET);

  //Read file contents into lpImgData
  _hread(hf,(char*)lpImgData,(long)sizeof(BITMAPINFOHEADER)
  +(long)NumColors*sizeof(RGBQUAD)+ImgSize);

  _lclose(hf);//Close file
  if(NumColors!=0) //NumColors is not zero, indicating that the color palette is used
  {
          //Allocate local memory for the logical palette. The size is the length of the logical palette structure plus NumColors PALETTENTRY
          hPal=LocalAlloc(LHND,sizeof(LOGPALETTE)+NumColors*sizeof(PALETTEENTRY));

          //The pointer pPal points to the memory area
          pPal=(LOGPALETTE*)LocalLock(hPal);

          //Fill in the header of the logical palette structure
          pPal->palNumEntries=NumColors;
          pPal->palVersion=0x300;

          //lpRGB points to the beginning of the palette
          lpRGB=(LPRGBQUAD)((LPSTR)lpImgData+(DWORD)sizeof(BITMAPINFOHEADER));

          //Fill in each item
          for(i=0;i  
          {
              pPal->palPalEntry[i].peRed=lpRGB->rgbRed;
              pPal->palPalEntry[i].peGreen=lpRGB->rgbGreen;
              pPal->palPalEntry[i].peBlue=lpRGB->rgbBlue;
              pPal->palPalEntry[i].peFlags=(BYTE)0;
              lpRGB++;//Move pointer to next item
          }

  //To generate a logical palette, hPalette is a global variable
  hPalette=CreatePalette(pPal);

  //Free local memory
  LocalUnlock(hPal);
  LocalFree(hPal);
 }

  //Get device context handle
  hDc=GetDC(hWnd);
  if(hPalette)//If you just generated the logic palette
  {
          //Select the new logical palette into the DC and save the old logical palette handle in hPrevPalette
          hPrevPalette=SelectPalette(hDc,hPalette,FALSE);
          RealizePalette(hDc);
  }

  //Generate bitmap handle
  hBitmap=CreateDIBitmap(hDc,(LPBITMAPINFOHEADER)lpImgData,(LONG)CBM_INIT,
  (LPSTR)lpImgData+sizeof(BITMAPINFOHEADER)+NumColors*sizeof(RGBQUAD),
  (LPBITMAPINFO)lpImgData,DIB_RGB_COLORS);

  //Select the original palette (if any) into the device context handle
  if(hPalette&&hPrevPalette)
  {
          SelectPalette(hDc,hPrevPalette,FALSE);
          RealizePalette(hDc);
  }
  ReleaseDC(hWnd,hDc); //Release device context
  GlobalUnlock(hImgData); //Unlock memory area
  Return TRUE; //Successful return
}
  • 146

In the above procedure, there are two points to be explained:
First, for the diagram that needs a palette, in order to display correctly, a logical palette must be generated according to the. bmp file. The method of generation is:

1.Allocate memory for the logical palette pointer, and the size is the logical palette structure(LOGPALETTE)Length plus NumColors individual PALETTENTRY size.
(Each item in the palette is a PALETTEENTRY Structure)

2.Fill in the header of the logical palette structure pPal->palNumEntries=NumColors;pPal->palVersion=0x300;

3.To read the color palette from a file RGB Value, fill in each item.
  4,Generate logical palette: hPalette=CreatePalette(pPal)

Second, generate a BITMAP handle, which is done by the function CreateDIBitmap

hBitmap=CreateDIBitmap(
hDc,
LPBITMAPINFOHEADER)lpImgData,
(LONG)CBM_INIT,(LPSTR)lpImgData+sizeof(BITMAPINFOHEADER)+NumColors*sizeof(RGBQUAD), 
(LPBITMAPINFO)lpImgData,
DIB_RGB_COLORS);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

The function of CreateDIBitmap is to generate a bitmap independent of Windows devices.
Parameters of this function:
The first item is the device context handle. If the bitmap uses a palette, select the logical palette into the device context before calling CreateDIBitmap. After generating hBitmap, select the original palette into the device context and release the context;
Item 2: pointer to BITMAPINFOHEADER;
Item 3: use constant CBM_INI, not considered;
Item 4: pointer to the palette;
Item 5: pointer to BITMAPINFO (including BITMAPINFOHEADER, palette, and actual image data);
Item 6: just use the constant DIB_RGB_COLORS, don't think about it.

The device context is mentioned above. I believe readers who have compiled windows programs are not unfamiliar with it. Here is a brief introduction. Windows operating system uniformly manages operations such as display and printing, and regards them as devices one by one. Each device has a complex data structure to maintain. The so-called device context refers to this data structure. However, we can't deal with these device contexts directly. We can only refer to the handle identifying it (actually an integer) and let windows handle it accordingly. The resulting logical palette handle hPalette and bitmap handle hBitmap are to be processed in WM_ The paint message is used to display it on the screen. The processing process is as follows.

   StaticHDC hDC,hMemDC;
  PAINTSTRUCT ps;
  case WM_PAINT:
  {
          hDC=BeginPaint(hwnd,&ps);//Get screen device context
          if(hBitmap)//hBitmap is NULL at first. When it is not NULL, it indicates that there is a graph
    {
              hMemDC=CreateCompatibleDC(hDC);//Create a memory device context
              if(hPalette)//There is a palette
            {
                      //Select palette into screen device context
                      SelectPalette(hDC,hPalette,FALSE);
                      //Select palette into memory device context
                      SelectPalette(hMemDC,hpalette,FALSE);
                      RealizePalette(hDC);
            }
              //Select bitmap into memory device context
              SelectObject(hMemDC,hBitmap);
              //display bitmap
              BitBlt(hDC,0,0,bi.biWidth,bi.biHeight,hMemDC,0,0,SRCCOPY);
              //Free memory device context
              DeleteDC(hMemDC);
  }
  //Release screen device context
  EndPaint(hwnd,&ps);
  break;
  }

  • In the above program, we call CreateCompatibleDC to create a memory device context. The SelectObject function selects a device independent bitmap into the memory device context. Then we call BitBlt function to make bit copy in memory device context and screen device context. Because all operations are performed in memory, it is the fastest.

The parameters of BitBlt function are:

  1. The target device context, in the above program, is the screen device context. If it is changed to the print device context, it will not display the bitmap, but print;
  2. Top left corner of target rectangle x Coordinates;
  3. Top left corner of target rectangle y Coordinates, in the above program, 2 and 3 are (0, 0), indicating that they are displayed in the upper left corner of the window;
  4. The width of the target rectangle;
  5. The height of the target rectangle;
  6. The source device context, in the above program, is the memory device context;
  7. Top left corner of source rectangle x Coordinates;
  8. Top left corner of source rectangle y Coordinates;
  9. Operation mode, here is SRCCOPY,Indicates that the source rectangle is copied directly to the destination rectangle. Can also be reverse color, erase, do"And"Operation and other operations. See for details VC++help. You can try to change the parameters 2, 3, 4, 5, 7, 8 and 9, and you can understand their meaning.

Topics: Windows