Skip to content

开发踩坑 #2:我一直以为 32 位 BMP 有 alpha 通道

2026-06-23

Tags: Windows · 开发踩坑


我有一个信了好多年的常识:32 位 BMP,每像素 4 字节,BGRA,第 4 个字节就是 alpha。 PNG 是 RGBA,BMP 不过是字节序反过来——清楚、干净、天经地义。

直到我做一个 ICO 工具,透明度以一种完全说不通的方式坏掉,我才翻开 BMP 规范——发现自己信了个谎。

一个所有人都「知道」的事实

故事的开头非常正常。我想要一个能拆开 ICO、预览每一帧、并保住透明度的工具。逻辑简单得不能再简单:

  1. 读出 ICO 里某一帧的 BMP 数据
  2. 解码成位图
  3. 显示出来

第一帧加载出来:颜色对,形状对,透明的地方全变成了黑色实心块

我的第一反应是——这 ICO 文件坏了。换一个,还是黑。再换一个,还是黑。

不对劲。这些图标在 Windows 资源管理器里明明透明得好好的。

规范是怎么说的

我去翻了 BMP 规范。经典的 BITMAPINFOHEADER 是 40 字节,biBitCount = 32 表示每像素 32 位。然后我看到那关键的一句——

biCompressionBI_RGB 时,每个像素 DWORD 的高位字节是 unused(未使用)

未使用。不是 alpha,不是透明度,是「我们也不知道这字节该放啥,先空着」。

换句话说:在最基础、最常见的 32 位 BMP 格式里,规范压根没定义 alpha 通道。每个像素的第 4 个字节,按规矩只是个凑数的 padding。

这跟我「知道」了多年的事,完全相反。

那 alpha 又是怎么「有」的

BMP 后来其实补过课。BITMAPV4HEADER(108 字节)引入了 BI_BITFIELDS,允许你显式声明一组位掩码——红、绿、蓝,还有 alpha。到了这一版,alpha 才第一次在 BMP 里名正言顺。

问题是:绝大多数 BMP 文件,包括 ICO 里嵌着的那一帧帧,用的还是老的 40 字节头、BI_RGB、不带掩码。按规范,它们的第 4 字节就是 padding。

那为什么图标在 Windows 里看着是透明的?

因为整个 Windows 图形栈在 XP 时代达成了一个不成文的默契:「得了,大家都假装那个 padding 字节是 alpha 吧。」GDI 的混合函数暗示可以这么用,各家渲染器心照不宣地照做。于是「32 位 BMP 有 alpha」就变成了一个人人都信、却没人写进规范的「事实」。

ICO 其实有两套透明度

更妙的是,ICO 根本不指望 BMP 的那一个字节。它有自己官方的透明度机制:AND 掩码。

一帧 ICO 的 BMP 数据长这样:

  • 像素数据(XOR 掩码)——就是 BGR 外加那个身份不明的第 4 字节
  • 紧跟着一块 1 位单色掩码(AND 掩码)——每个像素 1 个 bit,1 表示透明,0 表示不透明

这才是规范白纸黑字认可的 ICO 透明度。那个 32 位的 alpha,是后来叠加上去的「增强项」。

Windows 渲染时的取舍也很现实:

  • 32 位 alpha 里有任意非零值 → 用 alpha,做平滑混合
  • alpha 全是 0 → 当作不透明,退回 AND 掩码

两套系统并存,谁好用用谁。听起来挺周到——直到你自己写代码去解析它。

我到底踩进了什么坑

回到那个黑底图标。真相是:

我的解码器(无论是 WinUI 的 BitmapDecoder,还是浏览器里的 BMP 解码)都在老老实实遵守 BMP 规范——它们看到 BI_RGB + 32 位,就把第 4 字节当 padding 丢了。透明区域没了 alpha,剩下的颜色配上默认的不透明,就糊成了黑块。

它们没坏。是这格式在这件事上本身就不靠谱。

所以修法很反直觉:不是去「修好」ICO,也不是换个解码器——而是别再相信解码器。直接读那 4 个原始字节,自己把 BGRA 塞进画布;alpha 全零的时候,再退回去老老实实算 AND 掩码。

我后来在 ICO Unpacker 的网页版和桌面版里都是这么干的:绕开解码器,直接解析 DIB 的字节。这样无论那第 4 字节到底是 alpha 还是 padding,透明度都不会凭空消失。

ICO Unpacker 也提供免费的 Windows 桌面版——同样的逻辑,无需浏览器,完全离线。

所以到底该记住什么

  • 「32 位 BMP 有 alpha 通道」是一个全行业默契,不是规范定义的事实。
  • 在最经典的 BITMAPINFOHEADER + BI_RGB 下,第 4 字节是 padding;alpha 要到 BITMAPV4HEADER + BI_BITFIELDS 才名正言顺。
  • 一个工具「丢」了你的 BMP 透明度,未必是它的 bug——它可能只是在照规范办事。
  • ICO 有自己官方的透明度(AND 掩码),32 位 alpha 是后来的增强;两者并存,Windows 见机行事。
  • 真要保住透明度,就别绕路:直接读字节。

上一篇开发踩坑 #1 讲的是「在线转换工具会悄悄搞坏你的 BMP 帧」——那是别人把你的 alpha 写坏。这一篇讲的是另一面:就算 alpha 写对了,BMP 这格式本身也不保证有人认。


本文属于开发踩坑系列。