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 這格式本身也不保證有人認。


本文屬於開發踩坑系列。