Windows中有两种位图格式,一种是GDI位图对象,另一种就是设备无关位图DIB(Device Independent Bitmap),扩展名为BMP。
一、BMP图片格式
1、当DIB存储成文件时(即后缀为BMP的图片文件),它的格式如下图:
特别说明:
1位、4位、8位位图才有Color Table颜色表,像素位表示颜色表数组的索引
16位、24位、32位位图是没有Color Table的,像素位直接就是RGB数据
不过现在计算机中的位图几乎都是24位或32位的了。
2、文件头的定义如下:
typedef struct tagBITMAPFILEHEADER {
WORD bfType; // 文件类型,0x4d42即"BM",小端
DWORD bfSize; // 文件大小
WORD bfReserved1; // 保留字段1,总为0
WORD bfReserved2; // 保留字段2,总为0
DWORD bfOffBits; // 像素位距离文件头的偏移,字节
} BITMAPFILEHEADER;
用二进制方式打开BMP文件分析文件头:
bfType: 0x42 4d即"BM"
bfSize: 0x36 00 03 00,注意这里是小端模式,即文件大小为196662(0x30036)字节
bfOffBits:0x36 00 00 00,像素数据从文件开始偏移54字节,即BITMAPFILEHEADER和BITMAPINFOHEADER刚好54字节,不包含颜色表
3、信息头的定义如下:
typedef tagBITMAPINFOHEADER {
DWORD biSize; // 结构体大小
LONG biWidth; // BMP图片宽,像素
LONG biHeight; // BMP图片高,像素
WORD biPlanes; // 总是1
WORD biBitCount; // 用多少比特表示一个像素即位深
DWORD biCompression; // 通常为BI_RGB,表示未经压缩
DWORD biSizeImage; // 像素数据大小
LONG biXPelsPerMeter; // 实际水平分辨率,没什么用
LONG biYPelsPerMeter; // 实际垂直分辨率,没什么用
DWORD biClrUsed; // 用到的颜色数
DWORD biClrImportant; // 重要的颜色数
} BITMAPINFOHEADER;
注意:
biHeight为负数表示自上而下存储
biHeight为正数表示自下而上存储,这种情况图片显示出来是倒着的
用二进制方式打开BMP文件分析信息头:
biSize:0x28 00 00 00,结构体大小为40字节
biWidth: 0x00 01 00 00,图片宽256像素
biHeight:0x00 01 00 00,图片高256像素
biPlanes:0x01 00
biBitCount:0x18 00,每像素24比特
二、DIB位图的内存表示
把BMP文件加载到内存中,我们用BITMAPINFO结构的指针来指向这块内存,从信息头开始读取,不包括文件头。
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
} BITMAPINFO;
typedef struct tagRGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD;
颜色表就是RGBQUAD结构的数组,且biBitCount字段不大于8才有颜色表
从RGBQUAD结构也可以看到实际存储的时候是以BGR顺序存的
三、 处理DIB位图的重要API
1、给颜色表RGBQUAD赋值
UINT GetDIBColorTable(
__in HDC hdc,
__in UINT uStartIndex,
__in UINT cEntries,
__out RGBQUAD *pColors
);
2、显示DIB位图
int SetDIBitsToDevice(
__in HDC hdc,
__in int XDest,
__in int YDest,
__in DWORD dwWidth,
__in DWORD dwHeight,
__in int XSrc,
__in int YSrc,
__in UINT uStartScan,
__in UINT cScanLines,
__in const VOID *lpvBits,
__in const BITMAPINFO *lpbmi,
__in UINT fuColorUse
);
int StretchDIBits( // 可拉伸,图片会失真
__in HDC hdc,
__in int XDest,
__in int YDest,
__in int nDestWidth,
__in int nDestHeight,
__in int XSrc,
__in int YSrc,
__in int nSrcWidth,
__in int nSrcHeight,
__in const VOID *lpBits,
__in const BITMAPINFO *lpBitsInfo,
__in UINT iUsage,
__in DWORD dwRop
);
如果要重复多次显示同一张位图最好不用这两个函数,效率较低,可以先把DIB位图转换为GDI位图对象再进行显示
3、根据DIB位图创建GDI位图对象
HBITMAP CreateDIBitmap(
__in HDC hdc,
__in const BITMAPINFOHEADER *lpbmih,
__in DWORD fdwInit,
__in const VOID *lpbInit,
__in const BITMAPINFO *lpbmi,
__in UINT fuUsage
);
4、创建内存DIB位图
HBITMAP CreateDIBSection(
__in HDC hdc,
__in const BITMAPINFO *pbmi,
__in UINT iUsage,
__out VOID **ppvBits,
__in HANDLE hSection,
__in DWORD dwOffset
);
四、自己写的一个练手小项目
实现的功能有:
1.显示BMP图片
2.从BMP图片中提取RGB数据,保存成.rgb文件
可以用RGB播放器打开该.rgb文件,验证是否和BMP图片显示的一样。
3.屏幕截图
<使用方法>
a.按住鼠标左键拖动鼠标选择截图区域,释放鼠标左键完成截图
b.按下CTRL+A可以全屏截图
c.按下Esc键取消截图
d.鼠标双击图片可以另存为BMP文件
所有功能全部用Win32 API实现,不调用其他库。
rgb文件查看工具借用了雷霄骅(雷神)的YUV/RGB播放器:https://blog.csdn.net/leixiaohua1020/article/details/50466201
项目截图:
源码链接:https://download.csdn.net/download/weixin_37853880/12460951