BMP文件需手动构造BITMAPFILEHEADER和BITMAPINFOHEADER头结构,像素数据按BGR顺序、从下到上存储且每行4字节对齐;24位真彩色推荐biBitCount=24、biCompression=BI_RGB,并须翻转内存行序以匹配BMP左下原点规范。

用 BITMAPINFO 和 WriteFile 手动写 BMP 文件头与像素数据
Windows 平台下最直接、可控的方式不是调用 GDI/GDI+ 封装函数,而是自己构造 BMP 文件结构。BMP 是纯二进制格式:文件头(BITMAPFILEHEADER)+ 信息头(BITMAPINFOHEADER)+ 调色板(RGBQUAD,仅 1/4/8 位需要)+ 像素数据(BGR 顺序、每行 4 字节对齐)。内存中已有原始像素(比如 uint8_t*),只需按规范拼接并写入磁盘。
关键点在于:BITMAPINFOHEADER.biWidth 和 biHeight 决定图像尺寸;biBitCount 必须是 24 或 32(推荐 24 位真彩色,省去 Alpha 处理);biCompression 设为 BI_RGB;像素数据必须从下到上(BMP 的原点在左下角),如果你的内存数据是上到下(如 OpenGL 或多数 CPU 渲染结果),得手动翻转行序。
- 每行字节数 =
((width * biBitCount + 31) / 32) * 4(向上对齐到 4 字节) - 文件总大小 =
sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + pixel_data_size - 写入前先用
CreateFile打开文件,用WriteFile一次性写入头+数据,别用 C 标准库的fwrite(可能触发换行转换或缓冲问题)
24 位 BGR 数据保存时为何图像上下颠倒?
因为 BMP 规范要求像素数据从图像底部开始存储(即第 0 行是图片最下面一行)。如果你的内存 pixels 是按常规光栅顺序(第 0 行 = 图像顶部),直接写入就会导致镜像翻转。这不是 bug,是格式定义。
解决方式是逐行复制时反向遍历:
stride_in_bytes
bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + biSizeImage- 若你填了
biSizeImage却没同步更新bfSize,用画图打开会提示“不是有效的 BMP 文件” - 32 位数据需确保 Alpha 通道不参与写入(BMP 不支持带 Alpha 的 BI_RGB,除非用 BI_BITFIELDS,但兼容性差)
跨平台保存时为什么不用 Gdiplus::Bitmap::Save?
因为依赖 GDI+ DLL(gdiplus.dll),且初始化麻烦(要调 GdiplusStartup)、线程不安全、对内存数据封装弱。更严重的是:它默认把内存数据当 BGRA 解释,而你传入的可能是 BGR 或 RGB,容易颜色错乱;且不暴露对齐控制,某些尺寸下会悄悄补黑边。
纯 Win32 手动写法无额外依赖、零初始化开销、完全掌控字节布局。唯一代价是多写约 50 行 C++ 代码——但这些代码稳定运行二十年不会变。
真正复杂的地方不在结构体填充,而在行对齐计算和上下翻转逻辑是否与你的原始数据方向严格匹配。哪怕 width=1920 这种常见分辨率,(1920*3+31)/32*4 == 5760,而错算成 5764 就会导致每行多写 4 字节,后续所有行偏移全错。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











