ByteScope

BMP 檔案格式

Windows Bitmap,一種未壓縮(或輕度壓縮)的點陣圖格式。

MIME 類型
image/bmp
副檔名
.bmp
分類
圖片

Magic number(檔案簽章)

0x00000000424DBM'BM' header

關於 BMP 格式

BMP 幾乎是原封不動地存放像素,所以檔案很大,但讀寫極為簡單,在 Windows 上至今仍常作為中間格式。2 位元組的標頭 'BM' 之後接著檔案大小與像素陣列的位移,再來是第二份標頭 — DIB 標頭 — 而它自己的第一個欄位就是自身長度,同一種格式就這樣悄悄涵蓋了橫跨三十年的六種標頭版本。除非高度是負數,否則列是由下往上存的,而且每一列都會補齊到 4 位元組的倍數。第一次寫 BMP 讀取器會遇到的困惑,多半都來自這兩條規則。

檔頭結構,一個位元組一個位元組看

位移長度欄位意義
02Signature42 4D,ASCII 的 BM 兩個字母。以現在的標準來說兩個位元組的 magic number 太短了,只看到這裡就收工的偵測器,會很開心地把任何以 BMP 開頭的文字檔、或是叫 Bmw 的名字認成點陣圖;緊接在後的大小欄位就是慣用的合理性檢查。
24File size檔案總大小(位元組),小端序,含兩份標頭。跟幾乎所有其他格式的大小欄位不同,這個欄位照規定要精確,所以拿它跟磁碟上的實際大小比對,是花四個位元組就能做的完整性檢查。
64Reserved兩個 16 位元欄位,規定都要是零。有些編輯器把它們拿來當自己的暫存空間,而讀取端一律無視,所以這裡不是零,比較常代表是誰寫的這個檔案,而不是檔案壞了。
104Offset to the pixel array真正的像素從哪個位元組位置開始,小端序,從檔案開頭算起。單純的 24 位元影像配上經典的 40 位元組 DIB 標頭,這裡就是 54:十四加四十,中間沒有色盤。值更大代表中間那段塞了色盤、色彩遮罩,或內嵌的 ICC 描述檔。
144DIB header size第二份標頭的長度,實務上就是這個格式的版本號:12 是遠古的 OS/2 BITMAPCOREHEADER,40 是 BITMAPINFOHEADER、也是你幾乎到處都會看到的那個,52 和 56 加上色彩遮罩,108 是帶色彩空間欄位的 BITMAPV4HEADER,124 是帶 ICC 描述檔的 BITMAPV5HEADER。
184Width有號小端序 32 位元的像素寬度。雖然是有號的,但負的寬度並不合法 — 只有高度的正負號帶有意義。12 位元組的 OS/2 標頭改用 16 位元存寬高,所以那裡不適用這些位移。
224Height有號小端序 32 位元的像素高度,也是這個格式裡最令人意外的欄位:正值代表列是由下往上存的,影像的最後一列排第一;負值代表由上往下,絕對值才是真正的高度。Windows 的螢幕擷取常常在這裡寫負數。
262Colour planes必須是 1。這是平面式視訊記憶體年代留下來的倖存者,沒有任何讀取端會接受別的值,所以它剛好可以當 BM 簽章之後第二道好用的合理性檢查。
282Bits per pixel1、4 或 8 搭配色盤,16 或 32 搭配色彩遮罩,24 是單純的 BGR 三元組,而 0 只出現在一種特例:壓縮欄位宣告酬載其實是一整個內嵌的 JPEG 或 PNG。注意通道順序:BMP 存的是藍、綠、紅,跟這個站上其他所有東西剛好相反。
304Compression method0 是 BI_RGB,代表完全不壓縮。1 和 2 是 8 位元與 4 位元影像的 RLE 行程長度編碼,3 是帶明確通道遮罩的 BI_BITFIELDS,4 和 5 則宣告像素陣列其實是一整個 JPEG 或 PNG 檔案 — 一個很少用到的逃生口,把 BMP 變成了容器。
344Image data size像素陣列的位元組數;當壓縮方式是 BI_RGB 時,這裡合法地可以是零,因為讀取端自己算得出來。而那個計算正是補齊規則咬人的地方:每一列佔的位元組是寬度乘以每像素位元數再除以八,然後往上進位到 4 的倍數。
388Horizontal and vertical resolution兩個有號 32 位元值,單位是每公尺像素數、不是每英吋,所以 96 dpi 存成 3780。多數檔案這兩個都是零,而這也是 BMP 唯一記錄實體尺寸的地方。
468Palette entry counts色盤實際上有幾種顏色,接著是其中有幾種對顯示有意義。第一個欄位是零,代表完整的 2 的位元深度次方色盤都在,所以一張 256 色的 BMP 就算這裡寫零,標頭和像素之間仍然躺著 1024 個位元組的色盤。

