PNG 文件格式解析
PNG 圖像格式文件由一個 8 字節(jié)的 PNG 文件署名域和 3 個以上的后續(xù)數(shù)據(jù)塊(IHDR、IDAT卸例、IEND)組成逸雹。
PNG 文件包括 8 字節(jié)文件署名(89 50 4E 47 0D 0A 1A 0A,十六進(jìn)制)掺炭,用來識別 PNG 格式。
用十六進(jìn)制查看器打開任意一個 PNG 文件凭戴,都是可以看到這樣的頭部:
PNG 定義了兩種類型的數(shù)據(jù)塊:一種是 PNG 文件必須包含涧狮、讀寫軟件也都必須要支持的關(guān)鍵塊(critical chunk);另一種叫做輔助塊(ancillary chunks)么夫,PNG 允許軟件忽略它不認(rèn)識的附加塊者冤。這種基于數(shù)據(jù)塊的設(shè)計,允許 PNG 格式在擴(kuò)展時仍能保持與舊版本兼容档痪。
數(shù)據(jù)塊總覽
下表就是 PNG 中數(shù)據(jù)塊的類別涉枫,關(guān)鍵數(shù)據(jù)塊部分突出顯示以區(qū)分:
數(shù)據(jù)塊符號 | 數(shù)據(jù)塊名稱 | 多數(shù)據(jù)塊 | 是否可選 | 位置限制 |
---|---|---|---|---|
IHDR |
文件頭數(shù)據(jù)塊 |
否 |
否 |
第一塊 |
cHRM | 基色和白色點數(shù)據(jù)塊 | 否 | 是 | 在PLTE和IDAT之前 |
gAMA | 圖像γ數(shù)據(jù)塊 | 否 | 是 | 在PLTE和IDAT之前 |
sBIT | 樣本有效位數(shù)據(jù)塊 | 否 | 是 | 在PLTE和IDAT之前 |
PLTE |
調(diào)色板數(shù)據(jù)塊 |
否 |
是 |
在IDAT之前 |
bKGD | 背景顏色數(shù)據(jù)塊 | 否 | 是 | 在PLTE之后IDAT之前 |
hIST | 圖像直方圖數(shù)據(jù)塊 | 否 | 是 | 在PLTE之后IDAT之前 |
tRNS | 圖像透明數(shù)據(jù)塊 | 否 | 是 | 在PLTE之后IDAT之前 |
oFFs | (專用公共數(shù)據(jù)塊) | 否 | 是 | 在IDAT之前 |
pHYs | 物理像素尺寸數(shù)據(jù)塊 | 否 | 是 | 在IDAT之前 |
sCAL | (專用公共數(shù)據(jù)塊) | 否 | 是 | 在IDAT之前 |
IDAT |
圖像數(shù)據(jù)塊 |
是 |
否 |
與其他IDAT連續(xù) |
tIME | 圖像最后修改時間數(shù)據(jù)塊 | 否 | 是 | 無限制 |
tEXt | 文本信息數(shù)據(jù)塊 | 是 | 是 | 無限制 |
zTXt | 壓縮文本數(shù)據(jù)塊 | 是 | 是 | 無限制 |
fRAc | (專用公共數(shù)據(jù)塊) | 是 | 是 | 無限制 |
gIFg | (專用公共數(shù)據(jù)塊) | 是 | 是 | 無限制 |
gIFt | (專用公共數(shù)據(jù)塊) | 是 | 是 | 無限制 |
gIFx | (專用公共數(shù)據(jù)塊) | 是 | 是 | 無限制 |
IEND |
圖像結(jié)束數(shù)據(jù) |
否 |
否 |
最后一個數(shù)據(jù)塊 |
我們目前只需關(guān)注關(guān)鍵數(shù)據(jù)塊即可。
數(shù)據(jù)塊中有 4 個關(guān)鍵數(shù)據(jù)塊:
- 文件頭數(shù)據(jù)塊 IHDR(header chunk):包含有圖像基本信息腐螟,作為第一個數(shù)據(jù)塊出現(xiàn)并只出現(xiàn)一次愿汰。
- 調(diào)色板數(shù)據(jù)塊 PLTE(palette chunk):必須放在圖像數(shù)據(jù)塊之前困后。
- 圖像數(shù)據(jù)塊 IDAT(image data chunk):存儲實際圖像數(shù)據(jù)。PNG 數(shù)據(jù)允許包含多個連續(xù)的圖像數(shù)據(jù)塊衬廷。
- 圖像結(jié)束數(shù)據(jù) IEND(image trailer chunk):放在文件尾部摇予,表示 PNG 數(shù)據(jù)流結(jié)束。
數(shù)據(jù)塊連起來吗跋,大概這個樣子:
PNG 標(biāo)識符 | PNG 數(shù)據(jù)塊(IHDR) | PNG 數(shù)據(jù)塊(其他類型數(shù)據(jù)塊) | … | PNG 結(jié)尾數(shù)據(jù)塊(IEND) |
---|
數(shù)據(jù)塊結(jié)構(gòu)
PNG 文件中侧戴,每個數(shù)據(jù)塊(比如IHDR,IDAT等)由4個部分組成:
名稱 | 字節(jié)數(shù) | 說明 |
---|---|---|
Length (長度) | 4 字節(jié) | 指定數(shù)據(jù)塊中數(shù)據(jù)域的長度跌宛,其長度不超過(2^31-1)字節(jié) |
Chunk Type Code (數(shù)據(jù)塊類型碼) | 4 字節(jié) | 數(shù)據(jù)塊類型碼由 ASCII 字母(A-Z和a-z)組成 |
Chunk Data (數(shù)據(jù)塊數(shù)據(jù)) | 可變長度 | 存儲按照 Chunk Type Code 指定的數(shù)據(jù) |
CRC (循環(huán)冗余檢測) | 4 字節(jié) | 存儲用來檢測是否有錯誤的循環(huán)冗余碼 |
- CRC(cyclic redundancy check) 域中的值是對 Chunk Type Code 域和 Chunk Data 域中的數(shù)據(jù)進(jìn)行計算得到的酗宋。
- 注意:Length 值的是除:length 本身,Chunk Type Code疆拘,CRC 外的長度本缠,也就是 Chunk Data 的長度。
數(shù)據(jù)塊-文件頭數(shù)據(jù)塊 IHDR
它包含 PNG 文件中存儲的圖像數(shù)據(jù)的基本信息入问,并要作為第一個數(shù)據(jù)塊出現(xiàn)在 PNG 數(shù)據(jù)流中丹锹,而且一個 PNG 數(shù)據(jù)流中只能有一個文件頭數(shù)據(jù)塊。
文件頭數(shù)據(jù)塊由 13 字節(jié)組成:
域的名稱 | 字節(jié)數(shù) | 說明 |
---|---|---|
Width | 4 bytes | 圖像寬度芬失,以像素為單位 |
Height | 4 bytes | 圖像高度楣黍,以像素為單位 |
Bit depth | 1 byte | 圖像深度: 索引彩色圖像:1,2棱烂,4或8 灰度圖像:1租漂,2,4颊糜,8或16 真彩色圖像:8或16
|
ColorType | 1 byte | 顏色類型:0:灰度圖像, 1哩治,2,4衬鱼,8或16 2:真彩色圖像业筏,8或16 3:索引彩色圖像,1鸟赫,2蒜胖,4或8 4:帶α通道數(shù)據(jù)的灰度圖像,8或16 6:帶α通道數(shù)據(jù)的真彩色圖像抛蚤,8或16
|
Compression method | 1 byte | PNG Spec 規(guī)定此處總為 0台谢,表示使用壓縮方法(LZ77派生算法) |
Filter method | 1 byte | PNG Spec 規(guī)定此處總為 0,濾波器方法 |
Interlace method | 1 byte | 隔行掃描方法:0:非隔行掃描 1: Adam7(由Adam M. Costello開發(fā)的7遍隔行掃描方法)
|
用十六進(jìn)制查看器打開一個 PNG 文件:
十六進(jìn)制 | 說明 |
---|---|
00 00 00 0D | 數(shù)據(jù)塊長度 13 字節(jié) |
49 48 44 52 | 數(shù)據(jù)塊類型碼 “IHDR” 的 ASCII 字母 |
00 00 04 1D | 圖像寬度 1053 |
00 00 02 B3 | 圖像高度 691 |
08 | 圖像深度 8 |
06 | 帶α通道數(shù)據(jù)的真彩色圖 |
00 | 壓縮方法 |
00 | 濾波器方法 |
00 | 隔行掃描方法:00非隔行掃描 |
52 C3 75 3A | CRC (循環(huán)冗余檢測) |
數(shù)據(jù)塊-調(diào)色板數(shù)據(jù)塊 PLTE
包含有與索引彩色圖像(indexed-color image)相關(guān)的彩色變換數(shù)據(jù)岁经,它僅與索引彩色圖像有關(guān)朋沮,而且要放在圖像數(shù)據(jù)塊(image data chunk)之前。
PLTE 數(shù)據(jù)塊是定義圖像的調(diào)色板信息缀壤,PLTE 可以包含 1~256 個調(diào)色板信息樊拓,每一個調(diào)色板信息由 3 個字節(jié)組成:
顏色 | 字節(jié) | 意義 |
---|---|---|
Red | 1 byte | 0 = 黑色, 255 = 紅 |
Green | 1 byte | 0 = 黑色, 255 = 綠色 |
Blue | 1 byte | 0 = 黑色, 255 = 藍(lán)色 |
調(diào)色板的長度應(yīng)該是 3 的倍數(shù)朦蕴,否則甩恼,這將是一個非法的調(diào)色板愉舔。
對于索引圖像笨使,調(diào)色板信息是必須的樱溉,調(diào)色板的顏色索引從 0 開始編號疫向,然后是 1襟雷、2……冈止,調(diào)色板的顏色數(shù)不能超過色深中規(guī)定的顏色數(shù)(如圖像色深為 4 的時候岳瞭,調(diào)色板中的顏色數(shù)不可以超過 2^4=16)拥娄,否則,這將導(dǎo)致 PNG 圖像不合法瞳筏。
真彩色圖像和帶 α 通道數(shù)據(jù)的真彩色圖像也可以有調(diào)色板數(shù)據(jù)塊稚瘾,目的是便于非真彩色顯示程序用它來量化圖像數(shù)據(jù),從而顯示該圖像姚炕。
用十六進(jìn)制查看器打開一個索引圖像 PNG 文件:
十六進(jìn)制 | 說明 |
---|---|
00 00 00 27 | 數(shù)據(jù)塊長度 39 字節(jié) |
50 4C 54 45 | 數(shù)據(jù)塊類型碼 “PLTE” 的 ASCII 字母 |
B7 00 34 FF 99 00 60 00 73 FF 0F 00 FF ED 00 09 00 B2 FF 66 00 FF 3B 00 E2 00 15 8B 00 54 FF C1 00 33 00 99 FF FF 00
|
調(diào)色板顏色 13 個 |
48 29 75 2C | CRC (循環(huán)冗余檢測) |
數(shù)據(jù)塊-圖像數(shù)據(jù)塊 IDAT
它存儲實際的數(shù)據(jù)摊欠,在數(shù)據(jù)流中可包含多個連續(xù)順序的圖像數(shù)據(jù)塊。
IDAT 存放著圖像真正的數(shù)據(jù)信息柱宦,因此些椒,如果能夠了解 IDAT 的結(jié)構(gòu),我們就可以很方便的生成 PNG 圖像掸刊。
用十六進(jìn)制查看器打開一個索引圖像 PNG 文件:
十六進(jìn)制 | 說明 |
---|---|
00 00 00 D3 | 數(shù)據(jù)塊長度 211 字節(jié) |
49 44 41 54 | 數(shù)據(jù)塊類型碼 “IDAT” 的 ASCII 字母 |
78 9C ...... | 壓縮的數(shù)據(jù) 211 字節(jié)免糕,LZ77 派生壓縮方法 |
52 98 5D 9D | CRC (循環(huán)冗余檢測) |
圖像數(shù)據(jù)塊 IDAT 細(xì)節(jié)在本文下半部分有詳細(xì)分析
數(shù)據(jù)塊-圖像結(jié)束數(shù)據(jù) IEND
它用來標(biāo)記 PNG 文件或者數(shù)據(jù)流已經(jīng)結(jié)束,并且必須要放在文件的尾部忧侧。
如果我們仔細(xì)觀察 PNG 文件石窑,我們會發(fā)現(xiàn),文件的結(jié)尾 12 個字符看起來總應(yīng)該是這樣的:
00 00 00 00 49 45 4E 44 AE 42 60 82
用十六進(jìn)制查看器打開一個 PNG 文件:
由于數(shù)據(jù)塊結(jié)構(gòu)的定義蚓炬,IEND 數(shù)據(jù)塊的長度總 是 0(00 00 00 00松逊,除非人為加入信息),數(shù)據(jù)標(biāo)識總是 IEND(49 45 4E 44)肯夏,因此棺棵,CRC 碼也總是 AE 42 60 82。
圖像數(shù)據(jù)塊 IDAT 細(xì)節(jié)
IDAT 壓縮數(shù)據(jù)細(xì)節(jié)
在 PNG Spec 壓縮算法部分:
PNG compression method 0 is deflate/inflate compression with a sliding window (which is an upper bound on the distances appearing in the deflate stream) of at most 32768 bytes. Deflate compression is an LZ77 derivative [ZL].
Deflate-compressed datastreams within PNG are stored in the "zlib" format, which has the structure:
- zlib compression method/flags code 1 byte
- Additional flags/check bits 1 byte
- Compressed data blocks n bytes
- Check value 4 bytes
Further details on this format are given in the zlib specification [RFC-1950].
- PNG 使用 DEFLATE 壓縮算法熄捍。
- DEFLATE 是同時使用了 LZ77 算法與哈夫曼編碼(Huffman Coding)的一個無損數(shù)據(jù)壓縮算法烛恤。
- DEFLATE 壓縮的數(shù)據(jù)以 zlib 格式存儲。
zlib(RFC1950):一種格式余耽,是對 deflate 進(jìn)行了簡單的封裝缚柏,他也是一個實現(xiàn)庫(delphi中有zlib,zlibex)
gzip(RFC1952):一種格式,也是對 deflate 進(jìn)行的封裝碟贾。
gzip = gzip 頭 + deflate 編碼的實際內(nèi)容 + gzip 尾
zlib = zlib 頭 + deflate 編碼的實際內(nèi)容 + zlib 尾
提取&解壓 IDAT 中壓縮數(shù)據(jù)
Windows 上可以用 [hexeditor](https://www.hhdsoftware.com/free-hex-editor)
Mac 上可以用 [hexfiend](http://ridiculousfish.com/hexfiend/)币喧、[Hopper Disassembler](https://www.hopperapp.com/)
使用 zlib
解壓 78 9C ......
壓縮的數(shù)據(jù)字節(jié):
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "zlib.h"
int main() {
FILE *inFile = fopen("/Users/staff/Desktop/indexed-color-image.data", "rb");
FILE *outFile = fopen("/Users/staff/Desktop/indexed-color-image-uncompress.data", "wb");
fseek(inFile, 0L, SEEK_END);
long size = ftell(inFile);
fseek(inFile, 0L, SEEK_SET);
uint8_t dataBuf[size];
fread(dataBuf, size, 1, inFile);
printf("壓縮文件大泄煊颉:%ld\n", size);
uint8_t destBuf[1500000]={0};
uint32_t destLen = 0;
uncompress(destBuf, &destLen, dataBuf, size);
printf("解壓后大小:%d\n", destLen);
fwrite(destBuf, destLen, 1, outFile);
fflush(outFile);
fclose(inFile);
fclose(outFile);
return 0;
}
分析解壓后的數(shù)據(jù)
在 PNG Spec 7.1 Integers and byte order
All integers that require more than one byte shall be in network byte order (as illustrated in figure 7.1): the most significant byte comes first, then the less significant bytes in descending order of significance (MSB LSB for two-byte integers, MSB B2 B1 LSB for four-byte integers). The highest bit (value 128) of a byte is numbered bit 7; the lowest bit (value 1) is numbered bit 0. Values are unsigned unless otherwise noted. Values explicitly noted as signed are represented in two's complement notation.
- PNG 使用
網(wǎng)絡(luò)字節(jié)序
大端字節(jié)序(Big Endian)
杀餐。
In PNG images of colour type 0 (greyscale) each pixel is a single sample, which may have precision less than a byte (1, 2, or 4 bits). These samples are packed into bytes with the leftmost sample in the high-order bits of a byte followed by the other samples for the scanline.
In PNG images of colour type 3 (indexed-colour) each pixel is a single palette index. These indices are packed into bytes in the same way as the samples for colour type 0.
- PNG 圖像深度小于 1 字節(jié)干发,將會被
packed into bytes
。
PNG allows the scanline data to be filtered before it is compressed. Filtering can improve the compressibility of the data. The filter step itself results in a sequence of bytes of the same size as the incoming sequence, but in a different representation, preceded by a filter type byte. Filtering does not reduce the size of the actual scanline data. All PNG filters are strictly lossless.
Different filter types can be used for different scanlines, and the filter algorithm is specified for each scanline by a filter type byte. The filter type byte is not considered part of the image data, but it is included in the datastream sent to the compression step. An intelligent encoder can switch filters from one scanline to the next. The method for choosing which filter to employ is left to the encoder.
- 每一掃描行前有一字節(jié)用于指定
過濾器類型
史翘。
Filtering transforms the PNG image with the goal of improving compression. PNG allows for a number of filter methods. All the reduced images in an interlaced image shall use a single filter method. Only filter method 0 is defined by this International Standard. Other filter methods are reserved for future standardization (see 4.9 Extension and registration). Filter method 0 provides a set of five filter types, and individual scanlines in each reduced image may use different filter types.
枉长。。琼讽。必峰。。钻蹬。
- 文件頭數(shù)據(jù)塊 IHDR 中
Filter method
過濾方法只能是 0吼蚁。 -
Filter method
=0 定義了 5 種Filter Type
過濾器類型:0:None
、1:Sub
问欠、2:Up
肝匆、3:Average
、4:Paeth
顺献。
當(dāng) PNG 圖片是索引圖像時(下圖數(shù)據(jù):圖像深度: 4
尺寸 256X256
過濾器類型: 0:None
隔行掃描方法:0:非隔行掃描
):
- 每個高亮區(qū)域前面一個字節(jié)
00
代表過濾器類型
:0:None
【PNG Spec 7.3 Filtering】【 PNG Spec Filters】术唬。 - 如果高亮區(qū)域前面一個字節(jié)不是
00
,高亮區(qū)將不是掃描行索引數(shù)據(jù)滚澜,需要參考 【PNG Spec 9.2 Filter types for filter method 0】 - 每種顏色高亮顯示的部分
128 字節(jié)
是一個掃描行顏色索引數(shù)據(jù)粗仓,因為圖像深度:4
,所以每個字節(jié)代表兩個顏色索引 【PNG Spec 7.2 Scanlines】设捐。 - 如字節(jié)
55
是十六進(jìn)制借浊,二進(jìn)制為01010101
,前四 bit 位代表一個顏色索引0101
十進(jìn)制為 5萝招,后四 bit 位代表一個顏色索引0101
十進(jìn)制為 5 蚂斤。
當(dāng) PNG 圖片是真彩圖像時(下圖數(shù)據(jù):圖像深度: 8
尺寸 70X70
過濾器類型: 0:None
隔行掃描方法:0:非隔行掃描
):
- 每個高亮區(qū)域前面一個字節(jié)
00
代表過濾器類型
:0:None
【PNG Spec 7.3 Filtering】【 PNG Spec Filters】。 - 如果高亮區(qū)域前面一個字節(jié)不是
00
槐沼,高亮區(qū)將不是掃描行顏色數(shù)據(jù)曙蒸,需要參考 【PNG Spec 9.2 Filter types for filter method 0】 - 每種顏色高亮顯示的部分
210 字節(jié)
是一個掃描行顏色數(shù)據(jù),因為真彩圖片
圖像深度:8
岗钩,所以每三個字節(jié)代表一個像素顏色纽窟。 - 如字節(jié)
FF 00 00
,代表一個像素 RGB 顏色兼吓。
More
下一步臂港,將用代碼手動生成一張 PNG 圖片,文章目錄:* 音視頻入門文章目錄 *。
所有可能存在問題的答案:PNG Specification审孽。
代碼:
demos/demos-zlib
參考資料:
PNG县袱、JPEG、BMP等幾種圖片格式詳解(一)—— PNG
Portable Network Graphics (PNG) Specification and Extensions
Zlib庫的安裝與使用
本文由博客一文多發(fā)平臺 OpenWrite 發(fā)布佑力!