用十六进制编辑器打开一个 BMP 文件,前 14 个字节如下:
42 4D 46 00 00 00 00 00 00 00 36 00 00 00
这就是整个 BITMAPFILEHEADER。42 4D 是 ASCII 签名 "BM"。接下来四个字节表示文件大小——46 00 00 00 按小端序读作 70。再往后四个字节是保留字段,始终为零。最后四个字节是像素数据的偏移量——36 00 00 00 等于 54,意味着像素数组从第 54 个字节开始。
没有压缩标记,没有色彩配置文件,没有 Alpha 通道。BMP 只需要一个签名、一个大小和一个偏移量。顾名思义,它不过是一张位图。
BMP 究竟是什么
BMP 是 Bitmap(位图) 的缩写,更正式的名称是 Device Independent Bitmap(设备无关位图,DIB)。微软在 1990 年随 Windows 3.0 引入该格式,用于存储栅格图像,同时不将其绑定到特定显示设备。在 DIB 之前,Windows 使用 Device Dependent Bitmap(设备相关位图,DDB),其像素格式取决于当时安装的显卡。在一台机器上保存的 DDB,换一台机器可能无法读取。
BMP 通过在文件中保存足够的元数据解决了这个问题,使任何解码器都能重建图像:宽度、高度、位深、调色板、压缩类型以及原始像素数组。操作系统读取头部,分配缓冲区,然后将像素复制进内存。对于 24 位未压缩图像,像素数据通常只是自下而上逐行写入的 RGB 三元组,每行按四字节边界填充。
这种简洁性使 BMP 在十余年间成为 Windows 的默认图像格式。Paintbrush、Windows 壁纸引擎、早期扫描仪以及无数内部工具都原生支持 BMP。
文件结构
一个 BMP 文件由四部分组成,按顺序排列:
BITMAPFILEHEADER (14 bytes)
BITMAPINFOHEADER (40 bytes for the classic version)
Color Table (optional, for indexed color modes)
Pixel Data (the actual image)
BITMAPFILEHEADER(14 字节)
Bytes 0-1: Signature "BM" (0x42 0x4D)
Bytes 2-5: FileSize (uint32, little-endian)
Bytes 6-9: Reserved (always 0)
Bytes 10-13: Offset (uint32, offset to pixel data from file start)
签名并非总是 "BM"。Windows 也支持 "BA"、"CI"、"CP"、"IC" 和 "PT",用于光标和图标变体,但实际上你打开的每个 BMP 都以 "BM" 开头。
BITMAPINFOHEADER(40 字节,BITMAPINFOHEADER 类型)
Bytes 0-3: HeaderSize (40)
Bytes 4-7: Width (int32, pixels)
Bytes 8-11: Height (int32, pixels; negative means top-down)
Bytes 12-13: Planes (1)
Bytes 14-15: BitCount (1, 4, 8, 16, 24, or 32)
Bytes 16-19: Compression (0 = none, 1 = RLE8, 2 = RLE4, 3 = bitfields)
Bytes 20-23: ImageSize (0 if uncompressed)
Bytes 24-27: XpixelsPerMeter (physical resolution)
Bytes 28-31: YpixelsPerMeter (physical resolution)
Bytes 32-35: ColorsUsed (0 means 2^BitCount)
Bytes 36-39: ColorsImportant (0 means all)
后续 Windows 版本增加了更大的头部——52、56、108 和 124 字节——以支持 Alpha 掩码、色彩空间和 ICC 配置文件。但 40 字节版本仍是你最常遇到的。
高度即方向
如果高度字段为正,图像按自下而上存储。文件中的第 0 行是图像的最底行。如果高度字段为负,图像按自上而下存储。这是 BMP 的一个不易察觉的特点——大多数其他格式默认自上而下存储。
颜色表
对于 1、4 或 8 位深,BMP 使用索引调色板。每个调色板条目占四个字节:蓝、绿、红,以及一个几乎总是被忽略的保留 Alpha 字节。一个 256 色 BMP 在头部之后紧跟一个 1,024 字节的颜色表。随后的像素数据不是颜色,而是指向该表的索引。
像素数据填充
每条扫描线必须是四字节对齐。一张 24 位、宽 5 像素的图像每行需要 15 字节,向上取整为 16 字节。这些额外的填充字节就是浪费空间,也是 BMP 效率低下的另一个小原因。
以下是一个完整的 2 x 2 24 位未压缩 BMP 示例:
Offset 0x00: 42 4D 46 00 00 00 00 00 00 00 36 00 00 00 -- BITMAPFILEHEADER
Offset 0x0E: 28 00 00 00 02 00 00 00 02 00 00 00 01 00 18 00 -- BITMAPINFOHEADER
Offset 0x22: 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00
Offset 0x32: 00 00 00 00 00 00 00 00
Offset 0x36: FF 00 00 00 FF 00 00 00 FF -- bottom row: red, green, blue (with padding)
Offset 0x3F: 00 FF FF 00 FF FF 00 00 00 -- top row: cyan, magenta, yellow (with padding)
BMP 诞生时的竞争者
BMP 并非凭空出现。到 1990 年,已有多种图像格式在争夺注意力:
| 格式 | 压缩方式 | 色深 | 透明支持 | 专利风险 | 典型用途 |
|---|---|---|---|---|---|
| GIF | LZW | 最大 256 色 | 1 位 | 是(LZW) | 网页图形 |
| JPEG | DCT + Huffman | 24 位 | 无 | 基本无 | 照片 |
| TIFF | 多种可选 | 最高 48 位 | 有 | 可选 | 印刷、扫描 |
| PCX | RLE | 最高 24 位 | 无 | 无 | DOS 游戏、剪贴画 |
| PICT | RLE | 最高 32 位 | 有 | 无 | 经典 Mac OS |
| BMP | 无或 RLE | 最高 32 位 | 无 | 无 | Windows 壁纸、图标 |
GIF 当时已经是网页默认格式,但其 LZW 专利阴影使商业使用存在风险。JPEG 专为照片设计,会不可逆地丢弃数据。TIFF 功能强大但过度设计——编写一个通用 TIFF 读取器本身就是一项工程。PCX 则随 DOS 一同消亡。
BMP 的优势在于简单。它并不比其他格式更小、更快或更强大。它只是 Windows 无需任何额外库就能理解的格式。
BMP 的优势
BMP 在当时语境下确实有优势:
零解码复杂度。 24 位未压缩 BMP 只需读取固定大小的头部,然后将像素直接复制到帧缓冲区即可渲染。没有哈夫曼表,没有 DEFLATE 状态机,没有渐进式扫描。这使其成为 CPU 能力有限的嵌入式系统的理想选择。
无专利负担。 与 GIF 的 LZW 不同,BMP 的压缩要么不存在,要么是 RLE,两者都不受专利限制。这对希望避开 Unisys GIF 许可的开源工具和商业软件至关重要。
与 Windows 直接集成。 Win32 API 围绕 DIB 段提供了 LoadBitmap、BitBlt 和 StretchBlt。开发者可以将 BMP 加载到内存,用几行 C 代码就将其 blit 到屏幕上。
可预测的内存布局。 由于未压缩 BMP 像素可直接映射到显存,20 世纪 90 年代的游戏引擎和图形工具将 BMP 用作纹理交换格式。你可以对文件进行内存映射,把像素偏移量当作原始缓冲区处理。
BMP 的问题
BMP 的优势反过来也成了它的短板。这个格式没有跟上行业的发展。
没有有效压缩。 未压缩 BMP 体积巨大。一张 1920 x 1080 的 24 位图像大小为 5.93 MB。同一张图以 JPEG 质量 90 压缩后大约只有 300–500 KB。当网速和存储空间成为限制因素时,BMP 就显得很不合时宜。
RLE 压缩效果差。 BMP 支持 RLE8 和 RLE4 行程长度编码,但 RLE 只对包含大片均匀区域的图像有效。照片和渐变压缩效果很差。实际中 RLE 编码的 BMP 很少见。
没有真正的透明。 标准 BMP 没有 Alpha 通道。32 位 BMP 变体包含 Alpha 字节,但许多工具会忽略它或将其视为保留字段。对于网页图形和游戏精灵图,与 PNG 相比 BMP 毫无用处。
无动画、无元数据、无色彩管理。 BMP 只存储像素,仅此而已。没有 EXIF,原始头部不支持 ICC 配置文件,也没有动画帧。随着工作流日益复杂,BMP 始终停留在原始状态。
自下而上存储。 默认的自下而上顺序会让解码器需要额外处理,转换时也会浪费 CPU 周期。这个细节没有带来任何好处,只是增加了不必要的麻烦。
取代 BMP 的格式
BMP 的局限性直接塑造了后来的格式。
PNG(1996) 解决了透明和压缩问题。它提供无损压缩、8 位 Alpha 通道,以及比 TIFF 更简洁的规范。PNG 几乎在所有网页和跨平台应用中取代了 BMP。
JPEG(1992) 解决了照片问题。基于 DCT 的有损压缩使照片体积比未压缩 BMP 小 10–20 倍。对于任何连续色调图像,JPEG 都成为默认选择。
WebP(2010) 在一个容器中同时提供有损和无损模式、动画和 Alpha 通道。无损 WebP 通常比 PNG 小 20–30%,而 PNG 本身已远超 BMP。
HEIC/HEIF(2013) 将基于 HEVC 的压缩引入静态图像。它在画质更好的同时文件比 JPEG 更小,支持 Alpha 通道和深度图,现已成为 Apple 设备的默认格式。iPhone 保存照片时,通常就是 HEIC。
这些格式中的每一种出现,都是因为 BMP 已无法满足特定用例。
BMP 仍在哪些地方出现
BMP 并没有消失,只是退到了一些小众场景里——在那里,简单性比效率更重要。
Windows 内部。 Windows 启动画面、旧版对话框和某些系统资源仍在使用 BMP。Win32 GDI API 期望 DIB 数据,而重写这些代码路径对微软来说不值得。
嵌入式系统与微控制器。 Arduino 或 STM32 上的小尺寸 LCD 没有足够 RAM 和 CPU 来解码 PNG。一个 16 位未压缩 BMP 几乎无需代码就能直接复制到帧缓冲区。
医疗与科学影像。 一些旧版 DICOM 查看器和实验室设备导出 BMP,因为每个系统都能打开它。该格式的简单性降低了互操作性风险,在受监管环境中这一点至关重要。
逆向工程与教育。 BMP 仍是教授图像文件结构的最佳格式之一。头部小巧,像素布局直观,一个下午就能写出一个可用的解码器。
游戏开发遗产。 较老的游戏引擎和纹理工具曾将 BMP 用作中间格式。一些 Mod 社区仍在处理 BMP 资源,因为相关工具都是围绕它构建的。
通过签名检测 BMP
不要相信 .bmp 扩展名。读取前两个字节。
TypeScript
async function isBmp(file: File): Promise<boolean> {
const buffer = await file.slice(0, 2).arrayBuffer()
const bytes = new Uint8Array(buffer)
return bytes.length === 2 && bytes[0] === 0x42 && bytes[1] === 0x4d
}
Python
def is_bmp(path: str) -> bool:
with open(path, "rb") as f:
return f.read(2) == b"BM"
Go
func isBmp(path string) bool {
f, err := os.Open(path)
if err != nil {
return false
}
defer f.Close()
buf := make([]byte, 2)
if _, err := f.Read(buf); err != nil {
return false
}
return bytes.Equal(buf, []byte("BM"))
}
PHP
function isBmp(string $path): bool {
$header = file_get_contents($path, false, null, 0, 2);
return $header === "BM";
}
ImageMagick CLI
magick identify -verbose image.bmp | grep "Format:"
# Format: BMP (Microsoft Windows bitmap image)
或者简单地:
file image.bmp
# image.bmp: PC bitmap, Windows 3.x format, 1920 x 1080 x 24
BMP 的未来
BMP 是一个已完成的格式。自 Windows 95 时代以来,核心规范没有发生任何实质性变化。这既是限制,也是保证:1995 年写入的 BMP 今天仍能正确打开。
该格式不会增加新特性。它不会获得更好的压缩、完善的 Alpha 处理或动画支持。没人投资 BMP,因为没人需要更好的 BMP。PNG、WebP、AVIF 和 HEIC 已经在所有注重效率的场景中胜出。
但 BMP 仍会在它已经立足的地方继续存在:Windows 内部、嵌入式显示屏、旧版医疗设备以及课堂上。它不起眼、效率也不高,却很难被移除,因为太多系统都知道如何读取它。
如果你手头有需要在现代设备上继续使用的旧版 BMP 文件,最实际的做法就是转换格式。照片转成 JPEG 或 HEIC 可以节省空间;带透明度的图形适合用 PNG 或 WebP;而如果你需要与 Apple 生态对接,把 HEIC 转成通用可读格式是第一步。
如果你需要将 HEIC 转成更通用的格式,可以试试我们的 HEIC 转 JPG 工具,它完全在浏览器中运行,文件不会离开你的设备。如果需要保留透明度的无损输出,HEIC 转 PNG 更合适。而要兼顾网页体积与浏览器兼容性,HEIC 转 WebP 能输出更小的文件,并获得广泛支持。
BMP 给整个行业留下了一个有价值的参照:一种格式不一定是最优秀的,却能无处不在。只要它足够简单,人人都会实现;只要它被用得够广,就没有人会去移除它。