怎麼跟長得像的格式分開

容易搞混的怎麼分
ICOICO 目錄項目裡的影像,很多時候就是一張把十四位元組檔案標頭拿掉的 BMP,所以它從 DIB 標頭開始 — 前面沒有 BM,一上來就是 28 00 00 00。更麻煩的是它的高度欄位存的是真實值的兩倍,因為像素陣列後面還跟著一份同尺寸的 1 位元 AND 遮罩。把那段酬載從 ICO 裡挖出來直接存成 .bmp,得到的會是一張被拉成兩倍高、下半部全是垃圾的圖。
Clipboard DIBWindows 的 CF_DIB 剪貼簿資料同樣是一張從位移 14 開始的 BMP,沒有 BM 簽章也沒有檔案大小。任何把剪貼簿位元組直接寫進磁碟的工具,產出的檔案前四個位元組就是 28 00 00 00;在前面補上一份十四位元組、像素位移填對的 BITMAPFILEHEADER,它就變回一張正常的 .bmp。
TIFF兩者都用在未壓縮的典藏與掃描器輸出,多位元組數字也都是小端序,但標頭兩個位元組就分完了:BMP 是 42 4D,TIFF 是 49 49 或 4D 4D。實務上的差別是 TIFF 裝得下多頁、任意中介資料和多種壓縮方式,而 BMP 就只裝一張影像,而且幾乎一律完全不壓縮。
PNG89 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 搞混的格式,可以比對它們的開頭位元組。

格式開頭位元組副檔名
PNG89 50 4E 47 0D 0A.png
TIFF49 49 2A 00.tif
ICO00 00 01 00.ico

常見問題

檔案的 magic number 是什麼?

檔案開頭的前幾個位元組,不管檔名寫什麼,它標明的是真正的格式。PNG 一定以 89 50 4E 47 開頭,ZIP 則是 50 4B 03 04。作業系統、瀏覽器和 file 指令讀的都是這幾個位元組,而不是相信副檔名 — 這就是為什麼把檔案改名並不會改變它實際上是什麼。

我要怎麼自己檢查 magic number?

在 macOS 或 Linux 上,xxd -l 16 檔名 會印出前 16 個位元組;在 Windows 上,PowerShell 的 Format-Hex -Path 檔名 -Count 16。或者把檔案丟進本站的檔案類型辨識工具,它會在本機讀取位元組並告訴你是什麼格式,不會上傳任何東西。

為什麼兩種格式的 magic number 會一樣?

因為其中一種是建立在另一種之上。docx、xlsx、pptx、apk、jar 與 epub 骨子裡都是 ZIP 壓縮檔,所以全都以 ZIP 的簽章開頭;要分辨它們得看裡面的項目。Java class 檔和 macOS 的通用二進位檔都以 CA FE BA BE 開頭,靠緊接其後的欄位區分。

有完全沒有 magic number 的檔案嗎?

有。XML、HTML、JSON、SVG、CSV 這類以文字為基礎的格式沒有固定的位元組簽章,所以改用結構來辨識:開頭的 <?xml 宣告、<svg 元素,或是 { 後面接著帶引號的鍵。這種偵測是合理推測而非確定,因此本參考頁會把這些格式標示為結構偵測。

magic number 相符就代表檔案安全嗎?

不代表。它告訴你的是格式,不是內容是否無害 — 一個貨真價實的 PDF 或 docx 仍然可能夾帶惡意內容。辨識真實類型是有用的第一道檢查,尤其在副檔名和位元組不一致的時候,但它不是惡意軟體掃描。

手上有檔案可能是 BMP?用檔案類型辨識工具查出來 — 它在本機讀取 magic number,不上傳任何東西。或者瀏覽所有格式。