用#pragma pack(1)强制1字节对齐读取BITMAPFILEHEADER(14字节)和BITMAPINFOHEADER(按biSize动态读),仅当biBitCount≤8时按num_colors=1<<biBitCount读取RGBQUAD调色板,像素数据起始位置严格以bfOffBits为准。

怎么用 C++ 读取 BMP 文件头和调色板
直接读取 BMP 文件头和调色板,核心就两步:按字节顺序读结构体、小心对齐与大小端。Windows BMP 是小端序、结构体默认按 2 或 4 字节对齐,但文件头要求严格 1 字节对齐,否则 fread 会把字段读歪。
实操建议:
- 用
#pragma pack(1)强制结构体 1 字节对齐(VC/GCC 都支持),别依赖编译器默认对齐 -
BITMAPFILEHEADER固定 14 字节,BITMAPINFOHEADER至少 40 字节,但实际可能更长(比如含BITFIELDS扩展)——必须用biSize字段判断真实长度 - 调色板(color table)不是必有:只有
biBitCount ≤ 8时才存在;它紧跟在BITMAPINFOHEADER后面,每个表项是RGBQUAD(4 字节,BGR 顺序 + 保留字节) - 别硬写 “读 1024 字节调色板”,要算:
num_colors = (1 ,再乘以 4;超过 256 就说明不是标准索引色图(比如某些 OS/2 BMP)
为什么 fread 读出来的 biWidth 是负数或乱码
常见错误现象:刚读完 BITMAPINFOHEADER,发现 biWidth 是负值、biHeight 极大、biPlanes 不是 1 —— 八成是结构体没对齐,字段错位了。
根本原因是:未加 #pragma pack(1) 时,编译器会在 WORD、DWORD 字段间插入填充字节,而文件里没有这些填充。例如 biSize(4 字节)后面本该紧挨着 biWidth(4 字节),但编译器可能插了 2 字节 padding,导致后续所有字段偏移 2。
立即学习“C++免费学习笔记(深入)”;
使用场景:只要用 fread(&header, sizeof(header), 1, fp) 读原生结构体,就必须保证内存布局和文件字节流完全一致。
实操建议:
- 定义结构体前加
#pragma pack(push, 1),定义后加#pragma pack(pop) - 读完立刻检查
bmfh.bfType == 0x4D42(即'BM'),不是就说明整个读取已失效 - 验证
biPlanes == 1和biCompression == BI_RGB,不满足说明不是基础 BMP,调色板解析逻辑可能不适用
调色板 RGBQUAD 的 BGR 顺序和 alpha 字节怎么处理
Windows BMP 的 RGBQUAD 是 BGR+reserved,不是 RGB。第 0 字节是蓝(B),第 1 字节是绿(G),第 2 字节是红(R),第 3 字节保留(通常为 0)。很多代码直接当成 RGB 用,结果颜色全发紫。
性能与兼容性影响:这个顺序是文件规范强制的,改不了;但如果你后续要转成 RGBA 供 OpenGL 或 Skia 使用,得手动重排字节,别指望 memcpy 一把梭。
实操建议:
- 读取单个
RGBQUAD后,用uint8_t r = quad.rgbRed; uint8_t g = quad.rgbGreen; uint8_t b = quad.rgbBlue;—— 注意字段名是“Red/Green/Blue”,但文件里位置是反的 - 第 4 字节
rgbReserved多数情况为 0,但某些工具(如老版 Photoshop)会写 0xFF 表示“不透明”,不能忽略,需显式初始化或清零 - 如果目标是生成 PNG 或显示到 Qt widget,建议立刻转成标准 RGBA 格式缓存,避免每次取色都换序
为什么 32 位 BMP 没调色板却仍有 4 字节对齐的“伪调色板”区
常见错误现象:读 biBitCount == 32 的 BMP,仍尝试读调色板,结果把图像数据第一行当成了调色板,后续解码全乱。
原因:部分 BMP 编码器(尤其是 GDI+ 导出)会在 BITMAPINFOHEADER 后面空出 4 字节(有时更多),假装是调色板占位符,但 biClrUsed 为 0,且 biBitCount > 8,此时根本没有调色板。
实操建议:
- 只在
biBitCount ≤ 8时计算并读取调色板,其它情况跳过 - 计算像素数据起始位置用公式:
data_offset = bmfh.bfOffBits,而不是“header_size + palette_size”——bfOffBits是文件中明确给出的偏移,最可靠 - 如果
bfOffBits小于sizeof(BITMAPFILEHEADER) + biSize,说明文件损坏,直接拒绝解析
最容易被忽略的是:bfOffBits 包含了文件头、信息头、调色板三者总长度,但它本身是文件内绝对偏移,不是相对长度。有人用 ftell 读完头再算差值,反而容易因换行符或缓冲问题出错——直接信 bfOffBits 就行。



















