BMP 檔案格式
Windows Bitmap,一種未壓縮(或輕度壓縮)的點陣圖格式。
- MIME 類型
- image/bmp
- 副檔名
- .bmp
- 分類
- 圖片
Magic number(檔案簽章)
| 0x00000000 | 424D | BM | 'BM' header |
關於 BMP 格式
BMP 幾乎是原封不動地存放像素,所以檔案很大,但讀寫極為簡單,在 Windows 上至今仍常作為中間格式。2 位元組的標頭 'BM' 之後接著檔案大小與像素陣列的位移,再來是第二份標頭 — DIB 標頭 — 而它自己的第一個欄位就是自身長度,同一種格式就這樣悄悄涵蓋了橫跨三十年的六種標頭版本。除非高度是負數,否則列是由下往上存的,而且每一列都會補齊到 4 位元組的倍數。第一次寫 BMP 讀取器會遇到的困惑,多半都來自這兩條規則。
檔頭結構,一個位元組一個位元組看
| 位移 | 長度 | 欄位 | 意義 |
|---|---|---|---|
| 0 | 2 | Signature | 42 4D,ASCII 的 BM 兩個字母。以現在的標準來說兩個位元組的 magic number 太短了,只看到這裡就收工的偵測器,會很開心地把任何以 BMP 開頭的文字檔、或是叫 Bmw 的名字認成點陣圖;緊接在後的大小欄位就是慣用的合理性檢查。 |
| 2 | 4 | File size | 檔案總大小(位元組),小端序,含兩份標頭。跟幾乎所有其他格式的大小欄位不同,這個欄位照規定要精確,所以拿它跟磁碟上的實際大小比對,是花四個位元組就能做的完整性檢查。 |
| 6 | 4 | Reserved | 兩個 16 位元欄位,規定都要是零。有些編輯器把它們拿來當自己的暫存空間,而讀取端一律無視,所以這裡不是零,比較常代表是誰寫的這個檔案,而不是檔案壞了。 |
| 10 | 4 | Offset to the pixel array | 真正的像素從哪個位元組位置開始,小端序,從檔案開頭算起。單純的 24 位元影像配上經典的 40 位元組 DIB 標頭,這裡就是 54:十四加四十,中間沒有色盤。值更大代表中間那段塞了色盤、色彩遮罩,或內嵌的 ICC 描述檔。 |
| 14 | 4 | DIB header size | 第二份標頭的長度,實務上就是這個格式的版本號:12 是遠古的 OS/2 BITMAPCOREHEADER,40 是 BITMAPINFOHEADER、也是你幾乎到處都會看到的那個,52 和 56 加上色彩遮罩,108 是帶色彩空間欄位的 BITMAPV4HEADER,124 是帶 ICC 描述檔的 BITMAPV5HEADER。 |
| 18 | 4 | Width | 有號小端序 32 位元的像素寬度。雖然是有號的,但負的寬度並不合法 — 只有高度的正負號帶有意義。12 位元組的 OS/2 標頭改用 16 位元存寬高,所以那裡不適用這些位移。 |
| 22 | 4 | Height | 有號小端序 32 位元的像素高度,也是這個格式裡最令人意外的欄位:正值代表列是由下往上存的,影像的最後一列排第一;負值代表由上往下,絕對值才是真正的高度。Windows 的螢幕擷取常常在這裡寫負數。 |
| 26 | 2 | Colour planes | 必須是 1。這是平面式視訊記憶體年代留下來的倖存者,沒有任何讀取端會接受別的值,所以它剛好可以當 BM 簽章之後第二道好用的合理性檢查。 |
| 28 | 2 | Bits per pixel | 1、4 或 8 搭配色盤,16 或 32 搭配色彩遮罩,24 是單純的 BGR 三元組,而 0 只出現在一種特例:壓縮欄位宣告酬載其實是一整個內嵌的 JPEG 或 PNG。注意通道順序:BMP 存的是藍、綠、紅,跟這個站上其他所有東西剛好相反。 |
| 30 | 4 | Compression method | 0 是 BI_RGB,代表完全不壓縮。1 和 2 是 8 位元與 4 位元影像的 RLE 行程長度編碼,3 是帶明確通道遮罩的 BI_BITFIELDS,4 和 5 則宣告像素陣列其實是一整個 JPEG 或 PNG 檔案 — 一個很少用到的逃生口,把 BMP 變成了容器。 |
| 34 | 4 | Image data size | 像素陣列的位元組數;當壓縮方式是 BI_RGB 時,這裡合法地可以是零,因為讀取端自己算得出來。而那個計算正是補齊規則咬人的地方:每一列佔的位元組是寬度乘以每像素位元數再除以八,然後往上進位到 4 的倍數。 |
| 38 | 8 | Horizontal and vertical resolution | 兩個有號 32 位元值,單位是每公尺像素數、不是每英吋,所以 96 dpi 存成 3780。多數檔案這兩個都是零,而這也是 BMP 唯一記錄實體尺寸的地方。 |
| 46 | 8 | Palette entry counts | 色盤實際上有幾種顏色,接著是其中有幾種對顯示有意義。第一個欄位是零,代表完整的 2 的位元深度次方色盤都在,所以一張 256 色的 BMP 就算這裡寫零,標頭和像素之間仍然躺著 1024 個位元組的色盤。 |
怎麼跟長得像的格式分開
| 容易搞混的 | 怎麼分 |
|---|---|
| ICO | ICO 目錄項目裡的影像,很多時候就是一張把十四位元組檔案標頭拿掉的 BMP,所以它從 DIB 標頭開始 — 前面沒有 BM,一上來就是 28 00 00 00。更麻煩的是它的高度欄位存的是真實值的兩倍,因為像素陣列後面還跟著一份同尺寸的 1 位元 AND 遮罩。把那段酬載從 ICO 裡挖出來直接存成 .bmp,得到的會是一張被拉成兩倍高、下半部全是垃圾的圖。 |
| Clipboard DIB | Windows 的 CF_DIB 剪貼簿資料同樣是一張從位移 14 開始的 BMP,沒有 BM 簽章也沒有檔案大小。任何把剪貼簿位元組直接寫進磁碟的工具,產出的檔案前四個位元組就是 28 00 00 00;在前面補上一份十四位元組、像素位移填對的 BITMAPFILEHEADER,它就變回一張正常的 .bmp。 |
| TIFF | 兩者都用在未壓縮的典藏與掃描器輸出,多位元組數字也都是小端序,但標頭兩個位元組就分完了:BMP 是 42 4D,TIFF 是 49 49 或 4D 4D。實務上的差別是 TIFF 裝得下多頁、任意中介資料和多種壓縮方式,而 BMP 就只裝一張影像,而且幾乎一律完全不壓縮。 |
| PNG | 89 50 4E 47 對上 42 4D。它們瞄準同一種用途 — 可帶 alpha 通道的無損點陣圖 — 而 PNG 在大小上大約贏一個數量級。BMP 的反駁是:你可以在一個下午之內、不依賴任何函式庫,寫出一個正確的解碼器,所以韌體啟動畫面和嵌入式 UI 才一直在用它。 |
常見的壞法
- 忘了補齊列。每一列都必須補到 4 位元組的倍數,所以一張 63 像素寬的 24 位元影像,每列要多一個位元組。寫入端漏掉它,產出的檔案每一列就往旁邊漂一個位元組,圖被剪成一條斜的 — 這是 BMP 最容易一眼認出來的錯誤。
- 像素陣列位移寫錯。位移 10 那四個位元組是唯一指向影像資料的東西,工具插入色盤或 ICC 描述檔卻沒更新它,解碼器就會把一部分標頭當成像素讀。結果是影像底部一條彩色雜訊,上面的所有東西整個位移。
- 高度正負號搞混。把有號高度當成無號來讀的程式,會把一張由上往下的擷取圖算成四十幾億高然後拒收;完全無視正負號的則會把圖畫成上下顛倒。兩種失敗都出自位移 22 那四個完全合法的位元組。
- 截斷。因為列通常是由下往上存的,被截短的 BMP 掉的是圖的上半部而不是下半部 — 跟這個站上其他每一種格式都相反。位移 2 的大小會跟磁碟上的檔案對不起來,用它來確認最快。
- 通道順序被交換。BMP 存的是藍、綠、紅,轉檔工具若假設是紅、綠、藍,做出來的圖天空會是橘的、膚色會是藍的。檔案裡沒有任何地方不合法,所以不會有驗證器報這個問題。
這個格式現在還在用嗎
BMP 是 Windows 的原生格式,從來沒有出門的理由,也真的沒出過門。它在網路上毫無位置 — 同一張圖存成 PNG 通常小十倍,畫質還一模一樣 — 也沒有相機或手機會吐出它。讓它活下來的是兩種「不壓縮正是重點」的場合。第一種是 Windows 自己:剪貼簿的影像格式就是 DIB,螢幕擷取和掃描器驅動程式至今仍然交出點陣圖,檔案總管的縮圖和 .exe 的資源也建立在同一套結構上。第二種是任何「養不起解碼器」的地方:開機載入程式的啟動畫面、微控制器的顯示器、印表機韌體和測試治具都讀 BMP,因為像素陣列就擺在那裡,位移已知、順序已知,不必實作任何熵編碼器。拿它當影像流程裡的中間格式也完全合理,畢竟寫一張是瞬間的事,而且確實無損。唯一不該讓它當的,是最終送到使用者手上的格式。
在這個站上打開 BMP 檔
這些全部在你的瀏覽器裡跑,檔案不會離開你的電腦。
相關格式
容易和 BMP 搞混的格式,可以比對它們的開頭位元組。