直接用 fread 读 BMP 文件头会出错,必须先校验文件签名“BM”(0x4D42),再按 bfOffBits 跳转、处理行对齐与上下颠倒、BGR 顺序及小端存储,否则易导致花屏或无法加载。

直接用 fread 读 BMP 文件头会出错?先校验文件签名
BMP 文件开头必须是 "BM" 两个字节,否则不是合法 BMP。很多代码直接 fread 结构体就跑,结果读到损坏或非 BMP 文件时内存错乱。
- 打开文件后立刻用
fread(&signature, 1, 2, fp)读前两字节,检查是否等于0x4D42(小端序下'B'在低字节) -
BITMAPFILEHEADER固定 14 字节,但bfOffBits字段决定像素数据起始位置,不能硬编码跳过 54 字节——因为可能有调色板、压缩或对齐填充 - 若
biCompression != BI_RGB(比如BI_RLE8),标准 RGB 读取逻辑会失败,应直接拒绝处理
读取像素数据时注意 Windows BMP 的行对齐和上下颠倒
Windows BMP 每行字节数必须是 4 的倍数,不足则补 0;且图像数据从底行开始存储(即第 0 行是图片最下面一行),不翻转会导致图倒过来。
- 计算每行实际字节数:
rowSize = ((width * bitsPerPixel + 31) / 32) * 4 - 分配缓冲区大小为
rowSize * height,再用fread一次性读入 - 逐行复制到目标图像数组时,把第
i行写入目标的height - 1 - i行,完成上下翻转 - 如果只处理 24 位真彩色(
bitsPerPixel == 24),每个像素 BGR 顺序(不是 RGB),需手动交换 R 和 B
保存 BMP 时别漏掉 bfSize 和 biSizeImage 的正确计算
这两个字段填错,系统可能拒绝加载或显示为全黑——它们不是可选字段,而是解析器判断数据长度的关键依据。
-
bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + paletteSize + pixelDataSize,其中paletteSize对 24 位图是 0 -
biSizeImage = rowSize * height(注意不是width * height * 3,要含补齐字节) - 写入顺序必须严格为:
BITMAPFILEHEADER→BITMAPINFOHEADER→ 调色板(如有)→ 像素数据(从底行开始、BGR 顺序) - 写像素数据前记得把内存中的 RGB 图像先转成 BGR,并按行倒序排列
没有第三方库时,std::ifstream 比 fopen 更安全但要注意二进制模式
用 C++ 流操作容易在 Windows 下因换行符转换出问题,尤其读写二进制图像数据时。
立即学习“C++免费学习笔记(深入)”;
- 必须加
std::ios::binary标志:std::ifstream fin("a.bmp", std::ios::binary) - 读结构体推荐用
fin.read(reinterpret_cast,避免流运算符自动跳过空白或截断(&header), sizeof(header)) - 写入像素数据时,别用
,改用fout.write(ptr, size),否则可能只写部分字节 - 跨平台读写建议统一用小端序处理整数字段(BMP 规范要求小端),无需额外
htole32等转换
BMP 格式看着简单,但 bfOffBits、行对齐、BGR 顺序、小端存储这四点任意一个算错,都会导致图像花屏、偏移或打不开。尤其是调试时看到“能读出来但图是斜的/紫的/只有半张”,大概率是这几个地方没对齐。










