系列文章目录
文章目录
- 系列文章目录
- 前言
- 一、BMP格式概览
- 二、实战分析bmp图片数据
前言
对学习C++感兴趣的可以看看这篇文章哦:C/C++实战入门到精通
BMP图片大家见的应该也比较多,它是一种非常基本的图片格式
因为最近对C++生成二维码比较感兴趣,用第三方库可以很容易得到二维码的信息
但还需要将生成的二维码源数据保存为图片,而BMP结构稍微简单一点,所以便研究了一下
一、BMP格式概览
BMP文件格式还是比较简单的,总共分为四部分:
BITMAPFILEHEADER
结构体,BITMAPINFOHEADER
结构体,RGBQUAD
结构体(这个结构体可以有,也可以没有),DIB
数据区。(Device-Independent Bitmap
,设备无关位图)。
在继续探究这些格式之前,我们还需要明白一个概念,即:RGB
(Red
,Green
,Blue
)三原色,我们图片中的像素就是用这三种颜色构成的
我们常看到的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
最后的a
,Alpha
透明度
综上,我们需要了解的就只有前两个结构体,和最后的DIB
数据区了
首先来看第一个结构体定义:
typedef struct tagBITMAPFILEHEADER {
WORD bfType; //图片种类,BMP图片固定为BM,表示为十六进制就是0x4d42
DWORD bfSize; //该图片文件的大小
WORD bfReserved1; //保留字,不用管
WORD bfReserved2;//保留字,不用管
DWORD bfOffBits; //实际图片数据的偏移量,即`DIB`的偏移量,也即前三个结构体的大小
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;
该结构体中,我们需要在意的只有两个属性:bfSize
与bfOffBits
,分别等于该图片文件的大小,以及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则代表一个白色点