ISO/IEC_15948

**可攜式網路圖形**（英語：**P**ortable **N**etwork **G**raphics，**PNG**）是一種支援無失真壓縮的點陣圖圖形格式，支援索引、灰度、RGB 三種顏色方案以及 Alpha 通道等特性。PNG 的開發目標是改善並取代 GIF 作為適合網路傳輸的格式而不需專利許可，所以受廣泛應用於網際網路及其他方面上。

PNG 另一個非正式的名稱來源為遞迴縮寫：「**P**NG is **N**ot **G**IF」。PNG 的官方念法是「平」（英語 IPA：/pɪŋ/；「平」是以國語發音），但是多數人是當成三個英文字母分開讀。

PNG 圖片大多數都使用 `PNG` 作為副檔名，其網際網路媒體類型為 `image/png`。PNG 於 1997 年 3 月作為知識性 RFC 2083 發布，於 2004 年作為 ISO/IEC 標準發布。

## 特性

* 支援 256 色調色盤技術以產生小體積檔案
* 最高支援 24 位元真彩色圖像以及 8 位元灰度圖像。
* 支援 Alpha 通道的透明 / 半透明特性。
* 支援圖像亮度的 Gamma 校準資訊。
* 支援儲存附加文字資訊，以保留圖像名稱、作者、著作權、創作時間、注釋等資訊。
* 支援無失真壓縮。
* 漸近顯示和串流讀寫，適合在網路傳輸中快速顯示預覽效果後再展示全貌。
* 使用 CRC 防止檔案出錯。
* 最新的 PNG 標準允許在一個檔案記憶體儲多幅圖像。

## 版本及歷史

1995 年早期，Unisys 公司根據它在 GIF 格式中使用的 LZW 資料壓縮演算法的軟體專利（美國 第 4558302 號（頁面存檔備份，存於網際網路檔案館））開始商業收費。為避免專利影響，用於表現單張圖像的 PNG、用於表現動畫的 Multiple-image Network Graphics 圖形檔案格式同時建立出來。1999 年 8 月，Unisys 公司進一步中止了對自由軟體和非商用軟體開發者的 GIF 專利免費許可，從而使 PNG 格式獲得了更多的關注。

在 PNG 傳播過程中，很多網路瀏覽器經過很長時間才開始完全支援 PNG 格式；如 Microsoft Windows 預設的 Internet Explorer 瀏覽器一直到 7.0 版才支援 PNG 格式中的半透明效果，較早期的版本（如 6.0 SP1）需要下載 Hotfix 或由網站提供額外的 Script 去支援。這造成 PNG 格式並沒有得到廣泛的認知。

* PNG 的 1.0 版本規範於 1996 年 7 月 1 日發布，後來稱為 RFC 2083 標準，並在 1996 年 10 月 1 日成為 W3C 建議。
* PNG 的 1.1 版本進行了部分小幅修改並增加了三個新的資料塊定義，於 1998 年 12 月 31 日發布。
* PNG 的 1.2 版本增加了另外一個資料塊，於 1999 年 8 月 11 日發布。
* PNG 現行版本是國際標準（ISO／IEC 15948:2003），並在 2003 年 11 月 10 日作為 W3C 建議發布。這個版本與 1.2 版僅有細微差別。

此外也產生了基於 PNG 的動畫格式：1996 年 6 月提出 PNF（Portable Network Frame）草案，當年 8 月改名為 MNG（Multiple-image Network Graphics），但由於較為複雜，實現支援的軟體較少。2004 年末，PNG 的動畫擴充 ——APNG，受提出來。這是一個相對於 MNG 更簡單的動畫實現方案，不辨識 APNG 格式的 PNG 解碼器至少能夠正常回放第一幅普通 PNG 畫面，相反地得到大部分顯示或編輯的軟體支援。

## 檔案結構

### 儲存型別

PNG 圖片主要由三種型別儲存

* PNG 8: 圖片使用 8 bits 來儲存，可以用 2 的 8 次方大小個種類顏色來儲存一張黑白的圖片。也就是說 PNG 8 能儲存 256 種顏色，因為顏色少，檔案體積也非常小，一張圖片如果顏色簡單，將它設定成 PNG 8 的圖片是非常省空間合適的。
* PNG 24: 圖片使用 24bits 來儲存，用三個 8bits 分別去表示 R（紅）、G（綠）、B（藍）三個通道 (Channel) 的數值。可以表達 256 乘以 256 乘以 256=16777216 種顏色的圖片，色彩豐富度更高，但相對的所佔用的空間也就更大了。
* PNG 32: 圖片使用 32bits 來儲存，相當於 PNG 24 加上 8bits 的透明顏色通道，總共有 R（紅）、G（綠）、B（藍）、A（透明）四個通道。圖片能表示的色彩跟 PNG 24 一樣多，並且還支援 256 種透明度，能讓圖片色彩更加豐富。

### 檔案資料構成

PNG 圖像格式檔案由一個 8 位元組的 PNG 檔案標識（file signature or file header）域和 3 個以上的後續資料塊 (chunk) 如：IHDR、IDAT、IEND 等組成。

