前言
PNG是20世紀(jì)90年代中期開始開發(fā)的圖像文件存儲(chǔ)格式,其目的是企圖替代GIF和TIFF文件格式,同時(shí)增加一些GIF文件格式所不具備的特性。流式網(wǎng)絡(luò)圖形格式(Portable Network Graphic Format,PNG)名稱來(lái)源于非官方的“PNG's Not GIF”,是一種位圖文件(bitmap file)存儲(chǔ)格式,讀成“ping”。PNG用來(lái)存儲(chǔ)灰度圖像時(shí),灰度圖像的深度可多到16位,存儲(chǔ)彩色圖像時(shí),彩色圖像的深度可多到48位,并且還可存儲(chǔ)多到16位的α通道數(shù)據(jù)。PNG使用從LZ77派生的無(wú)損數(shù)據(jù)壓縮算法。
PNG數(shù)據(jù)塊(Chunk)
PNG定義了兩種類型的數(shù)據(jù)塊,一種是稱為關(guān)鍵數(shù)據(jù)塊(critical chunk),這是標(biāo)準(zhǔn)的數(shù)據(jù)塊,另一種叫做輔助數(shù)據(jù)塊(ancillary chunks),這是可選的數(shù)據(jù)塊。關(guān)鍵數(shù)據(jù)塊定義了4個(gè)標(biāo)準(zhǔn)數(shù)據(jù)塊,每個(gè)PNG文件都必須包含它們,PNG讀寫軟件也都必須要支持這些數(shù)據(jù)塊。雖然PNG文件規(guī)范沒有要求PNG編譯碼器對(duì)可選數(shù)據(jù)塊進(jìn)行編碼和譯碼,但規(guī)范提倡支持可選數(shù)據(jù)塊。
下表就是PNG中數(shù)據(jù)塊的類別,其中,關(guān)鍵數(shù)據(jù)塊部分我們使用深色背景加以區(qū)分。
PNG文件格式中的數(shù)據(jù)塊
|
數(shù)據(jù)塊符號(hào)
|
數(shù)據(jù)塊名稱
|
多數(shù)據(jù)塊
|
可選否
|
位置限制
|
IHDR |
文件頭數(shù)據(jù)塊 |
否 |
否 |
第一塊 |
cHRM |
基色和白色點(diǎn)數(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í)間數(shù)據(jù)塊 |
否 |
是 |
無(wú)限制 |
tEXt |
文本信息數(shù)據(jù)塊 |
是 |
是 |
無(wú)限制 |
zTXt |
壓縮文本數(shù)據(jù)塊 |
是 |
是 |
無(wú)限制 |
fRAc |
(專用公共數(shù)據(jù)塊) |
是 |
是 |
無(wú)限制 |
gIFg |
(專用公共數(shù)據(jù)塊) |
是 |
是 |
無(wú)限制 |
gIFt |
(專用公共數(shù)據(jù)塊) |
是 |
是 |
無(wú)限制 |
gIFx |
(專用公共數(shù)據(jù)塊) |
是 |
是 |
無(wú)限制 |
IEND |
圖像結(jié)束數(shù)據(jù) |
否 |
否 |
最后一個(gè)數(shù)據(jù)塊 |
為了簡(jiǎn)單起見,我們假設(shè)在我們使用的PNG文件中,這4個(gè)數(shù)據(jù)塊按以上先后順序進(jìn)行存儲(chǔ),并且都只出現(xiàn)一次。
數(shù)據(jù)塊結(jié)構(gòu)
PNG文件中,每個(gè)數(shù)據(jù)塊由4個(gè)部分組成,如下:
名稱
|
字節(jié)數(shù)
|
說(shuō)明
|
Length (長(zhǎng)度) |
4字節(jié) |
指定數(shù)據(jù)塊中數(shù)據(jù)域的長(zhǎng)度,其長(zhǎng)度不超過(guò)(231-1)字節(jié) |
Chunk Type Code (數(shù)據(jù)塊類型碼) |
4字節(jié) |
數(shù)據(jù)塊類型碼由ASCII字母(A-Z和a-z)組成 |
Chunk Data (數(shù)據(jù)塊數(shù)據(jù)) |
可變長(zhǎng)度 |
存儲(chǔ)按照Chunk Type Code指定的數(shù)據(jù) |
CRC (循環(huán)冗余檢測(cè)) |
4字節(jié) |
存儲(chǔ)用來(lái)檢測(cè)是否有錯(cuò)誤的循環(huán)冗余碼 |
CRC(cyclic redundancy check)域中的值是對(duì)Chunk Type Code域和Chunk Data域中的數(shù)據(jù)進(jìn)行計(jì)算得到的。CRC具體算法定義在ISO 3309和ITU-T V.42中,其值按下面的CRC碼生成多項(xiàng)式進(jìn)行計(jì)算:
x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1
下面,我們依次來(lái)了解一下各個(gè)關(guān)鍵數(shù)據(jù)塊的結(jié)構(gòu)吧。
IHDR
文件頭數(shù)據(jù)塊IHDR(header chunk):它包含有PNG文件中存儲(chǔ)的圖像數(shù)據(jù)的基本信息,并要作為第一個(gè)數(shù)據(jù)塊出現(xiàn)在PNG數(shù)據(jù)流中,而且一個(gè)PNG數(shù)據(jù)流中只能有一個(gè)文件頭數(shù)據(jù)塊。
文件頭數(shù)據(jù)塊由13字節(jié)組成,它的格式如下表所示。
域的名稱
|
字節(jié)數(shù)
|
說(shuō)明
|
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 |
壓縮方法(LZ77派生算法) |
Filter method |
1 byte |
濾波器方法 |
Interlace method |
1 byte |
隔行掃描方法: 0:非隔行掃描 1: Adam7(由Adam M. Costello開發(fā)的7遍隔行掃描方法) |
由于我們研究的是手機(jī)上的PNG,因此,首先我們看看MIDP1.0對(duì)所使用PNG圖片的要求吧:
- 在MIDP1.0中,我們只可以使用1.0版本的PNG圖片。并且,所以的PNG關(guān)鍵數(shù)據(jù)塊都有特別要求:
IHDR
- 文件大小:MIDP支持任意大小的PNG圖片,然而,實(shí)際上,如果一個(gè)圖片過(guò)大,會(huì)由于內(nèi)存耗盡而無(wú)法讀取。
- 顏色類型:所有顏色類型都有被支持,雖然這些顏色的顯示依賴于實(shí)際設(shè)備的顯示能力。同時(shí),MIDP也能支持alpha通道,但是,所有的alpha通道信息都會(huì)被忽略并且當(dāng)作不透明的顏色對(duì)待。
- 色深:所有的色深都能被支持。
- 壓縮方法:僅支持壓縮方式0(deflate壓縮方式),這和jar文件的壓縮方式完全相同,所以,PNG圖片數(shù)據(jù)的解壓和jar文件的解壓可以使用相同的代碼。(其實(shí)這也就是為什么J2ME能很好的支持PNG圖像的原因:))
- 濾波器方法:盡管在PNG的白皮書中僅定義了方法0,然而所有的5種方法都被支持!
- 隔行掃描:雖然MIDP支持0、1兩種方式,然而,當(dāng)使用隔行掃描時(shí),MIDP卻不會(huì)真正的使用隔行掃描方式來(lái)顯示。
- PLTE chunk:支持
- IDAT chunk:圖像信息必須使用5種過(guò)濾方式中的方式0 (None, Sub, Up, Average, Paeth)
- IEND chunk:當(dāng)IEND數(shù)據(jù)塊被找到時(shí),這個(gè)PNG圖像才認(rèn)為是合法的PNG圖像。
- 可選數(shù)據(jù)塊:MIDP可以支持下列輔助數(shù)據(jù)塊,然而,這卻不是必須的。
bKGD cHRM gAMA hIST iCCP iTXt pHYs
sBIT sPLT sRGB tEXt tIME tRNS zTXt
關(guān)于更多的信息,可以參考http://www.w3.org/TR/REC-png.html
PLTE
調(diào)色板數(shù)據(jù)塊PLTE(palette chunk)包含有與索引彩色圖像(indexed-color image)相關(guān)的彩色變換數(shù)據(jù),它僅與索引彩色圖像有關(guān),而且要放在圖像數(shù)據(jù)塊(image data chunk)之前。
PLTE數(shù)據(jù)塊是定義圖像的調(diào)色板信息,PLTE可以包含1~256個(gè)調(diào)色板信息,每一個(gè)調(diào)色板信息由3個(gè)字節(jié)組成:
顏色
|
字節(jié)
|
意義
|
Red
|
1 byte
|
0 = 黑色, 255 = 紅
|
Green
|
1 byte
|
0 = 黑色, 255 = 綠色
|
Blue
|
1 byte
|
0 = 黑色, 255 = 藍(lán)色
|
因此,調(diào)色板的長(zhǎng)度應(yīng)該是3的倍數(shù),否則,這將是一個(gè)非法的調(diào)色板。
對(duì)于索引圖像,調(diào)色板信息是必須的,調(diào)色板的顏色索引從0開始編號(hào),然后是1、2……,調(diào)色板的顏色數(shù)不能超過(guò)色深中規(guī)定的顏色數(shù)(如圖像色深為4的時(shí)候,調(diào)色板中的顏色數(shù)不可以超過(guò)2^4=16),否則,這將導(dǎo)致PNG圖像不合法。
真彩色圖像和帶α通道數(shù)據(jù)的真彩色圖像也可以有調(diào)色板數(shù)據(jù)塊,目的是便于非真彩色顯示程序用它來(lái)量化圖像數(shù)據(jù),從而顯示該圖像。
IDAT
圖像數(shù)據(jù)塊IDAT(image data chunk):它存儲(chǔ)實(shí)際的數(shù)據(jù),在數(shù)據(jù)流中可包含多個(gè)連續(xù)順序的圖像數(shù)據(jù)塊。
IDAT存放著圖像真正的數(shù)據(jù)信息,因此,如果能夠了解IDAT的結(jié)構(gòu),我們就可以很方便的生成PNG圖像。
IEND
圖像結(jié)束數(shù)據(jù)IEND(image trailer chunk):它用來(lái)標(biāo)記PNG文件或者數(shù)據(jù)流已經(jīng)結(jié)束,并且必須要放在文件的尾部。
如果我們仔細(xì)觀察PNG文件,我們會(huì)發(fā)現(xiàn),文件的結(jié)尾12個(gè)字符看起來(lái)總應(yīng)該是這樣的:
00 00 00 00 49 45 4E 44 AE 42 60 82
不難明白,由于數(shù)據(jù)塊結(jié)構(gòu)的定義,IEND數(shù)據(jù)塊的長(zhǎng)度總是0(00 00 00 00,除非人為加入信息),數(shù)據(jù)標(biāo)識(shí)總是IEND(49 45 4E 44),因此,CRC碼也總是AE 42 60 82。
實(shí)例研究PNG
以下是由Fireworks生成的一幅圖像,圖像大小為8*8,為了方便大家觀看,我們將圖像放大:
使用UltraEdit32打開該文件,如下:
00000000~00000007:
可以看到,選中的頭8個(gè)字節(jié)即為PNG文件的標(biāo)識(shí)。
接下來(lái)的地方就是IHDR數(shù)據(jù)塊了:
00000008~00000020:
- 00 00 00 0D 說(shuō)明IHDR頭塊長(zhǎng)為13
- 49 48 44 52 IHDR標(biāo)識(shí)
- 00 00 00 08 圖像的寬,8像素
- 00 00 00 08 圖像的高,8像素
- 04 色深,2^4=16,即這是一個(gè)16色的圖像(也有可能顏色數(shù)不超過(guò)16,當(dāng)然,如果顏色數(shù)不超過(guò)8,用03表示更合適)
- 03 顏色類型,索引圖像
- 00 PNG Spec規(guī)定此處總為0(非0值為將來(lái)使用更好的壓縮方法預(yù)留),表示使壓縮方法(LZ77派生算法)
- 00 同上
- 00 非隔行掃描
- 36 21 A3 B8 CRC校驗(yàn)
00000021~0000002F:
可選數(shù)據(jù)塊sBIT,顏色采樣率,RGB都是256(2^8=256)
00000030~00000062:
這里是調(diào)色板信息
- 00 00 00 27 說(shuō)明調(diào)色板數(shù)據(jù)長(zhǎng)為39字節(jié),既13個(gè)顏色數(shù)
- 50 4C 54 45 PLTE標(biāo)識(shí)
- FF FF 00 顏色0
- FF ED 00 顏色1
- …… ……
- 09 00 B2 最后一個(gè)顏色,12
- 5F F5 BB DD CRC校驗(yàn)
00000063~000000C5:
這部分包含了pHYs、tExt兩種類型的數(shù)據(jù)塊共3塊,由于并不太重要,因此也不再詳細(xì)描述了。
000000C0~000000F8:
以上選中部分是IDAT數(shù)據(jù)塊
- 00 00 00 27 數(shù)據(jù)長(zhǎng)為39字節(jié)
- 49 44 41 54 IDAT標(biāo)識(shí)
- 78 9C…… 壓縮的數(shù)據(jù),LZ77派生壓縮方法
- DA 12 06 A5 CRC校驗(yàn)
IDAT中壓縮數(shù)據(jù)部分在后面會(huì)有詳細(xì)的介紹。
000000F9~00000104:
IEND數(shù)據(jù)塊,這部分正如上所說(shuō),通常都應(yīng)該是
00 00 00 00 49 45 4E 44 AE 42 60 82
至此,我們已經(jīng)能夠從一個(gè)PNG文件中識(shí)別出各個(gè)數(shù)據(jù)塊了。由于PNG中規(guī)定除關(guān)鍵數(shù)據(jù)塊外,其它的輔助數(shù)據(jù)塊都為可選部分,因此,有了這個(gè)標(biāo)準(zhǔn)后,我們可以通過(guò)刪除所有的輔助數(shù)據(jù)塊來(lái)減少PNG文件的大小。(當(dāng)然,需要注意的是,PNG格式可以保存圖像中的層、文字等信息,一旦刪除了這些輔助數(shù)據(jù)塊后,圖像將失去原來(lái)的可編輯性。)
刪除了輔助數(shù)據(jù)塊后的PNG文件,現(xiàn)在文件大小為147字節(jié),原文件大小為261字節(jié),文件大小減少后,并不影響圖像的內(nèi)容。
如上說(shuō)過(guò),IDAT數(shù)據(jù)塊是使用了LZ77壓縮算法生成的,由于受限于手機(jī)處理器的能力,因此,如果我們?cè)谏蒊DAT數(shù)據(jù)塊時(shí)仍然使用LZ77壓縮算法,將會(huì)使效率大打折扣,因此,為了效率,只能使用無(wú)壓縮的LZ77算法,關(guān)于LZ77算法的具體實(shí)現(xiàn),此文不打算深究,如果你對(duì)LZ77算法的JAVA實(shí)現(xiàn)有興趣,可以參考以下兩個(gè)站點(diǎn):
參考資料:
PNG文件格式白皮書:http://www.w3.org/TR/REC-png.html
為數(shù)不多的中文PNG格式說(shuō)明:http://dev.gameres.com/Program/Visual/Other/PNGFormat.htm
RFC-1950(ZLIB Compressed Data Format Specification):ftp://ds.internic.net/rfc/rfc1950.txt
RFC-1950(DEFLATE Compressed Data Format Specification):ftp://ds.internic.net/rfc/rfc1951.txt
LZ77算法的JAVA實(shí)現(xiàn):http://jazzlib.sourceforge.net/
LZ77算法的JAVA實(shí)現(xiàn),包括J2ME版本:http://www.jcraft.com/jzlib/index.html