BMP图片格式分析(超详细)


前言

对学习C++感兴趣的可以看看这篇文章哦:C/C++教程

BMP图片大家见的应该也比较多,它是一种非常基本的图片格式

因为最近对C++生成二维码比较感兴趣,用第三方库可以很容易得到二维码的信息

但还需要将生成的二维码源数据保存为图片,而BMP结构稍微简单一点,所以便研究了一下

一、BMP格式概览

BMP文件格式还是比较简单的,总共分为四部分:

  1. BITMAPFILEHEADER结构体,
  2. BITMAPINFOHEADER结构体,
  3. RGBQUAD结构体(这个结构体可以有,也可以没有),
  4. DIB数据区。(Device-Independent Bitmap,设备无关位图)。

在继续探究这些格式之前,我们还需要明白一个概念,即:RGBRedGreenBlue)三原色,我们图片中的像素就是用这三种颜色构成的

我们常看到的8位色,16位色,24位色和32位色这些,实际指的就是用多少位来表示一个颜色(又称位深度)

位 指的是二进制的一位,一个字节(char)有8位

在这里插入图片描述
以8位色为例,即一个char,可以表示的范围为:0 - 28-1,即256种颜色,如果是1位色,那就只能表示两个颜色,即黑白图片

目前主流的是24位色和32位色,所以这里不讨论8位数和16位色

事实上第三个结构体RGBQUAD就是为8位、16位色等较低位数准备的,使用起来也更麻烦

本文以24位色为主。即RGB三原色,各占8位大小,即各占一个字节

如果是32位,则RGB三原色各占8位,还剩下8位用于描述透明度的,即:rgba最后的aAlpha透明度

综上,我们需要了解的就只有前两个结构体,和最后的DIB数据区了

首先来看第一个结构体定义:

typedef struct tagBITMAPFILEHEADER {
    
    
        WORD    bfType; //图片种类,BMP图片固定为BM,表示为十六进制就是0x4d42
        DWORD   bfSize; //该图片文件的大小
        WORD    bfReserved1; //保留字,不用管
        WORD    bfReserved2;//保留字,不用管
        DWORD   bfOffBits; //实际图片数据的偏移量,即`DIB`的偏移量,也即前三个结构体的大小
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;

该结构体中,我们需要在意的只有两个属性:bfSizebfOffBits,分别等于该图片文件的大小,以及DIB数据区前三个结构体的大小

然后是第二个结构体:

typedef struct tagBITMAPINFOHEADER{
    
    
        DWORD      biSize; //指定此结构体的长度
        LONG       biWidth; //bmp图片的宽度
        LONG       biHeight; //bmp图片的高度
        WORD       biPlanes; //平面数,显示器只有一个平面,所以一般为1
        WORD       biBitCount; //颜色位数,目前一般用24位或32位
        DWORD      biCompression; //压缩方式,可以是0,1,2,0表示不压缩,BMP为不压缩,所以为0
        DWORD      biSizeImage; //实际位图数据占用的字节数.由于上面不压缩,所以这里填0即可
        LONG       biXPelsPerMeter; //X方向分辨率,即每米有多少个像素,可以省略
        LONG       biYPelsPerMeter; //Y方向分辨率,即每米有多少个像素,可以省略
        DWORD      biClrUsed;  //使用的颜色数,如果为0,则表示默认值(2^颜色位数)
        DWORD      biClrImportant; //重要颜色数,如果为0,则表示所有颜色都是重要的
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;

这个结构体的信息量非常多,但需要我们填的其实并不多:

  • biSize:指定此结构体的长度,一般都直接为: sizeof(BITMAPINFOHEADER)即可
  • biWidth:图片宽度,需要自己根据需要填
  • biHeight:图片高度,需要自己根据需要填
  • biPlanes:平面数,填1即可
  • biBitCount:位数,本文采用的是24位图,所以填24

除了以上几个需要填的,其它都直接清零即可

由于第三个结构体只有低于24位的才用得到,所以这里不予讨论了

结构体为:

typedef struct tagRGBQUAD {
    
    
        BYTE    rgbBlue; //该颜色的蓝色分量
        BYTE    rgbGreen;  //该颜色的绿色分量
        BYTE    rgbRed; //该颜色的红色分量
        BYTE    rgbReserved;  //保留值
} RGBQUAD;

最后是数据区域,由于本文以24位图为主,所以每一个数据都是三个字节,分别代表红绿蓝

二、实战分析bmp图片数据

这里仍然是以24位图为例,用十六进制编辑器打开bmp图片:

在这里插入图片描述

首先来看第一个结构体:BITMAPFILEHEADER,其四个成员变量定义分别为:

  • WORD bfType

WORD,两个字节,这里为42 4D,右边也显示出字符为BM,但由于内存中数据排列高位在左,低位在右,所以代码中我们要写为0x4D42

  • DWORD bfSize

DWORD,4个字节,这里为76 05 00 00,颠倒还原之后为 00 00 05 76,换算为十进制为1398字节
在这里插入图片描述

然后是两个保留字段,没有意义,都为WORD类型,共占4个字节,跳过

然后是

  • DWORD bfOffBits;

DWORD,占4个字节,为36 00 00 00,颠倒还原后为00 00 00 36 ,换算为10进制为:54,两个结构体大小分别为:14 和 40 符合

在这里插入图片描述

然后再来看第二个结构体:

  • DWORD biSize;

占4个字节,为28 00 00 00 ,还原后为00 00 00 28,换算为十进制为:40,符合该结构体的大小

  • LONG biWidth;

4字节,为15 00 00 00,颠倒还原,转换为10进制为21

  • LONG biHeight;

4字节,EB FF FF FF,颠倒后为 FF FF FF EB,第一位为F(即1111),二进制首位为1,说明它为负数,转换为10进制后为 -21,可参考官方文档对它的说明:

在这里插入图片描述

简单来说,就是该数如果为正,那么该图片的数据就是倒着放的,先放倒数第一行的数据,然后是倒数第二行,最后放第一行

而如果为负数,则相反,数据正着放,更符合我们的直观

同样符合:

在这里插入图片描述

  • WORD biPlanes;

2字节,为01 00 ,还原后就是1,即一个平面数,符合

  • WORD biBitCount;

2字节,为18 00,还原后为24,即24位图,符合

  • DWORD biCompression;

4字节,为00 00 00 00 ,即为0,未压缩,符合

  • DWORD biSizeImage;

4字节,同样是00 00 00 00,由于未压缩,所以该字段为0,符合

最后四个字段,都为4字节,即16个字节。都为0,即默认值,符合
在这里插入图片描述

结束了前面两个结构体的解析,后面就是真正的颜色数据了,但由于这张图片是一个我自己生成的二维码:

在这里插入图片描述
所以只有两种色值,即黑色(rgb均为 0)与白色(rgb均为0xFF)

随便举个例子:
在这里插入图片描述
分别三个字节,即24位表示一个颜色,00 00 00 代表一个黑色点,而 FF FF FF则代表一个白色点

猜你喜欢

转载自blog.csdn.net/weixin_50964512/article/details/128646165