PNG 檔案包括 8 位元組檔案署名（89 50 4E 47 0D 0A 1A 0A，十六進位），用來辨識 PNG 格式，所有 PNG 圖片檔案內容開頭都會有這一串 header。

PNG 定義了兩種類型的資料塊：一種是 PNG 檔案必須包含、讀寫軟體也都必須要支援的關鍵塊（critical chunk）；另一種叫做輔助塊（ancillary chunks），PNG 允許軟體忽略它不認識的附加塊。這種基於資料塊的設計，允許 PNG 格式在擴充時仍能保持與舊版本相容。

#### 數據塊結構

PNG 檔案中，每個數據塊都由四個部分組成，如下:

關鍵資料塊中有 4 個標準資料塊：

* 檔案頭資料塊 IHDR（header chunk）：包含有圖像基本資訊，作為第一個資料塊出現並只出現一次。
* 調色盤資料塊 PLTE（palette chunk）：必須放在圖像資料塊之前。
* 圖像資料塊 IDAT（image data chunk）：儲存實際圖像資料。PNG 資料允許包含多個連續的圖像資料塊。
* 圖像結束資料 IEND（image trailer chunk）：放在檔案尾部，表示 PNG 資料流結束。

其餘輔助數據塊列表:

## 壓縮方式

PNG 圖片的壓縮主要分有兩個階段：

* 預解析（Prediction）
* 壓縮（Compression）

由於壓縮方式主要由以上兩個階段構成，優化方向也對應著兩個階段。針對預解析階段做優化主要的選擇是去優化差分編碼器，讓編碼出的結果能出現盡可能多的零值或是相同的值。針對壓縮階段的優化則是找出更好的 Deflate 演算法，以獲得更高的壓縮率。

### 預解析

在這階段中，圖片會做先做些預處理，以利後續壓縮。 每次會處理圖片中某一行（column）的數據，首先經過濾波器 (Filter) 來處理這些數據，每個像素點（Pixel）在不同通道中（Channel ex:RGB）的數值都會經過濾波器運算，然就得出的結果會交由差分處理器來重新計算通道中的數值。 差分處理器會將目前這個像素點上通道中的數值和之前或之上（EX：左邊上方）像素點做對比，根據差異進行差分編碼。若是相鄰的像素點在通道上的數值非常接近，就會得出很多 1,0,-1 這種很小的值。 整個 prediction 階段要做的事就是找到適合的差分編碼器，使得最終差分編碼的結果盡可能的都是重複很小或是零的值，這一階段的結果會影響到 Compression 階段中能實現的壓縮率。

### 壓縮

經過預解析階段之後，將編碼出的結果輸出給 Deflate（是一種無損數據壓縮演算法，同時使用了 LZ77 和 Huffman Coding 方法），由其執行真正的壓縮操作，在此階段會通過 LZ77 和 Huffman Coding 方法來對圖像進行編碼，最後將處理後的圖片結果儲存。最終的壓縮率主要受到兩方面的影響:

* 預解析階段（Prediction）的處理結果：對於 RGB 通道數值相近的像素區域，也就是編碼結果有很多零值的區域，在壓縮階段得當的壓縮率會特別高，若是相鄰區域的顏色差異大，則壓縮效果就會大打折扣。
* Deflate 每一行的匹配情形：在整個壓縮處理過程中都是以行（column）為單位來進行處理。而在處裡數據的過程中，處裡的符號數被限制在 3－258 之間，換言之，最高的壓縮率可以達到 1032:1，當出現的符號數小於 3 時，就可能會出現無法匹配的情況。因此圖片的寬度也會是一個可能影響最終壓縮效果的因素之一。

### 壓縮方案

PNG 壓縮提供兩種壓縮方案

* 無失真壓縮（Lossless Compression）：數據經過壓縮之後，資訊不受損失，能夠完全還原至壓縮前的模樣，通常此種壓縮方案的壓縮率有一定的限制。
* 有損壓縮（Lossy compression）：數據經過壓縮之後，資訊數據會和原始數據不同，但僅限於些微差距，通常此種壓縮方案能提供非常高的壓縮率。

#### 無失真壓縮方案

目前大部分 PNG 的壓縮採用基於 LZ77 衍生演算法，使得它壓縮比率更高，生成的檔案體積更小，並且不損失數據。

#### 有損壓縮方案

由於 PNG 原本的無失真壓縮方案在壓縮率上有一定的限制，有些開源者無法接受既有的壓縮率，因此基於 PNG 原本的壓縮方式開發新的有損壓縮方案，在工程上與得到了一定的應用。例如 TinyPNG, 根據官方公開的資訊，其利用了 Quantization 的技術來實現提高壓縮率，通過合併相似的顏色將 24 位元的 PNG 圖片壓縮成更小的 8 位元圖片。其中更著名的 PNG 有損壓縮演算法 **pngquant**，在 Github 上有完整的開原始碼，同樣也是使用了 Vector Quantization 來減少圖片中顏色的種類，在向量化過程中將像素點根據顏色相似程度進行分組，隨後組內的像素統計出一個中心顏色並將所有組內像素都替代為此中心顏色。向量化會造成圖片失真，如何在壓縮率與圖片品質之間取得一個恰到好處的平衡點是個值得優化的方向。

