開發踩坑 #2:我一直以為 32 位元 BMP 有 Alpha 通道
2026-06-23
我有一個信了好多年的常識:32 位元 BMP,每像素 4 位元組,BGRA,第 4 個位元組就是 Alpha。 PNG 是 RGBA,BMP 不過是位元組順序反過來——清楚、乾淨、天經地義。
直到我做一個 ICO 工具,透明度以一種完全說不通的方式壞掉,我才翻開 BMP 規範——發現自己信了個謊。
一個所有人都「知道」的事實
故事的開頭非常正常。我想要一個能拆開 ICO、預覽每一影格、並保住透明度的工具。邏輯簡單得不能再簡單:
- 讀出 ICO 裡某一影格的 BMP 資料
- 解碼成點陣圖
- 顯示出來
第一影格載入出來:顏色對,形狀對,透明的地方全變成了黑色實心塊。
我的第一反應是——這 ICO 檔案壞了。換一個,還是黑。再換一個,還是黑。
不對勁。這些圖示在 Windows 檔案總管裡明明透明得好好的。
規範是怎麼說的
我去翻了 BMP 規範。經典的 BITMAPINFOHEADER 是 40 位元組,biBitCount = 32 表示每像素 32 位元。然後我看到那關鍵的一句——
當 biCompression 為 BI_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 這格式本身也不保證有人認。
本文屬於開發踩坑系列。