bmp调色板仅存在于bibitcount≤8时,位于bfoffbits偏移处,每项4字节(bgr+reserved),大小为1

怎么读取 BMP 文件里的调色板(palette)数据
BMP 的调色板只存在于 BITMAPINFOHEADER 中 biBitCount ≤ 8 的情况,比如 1-bit、4-bit、8-bit 索引色图。它紧挨着文件头和信息头之后存放,每个调色板项是 4 字节:B、G、R、Reserved(通常为 0),顺序是蓝-绿-红,不是 RGB。
常见错误是直接按 RGBQUAD 结构体 memcpy,却忽略文件可能没对齐——BMP 调色板起始位置必须是 4 字节对齐,而信息头长度本身可能不是 4 的倍数,所以要手动跳过填充字节。
- 先读
BITMAPFILEHEADER得到bfOffBits,它就是调色板起始偏移(也是像素数据起始偏移) - 用
fseek(fp, bfOffBits - palette_size, SEEK_SET)回退到调色板开头再读,比靠计算更可靠 - 调色板大小 =
1 × 4 字节;但若 <code>biClrUsed > 0,则只取前biClrUsed项(有些图像只用了部分调色板) - 别硬编码 256 项——
biBitCount == 1时只有 2 项,== 4时是 16 项
怎么算每一行像素在文件里的起始位置(行偏移)
BMP 每行像素数据必须是 4 字节对齐的,所以实际每行占用字节数不是简单的 width * bytes_per_pixel,而是向上对齐到 4 的倍数: ((width * bits_per_pixel + 31) / 32) * 4(整数除法)。这个值叫 row_size。
容易踩的坑是以为像素数据从 bfOffBits 开始就连续排布——其实第 0 行(通常是图像底部)在最前面,第 height-1 行在最后;而且如果 biHeight 是负数,说明是 top-down 图像,此时第 0 行在顶部,但 Windows 大多数工具仍输出 bottom-down(biHeight > 0)。
- 第
y行(从 0 开始计数)的文件偏移 =bfOffBits + (height - 1 - y) * row_size(bottom-down 情况) - 如果
biHeight ,则偏移 = <code>bfOffBits + y * row_size -
row_size必须用位宽算,不是用字节数:例如 1-bit 图,width=17→ 实际每行占((17 + 31) / 32) * 4 = 8字节,不是 3 - 别用
width * sizeof(pixel_type)直接乘——1-bit 和 4-bit 根本没有“pixel_type”,它们是 packed bits
怎么正确解析 1-bit / 4-bit 像素数据(packed 格式)
1-bit 和 4-bit BMP 不是每个像素占独立字节,而是把多个像素 pack 进一个字节,高位在前(MSB first),且每行从左到右填满,不足 8 位的部分补 0。这意味着你得逐 bit 或逐 nybble 解包,不能当字节数组直接 cast。
典型错误是把 1-bit 图当 uint8_t* 遍历,结果每个字节当成 1 个像素,实际它含 8 个像素;或者没处理好跨字节边界时的位序,导致左右颠倒。
- 对 1-bit 图:第
x像素在字节byte_idx = x / 8,位索引bit_idx = 7 - (x % 8),用(data[byte_idx] >> bit_idx) & 1 - 对 4-bit 图:偶数
x取高 4 位:(data[x/2] >> 4) & 0xF;奇数x取低 4 位:data[x/2] & 0xF - 务必检查
biCompression == BI_RGB,否则可能是 RLE 编码,那整套逻辑都失效 - 别忘了每行末尾可能有填充字节(
row_size - ((width * bits_per_pixel + 7) / 8)),读的时候要跳过
为什么用 fread 直接读调色板或像素常出错
因为 BMP 是小端序(little-endian),而结构体成员如 BITMAPFILEHEADER 在不同编译器/平台上的内存布局可能受 #pragma pack 或默认对齐影响。直接 fread(&header, 1, sizeof(header), fp) 很可能读歪——特别是 DWORD 和 LONG 成员错位。
更稳妥的做法是逐字段读、手动转换字节序,并校验关键字段范围(比如 bfType 应该是 0x4D42 即 'BM',biSize 应为 40 或 108 等合法值)。
- 先读 2 字节判断
bfType是否等于0x4D42,不是就不是 BMP - 用
fread(&bfSize, 4, 1, fp)读 4 字节再用le32toh()或手动翻转,别依赖结构体整体读 - Windows BMP 的
biSizeImage字段经常为 0,此时必须自己算:row_size * abs(biHeight) - 如果
biBitCount == 24或32,根本没调色板,bfOffBits就是像素起点——这点常被忽略,导致强行读 256 项调色板然后越界
事情说清了就结束。调色板和行偏移这两块,错一点,整张图就花掉或上下颠倒,而且很难 debug——因为错误表现往往是颜色乱码或偏移错位,而不是崩溃或报错。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