## 與其他格式相比

### 與 GIF 相比

* 一般情況下將靜態 GIF 圖像無失真轉換為 PNG 後可以壓縮率會略為提高（前提是同樣採用 8 位元索引模式）。
* PNG 可提供更大顏色深度的支援，包括 24 位元（8 位元 3 通道）和 48 位元（16 位元 3 通道）真彩色。加入 Alpha 通道後可進一步支援每像素 64 位元的表示。
* 超過 8 位元色深的 PNG 圖像轉換為 GIF 時，圖像品質會由於分色（顏色數減少）而下降。
* GIF 原生支援動態圖像，PNG 只能通過非標準實現，在 PNG 的基礎上另有發展出支援動畫的 APNG 和 MNG 格式，但目前普及度不高。

PNG 在 IE6 等舊代瀏覽器上的支援較差。

### 與 JPEG 相比

JPEG 可以對相片（或類似）圖像生成更小的檔案，這是由於 JPEG 採用了一種針對相片圖像的特定有損編碼方法，這種編碼適用於低對比，圖像顏色過渡平滑，雜訊多，且結構不規則的情況下。如果在這種情況下用 PNG 代替 JPEG，檔案尺寸增大很多，而圖像品質的提高有限。相應的，如果儲存文字，線條或類似的邊緣清晰，有大塊相同顏色區域的圖像，PNG 格式的壓縮效果就要比 JPEG 好很多，並且不會出現 JPEG 那樣的高對比度區域的圖像失真。如果圖像既有清晰邊緣，又有相片圖像的特點，就需要在這兩種格式之間權衡一下了。JPEG 不支援透明度。

由於 JPEG 是失真壓縮，會產生迭代失真，在重複壓縮和解碼的過程中會不斷遺失資訊使圖像品質下降。由於 PNG 是無失真的，儲存將要編輯的圖像來說更加合適。雖然 PNG 壓縮相片圖像也有效，但有專門針對相片圖像設計的無失真壓縮格式，例如無失真 JPEG2000 和 DNG。總的來說這些格式都不能做到適用所有圖像。對於將要發布的圖像可以儲存成 JPEG，用 JPEG 編碼一次不會造成明顯的圖像失真。

### 與 JPEG-LS 相比

JPEG-LS 是一個「幾乎」無失真壓縮格式，相對於上面提到的有損 JPEG 壓縮，它的知名度不高。它可以直接和 PNG 相比較，使用一組標準的測試圖像。在 Waterloo Repertoire ColorSet（一組標準測試圖像）下，JPEG-LS 通常表現要比 PNG 好 10%-15%，但其中有一些圖像 PNG 表現明顯更好一些，大約 50%-75%。所以，如果這兩種格式都支援而且對圖檔大小很敏感的話，可以用這兩種格式都試試，和圖像資料本身有比較大關係。

### 與 TIFF 相比

TIFF 是一個相當多方案結合的格式。它廣泛用作專業圖像編輯軟體之間圖像交換的中間格式，因此它不斷支援更多應用程式所需的功能，而對應用程式不關心的圖像操作部分支援不多。這也意味著許多應用程式只能辨識 TIFF 的一個子集，而產生更多的潛在混淆之處。

TIFF 使用的最通用的無失真壓縮演算法是 LZW。這種演算法 --GIF 中也在使用，直到 2003 年一直在專利保護之中。有一種 TIFF 變種使用與 PNG 相同的壓縮演算法，但是沒有獲許多專利程式所支援。TIFF 也提供了一種特殊的無失真壓縮演算法，類似 CCITT Group IV，可以對二值圖像（比如傳真或黑白文字）比 PNG 有更好的壓縮效果。

PNG 只支援非自左乘 α，而 TIFF 也支援聯合（自左乘）α。

PNG 規範中不包含嵌入式 EXIF（可交換圖檔格式）圖像資料的標準，比如數位相機拍得的圖像。而 TIFF，JPEG 2000, DNG 都支援 EXIF。

早期的瀏覽器不支援 PNG 圖像；JPEG 和 GIF 是主流圖像格式。由於 GIF 的顏色深度限制，網頁中的有顏色過渡的圖像都是使用 JPEG。不管怎樣，JPEG 壓縮都會導致圖像的輕微模糊。而 PNG 可以做到在相應顏色深度下的儘可能精確，同時保持圖檔不大。PNG 已經漸漸成為一種對於小的梯度圖像的較好的選擇，眾多瀏覽器都已經對 PNG 有了很好的支援。

## 參考文獻

## 外部連結

* PNG 檔案格式（頁面存檔備份，存於網際網路檔案館）
* PNG 規範第二版（頁面存檔備份，存於網際網路檔案館）（英文），2003 年 11 月 10 日 W3C 建議
* PNG 官方站（頁面存檔備份，存於網際網路檔案館）（英文）
* PNG 的故事（頁面存檔備份，存於網際網路檔案館）（英文）