用十六進位編輯器開啟一個 PNG 檔案。前八個位元組:
89 50 4E 47 0D 0A 1A 0A
這就是 PNG 檔案簽章。每個 PNG 解碼器都會檢查它。第一個位元組 0x89 被刻意設得很高——為了防止簡單的文字編輯器把它當成 ASCII 處理。50 4E 47 是 ASCII 裡的 "PNG"。0D 0A 是 DOS 換行字元,1A 是 DOS 的檔案結束標記,0A 是 Unix 換行字元。設計者在簽章裡埋入這些換行字元,是為了讓以文字模式進行 FTP 傳輸時檔案立即損毀,迫使用戶使用二進位模式。說到底,這只是開發者們被嚇出來的過度反應——他們剛親眼見到一種受專利保護的格式淪為法律武器。
PNG 如今無所不在。截圖、UI 素材、圖表、Logo,任何需要反覆存檔後像素仍舊一模一樣的影像,都由它驅動。它比 Google 還老,比 iPod 還老,至今仍是需要無失真壓縮時的預設選擇。但它最初壓根就不打算成為一種格式。最初只是個暫時的權宜之計,從沒想過要變成標準。
催生新格式的訴訟
1995 年 1 月,Unisys 宣布將對使用 GIF 格式的開發者強制執行 LZW 壓縮專利——美國專利 4,558,302。該專利涵蓋了 Lempel-Ziv-Welch 演算法,這是每個 GIF 編碼器和解碼器的核心壓縮方法。
1995 年的網際網路靠 GIF 運轉。動態橫幅、透明 Logo、平鋪背景——GIF 全包了。然後 Unisys 開始收授權費。商業軟體廠商面臨授權費用,開源專案面臨生存威脅。GNU Image Manipulation Program(GIMP)直接受到影響。Web 開發社群震怒。
必須有種東西能替代 GIF 處理靜態影像。需求很明確:沒有專利、壓縮率優於 GIF、支援全彩、擁有真正的 Alpha 通道而不是 GIF 那種粗糙的單色透明。它還必須足夠簡單,讓一個開發者用一個週末就能寫出解碼器。
1995 年 4 月 4 日,Thomas Boutell 在 comp.graphics Usenet 新聞群組發了一份提案。他稱之為 PBF——Portable Bitmap Format。這個名字沒叫開。接下來的幾個月裡,一個郵件清單逐漸形成。貢獻者包括 Tom Lane(Independent JPEG Group 負責人)、Lee Daniel Crocker、Alexander Lehmann,以及幾十位其他人。到 1996 年 10 月 1 日,PNG 規範以 RFC 2083 的形式凍結。從想法到標準,整個過程大約花了十八個月。作為對比,JPEG 花了六年。
PNG 出現之前的選擇
1995 年,你的影像格式選項很有限,而且每個都有包袱:
| 格式 | 壓縮 | 色深 | 透明 | 專利風險 | 典型用途 |
|---|---|---|---|---|---|
| GIF | LZW | 最多 256 色 | 1 位,1 種顏色 | 有(LZW) | Web 圖形、動畫 |
| JPEG | DCT + Huffman | 24 位全彩 | 無 | 基礎專利已過期 | 照片 |
| BMP | 無或 RLE | 最高 24 位 | 無 | 無 | Windows 桌布 |
| TIFF | LZW、PackBits 等 | 最高 48 位 | 有 | LZW 可選 | 印刷、掃描 |
| PCX | RLE | 最高 24 位 | 無 | 無 | DOS 遊戲、早期剪貼畫 |
GIF 統治了 Web,但在法律上是有毒的。它的 256 色調色盤對圖示和卡通來說夠用,對照片則完全不行。它的透明是二元的:一個像素要么完全不透明,要么完全透明。沒有柔和邊緣,沒有陰影。
JPEG 處理照片極為出色,但會不可逆地銷毀資料。開啟一張 JPEG,編輯,再儲存一次,影像就開始劣化。JPEG 也完全不支持透明。對於需要把 Logo 放在紋理背景上的 Web 設計師來說,JPEG 毫無用處。
BMP 和 PCX 要么不壓縮,要么幾乎不壓縮。1995 年一張 640 × 480 的 BMP 要吃掉 900 KB。在 28.8 kbps 的數據機上,下載需要四分多鐘。
TIFF 強大且靈活,但靈活性正是它的詛咒。TIFF 檔案可以使用十幾種不同的壓縮方案、色彩空間和位元深度。寫一個通用 TIFF 解碼器是論文等級的專案,不是週末就能 hack 出來的。
PNG 的設計目標很精準:取代 GIF 處理靜態影像、壓縮率超過 GIF、加入全彩和真正的透明、並且永遠免費。
PNG 如何實際壓縮
PNG 壓縮是一條兩階段管線。單獨看,每一步都沒什麼了不起。合在一起,效果卻出奇地好。
階段 1:濾波
在任何壓縮發生之前,PNG 先把原始像素資料送進一個濾波器。濾波器本身不壓縮任何內容。它只是重新排列資料,讓下一階段能壓得更好。
影像是一個位元組網格。在一張晴朗天空的照片裡,相鄰像素幾乎相同。但原始位元組流仍然為每個通道儲存 120, 121, 120, 122, 119。差值很小:+1, -1, +2, -3。如果你儲存差值而不是絕對值,結果數字會聚集在零附近。這正是壓縮演算法最拿手的。
PNG 為每條掃描線定義了五種濾波類型:
| 濾波類型 | 名稱 | 作用 |
|---|---|---|
| 0 | None | 儲存原始位元組 |
| 1 | Sub | 儲存與前一個像素的差值 |
| 2 | Up | 儲存與上方像素的差值 |
| 3 | Average | 儲存與 Sub 和 Up 平均值的差值 |
| 4 | Paeth | 儲存與最佳預測值(Sub/Up/對角線)的差值 |
編碼器對每條掃描線嘗試全部五種濾波,然後選擇在階段 2 之後輸出最小的那種。這就是為什麼 PNG 編碼器可能很慢:它在暴力搜尋濾波組合。但解碼很快——濾波類型存在檔案裡,解碼器只需應用逆運算。
階段 2:DEFLATE
濾波之後,資料用 DEFLATE 壓縮,gzip 和 ZIP 檔案用的也是同一種演算法。DEFLATE 是 LZ77(滑動視窗重複字串消除)和 Huffman 編碼(為頻繁出現的符號分配變長前綴碼)的組合。
結果:一張典型的截圖或 UI 圖形,可以壓到未壓縮 BMP 的 3–5 分之一。照片用 PNG 壓縮通常比 JPEG 大 5–10 倍,但每個像素都可恢復。壓縮從設計上就是無失真的:沒有資料被丟棄,只去除冗餘。
作為參考,一張 1920 × 1080 的原始 RGB 截圖是 6.2 MB。同一張截圖存成 PNG 通常會降到 800 KB – 1.5 MB。同一張影像用 JPEG 品質 90 是 300–500 KB,但重複儲存十次就會引入可見瑕疵。
真正重要的功能
PNG 不只是個沒有專利的 GIF 複製品。它增加了 Web 設計師從 1993 年起就一直想要的功能。
全彩:PNG 支援 24 位元 RGB(1670 萬色)和 48 位元深彩色。沒有調色盤限制。一張 PNG 照片可以顯示人眼能分辨的每一種顏色。
Alpha 通道:PNG 支援 8 位元 Alpha——每個像素 256 級透明。陰影可以從完全不透明平滑過渡到完全透明。圓角按鈕可以在任何背景上抗鋸齒。GIF 只提供 1 位元透明:一種顏色要么全開,要么全關。兩者完全是兩碼事。
Adam7 交錯:PNG 可以把像素按 7 輪交錯的順序儲存。瀏覽器在收到檔案的 1/64 後就能渲染出粗略預覽,然後逐步細化。與 GIF 的逐行交錯不同,Adam7 從第一輪就把細節分佈到整幅影像上。到第三輪,影像已經可辨認。到第七輪,完全精確。
伽瑪校正:PNG 在詮釋資料裡儲存伽瑪值。在 Mac(伽瑪 1.8)上建立的影像,在 Windows PC(伽瑪 2.2)上也能正確顯示,無需手動校色。這在 1990 年代跨平台一致性還很罕見時,是個實實在在的問題。
CRC 檢查碼:每個 PNG 資料塊都帶有 CRC-32 檢查碼。損毀的下載能被立即檢測出來,而不是渲染成半張影像。
透過讀取檔案簽章識別 PNG
別信 .png 副檔名。讀前八個位元組,檢查簽章。
精確的位元組布局:
Bytes 0–7: 簽章 89 50 4E 47 0D 0A 1A 0A
Bytes 8–11: 資料塊長度(大端 uint32)
Bytes 12–15: 資料塊類型:"IHDR"(影像標頭)
Bytes 16–19: 影像寬度(大端 uint32)
Bytes 20–23: 影像高度(大端 uint32)
Byte 24: 位元深度
Byte 25: 色彩類型
Byte 26: 壓縮方法(始終為 0)
Byte 27: 濾波方法(始終為 0)
Byte 28: 交錯方法(0 或 1)
瀏覽器裡的 TypeScript:
async function isPng(file: File): Promise<boolean> {
const buffer = await file.slice(0, 8).arrayBuffer()
const bytes = new Uint8Array(buffer)
const signature = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]
return bytes.length === 8 && bytes.every((b, i) => b === signature[i])
}
Python
def is_png(path: str) -> bool:
with open(path, "rb") as f:
header = f.read(8)
return header == b"\x89PNG\r\n\x1a\n"
用標準函式庫的更高階做法:
import imghdr
if imghdr.what("image.png") == "png":
pass
或者使用 Pillow:
from PIL import Image
try:
with Image.open("image.png") as img:
is_png = img.format == "PNG"
except Exception:
is_png = False
Go
func isPng(path string) bool {
f, err := os.Open(path)
if err != nil {
return false
}
defer f.Close()
buf := make([]byte, 8)
if _, err := f.Read(buf); err != nil {
return false
}
return bytes.Equal(buf, []byte{0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a})
}
PHP
function isPng(string $path): bool {
$header = file_get_contents($path, false, null, 0, 8);
return $header === "\x89PNG\r\n\x1a\n";
}
使用 fileinfo:
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file('image.png');
// image/png
ImageMagick CLI
magick identify -verbose image.png | grep "Format:"
# Format: PNG (Portable Network Graphics)
或者簡單一點:
file image.png
# image.png: PNG image data, 1200 x 675, 8-bit/color RGBA, non-interlaced
PNG 的局限
PNG 並不完美。它的設計選擇是權衡,有些直到今天仍在讓我們付出代價。
沒有內建動畫:PNG 工作群組明確拒絕了動畫。他們想在增加複雜性之前,先把靜態影像問題徹底解決。結果:動畫 GIF 又存活了二十年。APNG(Animated PNG)最終在 2004 年被標準化,但瀏覽器支援直到 2010 年代末才趨於完善。即使今天,動畫 GIF 的數量仍然以數量級優勢超過 APNG。
照片檔案體積過大:一張 1200 萬像素的照片存成 PNG 通常是 15–25 MB。用 JPEG 品質 90 則只有 3–5 MB。PNG 的無失真壓縮無法與基於 DCT 的心理視覺丟棄競爭。對於照片,PNG 是錯誤的工具。
不支持 CMYK:PNG 只支援 RGB。需要 CMYK 分色的印刷工作流程必須把 PNG 轉成 TIFF 或 JPEG。這是刻意為之——設計者聚焦於螢幕顯示,而非印刷——但它限制了 PNG 在專業出版中的實用性。
解碼比 JPEG 慢:PNG 解碼需要對每條掃描線進行逆濾波和 DEFLATE 解壓。JPEG 解碼則可以高度平行化,並且已被硬體深度最佳化。在行動裝置上,一張大 PNG 的渲染時間可能是同等解析度 JPEG 的 2–3 倍,拖慢 Largest Contentful Paint。
沒有漸進品質:與 JPEG 2000 或 JPEG XL 不同,PNG 無法從截斷的檔案生成低品質預覽。要么你有完整的檔案,要么什麼都沒有。Adam7 交錯有助於生成粗略預覽,但它不會減少總檔案大小。
為什麼 PNG 從未取代 JPEG
這是關於影像格式最常見的誤解。PNG 和 JPEG 從來不是在做同一份工作。
JPEG 是一種失真心理視覺壓縮器。它丟棄的是你的眼睛大概率不會注意到的資料。它為連續色調照片設計——那些具有平滑漸層、精細紋理和自然光照的影像。在這個領域,JPEG 在尺寸-品質曲線上三十三年後仍然無人能敵。
PNG 是一種無失真資料壓縮器。它保留每一位元。它為離散色調影像設計——截圖、UI 元素、圖表、Logo、文字疊加——其中銳利邊緣和精確顏色至關重要。在這個領域,PNG 是標準。
兩種格式各自劃分了地盤:
| 使用情境 | 正確格式 | 原因 |
|---|---|---|
| 照片 | JPEG/AVIF | 失真壓縮可以小 5–10 倍 |
| 截圖 | PNG/WebP | 無失真保留文字銳度 |
| 帶透明的 Logo | PNG/WebP | Alpha 通道 + 無失真邊緣 |
| UI 圖示 | PNG/SVG | 體積小,顏色精確匹配 |
| 科學資料視覺化 | PNG | 漸層圖例不產生瑕疵 |
| 印刷就緒影像 | TIFF/JPEG XL | 支援 CMYK、高位元深度 |
PNG 從來沒打算取代 JPEG。它只是找到了不同的用武之地。
PNG 今天的位置
2025 年 Web Almanac 的資料顯示,PNG 約占 Web 上所有服務影像的 22%。這比峰值有所下降——WebP 和 AVIF 正在蠶食份額——但 PNG 仍然是所有瀏覽器、影像編輯器、作業系統都能直接開啟的備用格式。
WebP(Google,2010)同時支援失真和無失真模式,外加動畫和透明。無失真 WebP 通常比同等 PNG 小 20–30%。瀏覽器支援自 2020 年起已全面普及。對於新專案,WebP 無失真在大多數情況下是 PNG 的務實替代。
AVIF(AOM,2019)實現了更優的壓縮率,但其無失真模式的編碼和解碼速度都比 PNG 慢。AVIF 也缺少對某些進階 PNG 功能的完整瀏覽器支援,比如 16 位元通道和嵌入式伽瑪校正。
SVG 在簡單的向量圖形領域統治了圖示和 Logo,但點陣化的複雜圖形仍然需要 PNG。
現實是:PNG 不會消失。它是安全預設值,是你匯出檔案時用來保證收件人能開啟它的格式。它是影像格式的 QWERTY 鍵盤——不是最優的,但人人都會用。
PNG 的未來
PNG 是一個已完成的標準。規範自 2003 年以來沒有實質性變化。這種穩定性是特性,不是缺陷。你在 1997 年建立的 PNG,在任何現代瀏覽器裡開啟,渲染結果完全一致。
但 PNG 周圍的生態系統仍在演進:
APNG 正在逐步普及。Safari 從 2014 年起就支援它。Chrome 和 Firefox 隨後跟上。Discord、Slack 和 Twitter 都原生渲染 APNG。對於短的 UI 動畫——載入轉圈、反應表情、狀態指示器——APNG 正以更小的檔案和更好的色彩保真度取代動畫 GIF。
PNG 最佳化工具 持續進步。oxipng、pngcrush 和 zopfli 可以透過暴力搜尋更好的濾波組合和 DEFLATE 參數,把 PNG 檔案再削減 10–30%。對於高流量網站,把每張 PNG 過一遍 oxipng 已是標準做法。
PNG 作為容器:一些現代工作流程把 ICC 色彩設定檔、EXIF 詮釋資料甚至 XMP 資料嵌入 PNG 資料塊。PNG 已經成為一種輕量級封存格式——不如 TIFF 豐富,但可攜性強得多。
長期展望:PNG 將與 WebP、AVIF 和 JPEG XL 共存多年。它占據了一個沒有其他格式能完全覆蓋的細分領域:無失真、無專利、所有地方都支援、簡單到足以讓一個開發者在一週內根據規範寫出解碼器。這種組合很難被取代。
結語
PNG 之所以誕生,是因為開發者們受夠了。一場專利訴訟威脅到了開放網路,一群開發者在業餘時間造出了替代品。他們本來也沒想建立一個能存續三十年的標準。他們只是想解決一個問題:如何在不付授權費的情況下,把一張透明 Logo 放到網頁上。
結果比任何人預料的都還要好。PNG 給了 Web 全彩圖形、平滑透明和抗損毀的檔案完整性。它證明了志願者建立的開放標準,能夠擊敗有大企業撐腰的專有格式。
它不是最小的格式。它不是解碼最快的。它做不好動畫,對照片也束手無策。但當你需要確保存進去的每一個像素,日後取出來還是原樣——你依然會選擇 PNG。
不是所有圖片生來就是 PNG。如果你手邊的 JPG 需要透明通道或無失真編輯,直接在瀏覽器裡就能轉換——不用裝任何軟體,資料也不會離開你的裝置。JPG 轉 PNG在本地完成這件事。做 Web 專案時如果檔案大小讓人頭痛,JPG 轉 WebP能在不犧牲畫質的情況下把檔案變小。而當你需要網站圖示(favicon)時,JPG 轉 ICO能把照片變成多種尺寸的 ICO 檔案。


