png图片结构分析与加密解密原理(转)
png圖片結(jié)構(gòu)分析與加密解密原理
分類:圖像處理
解密加密byteimage算法存儲(chǔ)
PNG文件格式分為PNG-24和PNG-8,其最大的區(qū)別是PNG-24是用24位來保存一個(gè)像素值,是真彩色,而PNG-8是用8位索引值來在調(diào)色盤 中索引一個(gè)顏色,因?yàn)橐粋€(gè)索引值的最大上限為2的8次方既128,故調(diào)色盤中顏色數(shù)最多為128種,所以該文件格式又被叫做PNG-8 128仿色。PNG-24因?yàn)槠鋱D片容量過大,而且在Nokia和Moto等某些機(jī)型上創(chuàng)建圖片失敗和顯示不正確等異常時(shí)有發(fā)生,有時(shí)還會(huì)嚴(yán)重拖慢顯示速度,故并不常 用,CoCoMo認(rèn)為這些異常和平臺(tái)底層的圖像解壓不無關(guān)系。不過該格式最大的優(yōu)點(diǎn)是可以保存Alpha通道,同事也曾有過利用該圖片格式實(shí)現(xiàn)Alpha 混合的先例,想來隨著技術(shù)的發(fā)展,手機(jī)硬件平臺(tái)的提升,Alpha混合一定會(huì)被廣泛的應(yīng)用,到那時(shí)該格式的最大優(yōu)勢(shì)才會(huì)真正發(fā)揮。?
8 bit PNGs use an indexed color palette like GIF. If you want variable transparency, use 32bit PNGs (24 bit color, 8 bit alpha). If you don't care about transparency, use 24 bit PNGs.
PNG-8文件是目前廣泛應(yīng)用的PNG圖像格式,其主要有六大塊組成:
?1.PNG文件標(biāo)志,為固定的64個(gè)字節(jié):0x89504e47 0x0d0a1a0a
?2.文件頭數(shù)據(jù)塊IHDR(header chunk)
3.調(diào)色板數(shù)據(jù)塊PLTE(palette chunk)
4.sBIT,tRNS塊?等。。。
5.圖像數(shù)據(jù)塊IDAT(image data chunk)
6.圖像結(jié)束數(shù)據(jù)IEND(image trailer chunk),固定的96個(gè)字節(jié):0x00000000 0x49454e44 0xae426082
?這六大塊按順序排列,也就是說IDAT塊永遠(yuǎn)是在PLTE塊之后,期間也會(huì)有許多其他的區(qū)塊用來描述信息,例如圖像的最后修改時(shí)間是多少,圖像的創(chuàng)建者是誰等,不過這些區(qū)塊的信息對(duì)我們來說都是可有可無的描述信息,故壓縮時(shí)一般先向這些區(qū)塊開刀。
??數(shù)據(jù)塊1-4:?
除了PNG文件標(biāo)志,其中四大數(shù)據(jù)塊和文件尾都是由統(tǒng)一的數(shù)據(jù)塊文件結(jié)構(gòu)描述的:?
Chunk Length: 4byte?
Chunk Type:4byte?
Chunk Data:Chunk Length的長(zhǎng)度?
Chunk CRC: 4byte
?例如IHDR塊的數(shù)據(jù)長(zhǎng)度為13,既?
Chunk Length = 13 ?
Chunk Type = "IHDR"
IHDR塊:
?用來描述圖像的基本信息,其格式為:?
圖像寬: 4byte?
圖像高: 4byte?
圖像色深: 4byte?
顏色類型:1byte?
壓縮方法: 1byte?
濾波方法: 1byte?
掃描方法: 1byte?
曾經(jīng)有人問過我,撒叫濾波方法和掃描方法,汗,說實(shí)話我也不知道,不過我們是在做手機(jī)游戲,不是在搞圖形學(xué)不是嘛。
PLTE塊:?
這個(gè)就是傳說中放置調(diào)色盤數(shù)據(jù)的地方啦,其格式為:?
循環(huán)?
RED: 1byte?
GREEN:1byte?
BLUE: 1byte?
END
?循環(huán)長(zhǎng)度嘛,不就是Chunk Length / 3的長(zhǎng)度嘛,而且Chunk Length一定為3的倍數(shù)。
tRNS塊:?
這個(gè)塊時(shí)有時(shí)無,主要是看你是否使用了透明色。該區(qū)塊的格式為:?
循環(huán)?
if(對(duì)應(yīng)調(diào)色盤顏色非透明)?
0xFF: 1byte?
else?
0x00: 1byte?
END?
循環(huán)長(zhǎng)度為調(diào)色盤的顏色數(shù),相當(dāng)于調(diào)色盤顏色表的一個(gè)對(duì)應(yīng)表,標(biāo)識(shí)該顏色是否透明,0xFF不透明,0x00透明。故如果用UltraEdit查看PNG文件的二進(jìn)制編碼,如果看到一大片F(xiàn)F,一般就是tRNS區(qū)塊啦,因?yàn)橐粋€(gè)PNG文件一般只有一個(gè)透明色。
??IDAT塊:?這個(gè)就是存放圖像數(shù)據(jù)的地方啦,這里要注意的是一個(gè)PNG文件可能有多個(gè)IDAT區(qū)塊,而其他三大區(qū)塊只可能有一個(gè)。?IDAT 區(qū)塊是經(jīng)過壓縮的,所以數(shù)據(jù)不可讀,壓縮算法一般為L(zhǎng)Z77滑動(dòng)窗口算法,如果硬要看里面的數(shù)據(jù)的話,用zlib庫也是可以的,CoCoMo當(dāng)年就見過 Windows Mobile上的帝國(guó)時(shí)代巨變態(tài)的用zlib庫壓縮和解壓該區(qū)塊來進(jìn)一步減少PNG文件大小,真是寸K寸金啊。
IEND塊:?該區(qū)塊雖然也按照數(shù)據(jù)塊的結(jié)構(gòu),但Chunk Data是沒有的,所以是固定的96個(gè)字節(jié):0x00000000 0x49454e44 0xae426082
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。
PNG圖像壓縮:
了解了PNG的文件結(jié)構(gòu),壓縮就有的放矢了。壓縮有6個(gè)級(jí)別,可以根據(jù)需要選擇。
Level1:讀取PNG文件,將除六大塊之外的所有區(qū)塊都過濾掉
Level2:文件頭是固定的0x89504e47 0x0d0a1a0a,文件尾是固定的0x00000000 0x49454e44 0xae426082,去掉!
Level3:每個(gè)區(qū)塊的Chunk Type我們是否需要呢?很明顯,我們自己寫的壓縮格式自己應(yīng)該清楚是按照什么樣的順序,去掉!
Level4:每個(gè)區(qū)塊的Chunk Length我們是否需要呢?
IHDR塊:定長(zhǎng)13個(gè)字節(jié),明顯不需要,去掉。
PLTE塊:最多128個(gè)顏色,為撒要用4byte來記錄區(qū)塊長(zhǎng)度而不是用1byte來記錄顏色數(shù)呢?
tRNS塊:既然有顏色數(shù),tRNS又是調(diào)色盤顏色表的對(duì)應(yīng)表,既數(shù)量與顏色數(shù)相同,為撒還需要呢?
IDAT塊:我想這個(gè)是唯一需要4byte來記錄長(zhǎng)度的區(qū)塊。
Level5:每個(gè)區(qū)塊的Chunk CRC是否需要呢?
因?yàn)橛?jì)算CRC需要一些時(shí)間,但對(duì)于字節(jié)較少的區(qū)塊一般可以忽略不計(jì),所以對(duì)于這個(gè)問題還是由程序員自己決定吧。對(duì)于CRC的計(jì)算可以參看CoCoMo的另一篇Blog“PNG文件的CRC碼計(jì)算”
Level6:每個(gè)區(qū)塊我們是否要原封不動(dòng)的保存期數(shù)據(jù)呢?
IHDR塊:除了寬、高、色深是需要的,后面那4byte的信息是固定的0x03000000
PLTE塊:為撒要用3byte來表示RGB而不是2byte的565格式?壓縮方法可以參看CoCoMo的另一篇Blog“關(guān)于PNG圖像壓縮的一點(diǎn)感悟”
tRNS塊:我想tRNS塊是冗余最多的區(qū)塊了吧,大段大段的0xFF明顯沒有必要,一般的PNG文件只有一個(gè)透明色,為撒要用對(duì)應(yīng)表的方法而不是一個(gè)索 引來記錄到底哪個(gè)是透明色呢?由于顏色數(shù)最多128,所以只需1byte就可以代替tRNS那么多0xFF啦。
IDAT塊:么想法,如果你夠變態(tài),把zlib加進(jìn)來吧!
PNG圖像解壓:
創(chuàng)建了自定義的文件,J2ME端讀取后,就面臨解壓的問題了。我們可以利用此函數(shù)來創(chuàng)建Image:
staticImage
createImage(byte[]imageData, intimageOffset, intimageLength)
前提是傳入的imageData與PNG未被壓縮前的一致。因?yàn)镻NG文件格式是固定的,所以讀取自定義的壓縮文件后,開始將那些默認(rèn)的數(shù)據(jù)再添加進(jìn)去,實(shí)現(xiàn)解壓的目的。下面就開始解壓之旅吧!
首先要?jiǎng)?chuàng)建一個(gè)ByteArrayOutputStream out,
1.寫入文件頭:
out.writeInt(0x89504e47);
out.writeInt(0x0d0a1a0a);
2.寫入IHDR塊
out.writeInt(13);
out.writeInt(0x49484452); //0x49484452為Chunk Type "IHDR"
out.writeInt(width);
out.writeInt(height);
out.writeByte(depth);
out.writeInt(0x03000000); //壓縮時(shí)舍掉的4byte,默認(rèn)0x03000000
out.writeInt(crc);
其他區(qū)塊方法一致,故略過。。。
3.寫入文件尾
out.writeInt(0x00000000);
out.writeInt(0x49454e44);
out.writeInt(0xae426082);
4.轉(zhuǎn)換成數(shù)組,創(chuàng)建Image
byte[] pngBuffer = out.toByteArray();
Image image = Image.createImage(pngBuffer, 0, pngBuffer.length);
哈哈,大功告成。這里注意如果中途數(shù)據(jù)寫入有錯(cuò)誤,經(jīng)常會(huì)出現(xiàn)創(chuàng)建Image失敗的異常,而且非常不好調(diào)試,不過只要自定的壓縮格式定下來后,對(duì)應(yīng)的創(chuàng)建Image的函數(shù)只要寫一次,以后基本不會(huì)出問題哈。
PNG圖像加解密:
很多人都擔(dān)心自己辛苦創(chuàng)作的漂亮的美術(shù)圖片很easy就被別人拿到了,究其原因是由于PNG文件格式是固定的,稍微了解的人用UltraEdit很容易就 能找到IHDR,PLTE等標(biāo)識(shí)了。CoCoMo就經(jīng)常看GameLoft的圖像文件,哈哈。一般是2byte的Length,然后緊接著圖片數(shù)據(jù),都放 在一個(gè)文件里,直接拷貝2進(jìn)制然后粘貼到一個(gè)新文件里就是一幅圖。后來的加密技術(shù)會(huì)把PNG分塊,例如前100個(gè)字節(jié)一塊,緊接著1K一塊,最后剩余字節(jié) 一塊,然后把塊順序打亂,用2byte來記錄總長(zhǎng)度,1byte記錄順序,但是這并沒有從根本上消除IHDR,IEND這些顯眼的定位標(biāo)識(shí),好像在對(duì)破解 者說:嘿,看,我就在這里!
現(xiàn)在了解了之前的壓縮和解壓技術(shù),這個(gè)問題也就迎刃而解了,因?yàn)镃hunk Length,Chunk Type和Chunk CRC這些東西都消失了,甚至連數(shù)據(jù)塊本身的數(shù)據(jù)都修改了,我可以按照ImageWidth、ImageHeight、ImageDepth的順序?qū)憯?shù) 據(jù),也可以倒過來寫。我想再牛的PNG分析器也是無能為力的吧,唯一可以定位的就只有IDAT區(qū)塊了,不過就算得到該區(qū)塊的數(shù)據(jù),也應(yīng)該是一張黑白圖。
-----------------------------------------------------------------
-----------------------------------------------------------------
-----------------------------------------------------------------
附錄
PNG文件結(jié)構(gòu)分析(上:了解PNG文件存儲(chǔ)格式)
PNG的文件結(jié)構(gòu)
對(duì)于一個(gè)PNG文件來說,其文件頭總是由位固定的字節(jié)來描述的:
| 十進(jìn)制數(shù) | 137 80 78 71 13 10 26 10 |
| 十六進(jìn)制數(shù) | 89 50 4E 47 0D 0A 1A 0A |
其中第一個(gè)字節(jié)0x89超出了ASCII字符的范圍,這是為了避免某些軟件將PNG文件當(dāng)做文本文件來處理。文件中剩余的部分由3個(gè)以上的PNG的數(shù)據(jù)塊(Chunk)按照特定的順序組成,因此,一個(gè)標(biāo)準(zhǔn)的PNG文件結(jié)構(gòu)應(yīng)該如下:
| PNG文件標(biāo)志 | PNG數(shù)據(jù)塊 | …… | PNG數(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ù)塊 | 否 | 是 | 無限制 |
| tEXt | 文本信息數(shù)據(jù)塊 | 是 | 是 | 無限制 |
| zTXt | 壓縮文本數(shù)據(jù)塊 | 是 | 是 | 無限制 |
| fRAc | (專用公共數(shù)據(jù)塊) | 是 | 是 | 無限制 |
| gIFg | (專用公共數(shù)據(jù)塊) | 是 | 是 | 無限制 |
| gIFt | (專用公共數(shù)據(jù)塊) | 是 | 是 | 無限制 |
| gIFx | (專用公共數(shù)據(jù)塊) | 是 | 是 | 無限制 |
| 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ù) | 說明 |
| Length (長(zhǎng)度) | 4字節(jié) | 指定數(shù)據(jù)塊中數(shù)據(jù)域的長(zhǎng)度,其長(zhǎng)度不超過(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ǔ)用來檢測(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
下面,我們依次來了解一下各個(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ù) | 說明 |
| 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è)圖片過大,會(huì)由于內(nèi)存耗盡而無法讀取。
顏色類型:所有顏色類型都有被支持,雖然這些顏色的顯示依賴于實(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ì)真正的使用隔行掃描方式來顯示。
PLTE chunk:支持
IDAT chunk:圖像信息必須使用5種過濾方式中的方式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ī)定的顏色數(shù)(如圖像色深為4的時(shí)候,調(diào)色板中的顏色數(shù)不可以超過2^4=16),否則,這將導(dǎo)致PNG圖像不合法。
真彩色圖像和帶α通道數(shù)據(jù)的真彩色圖像也可以有調(diào)色板數(shù)據(jù)塊,目的是便于非真彩色顯示程序用它來量化圖像數(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):它用來標(biāo)記PNG文件或者數(shù)據(jù)流已經(jīng)結(jié)束,并且必須要放在文件的尾部。
如果我們仔細(xì)觀察PNG文件,我們會(huì)發(fā)現(xiàn),文件的結(jié)尾12個(gè)字符看起來總應(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í)。
接下來的地方就是IHDR數(shù)據(jù)塊了:
00000008~00000020:
00 00 00 0D 說明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ù)不超過16,當(dāng)然,如果顏色數(shù)不超過8,用03表示更合適)
03 顏色類型,索引圖像
00 PNG Spec規(guī)定此處總為0(非0值為將來使用更好的壓縮方法預(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 說明調(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ù)塊,這部分正如上所說,通常都應(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)后,我們 可以通過刪除所有的輔助數(shù)據(jù)塊來減少PNG文件的大小。(當(dāng)然,需要注意的是,PNG格式可以保存圖像中的層、文字等信息,一旦刪除了這些輔助數(shù)據(jù)塊后, 圖像將失去原來的可編輯性。)
刪除了輔助數(shù)據(jù)塊后的PNG文件,現(xiàn)在文件大小為147字節(jié),原文件大小為261字節(jié),文件大小減少后,并不影響圖像的內(nèi)容。
其實(shí),我們可以通過改變調(diào)色板的色值來完成一些又趣的事情,比如說實(shí)現(xiàn)云彩/水波的流動(dòng)效果,實(shí)現(xiàn)圖像的淡入淡出效果等等,在此,給出一個(gè)鏈接給大家看也許更直接:http://blog.csdn.net/flyingghost/archive/2005/01/13/251110.aspx,我寫此文也就是受此文的啟發(fā)的。
如 上說過,IDAT數(shù)據(jù)塊是使用了LZ77壓縮算法生成的,由于受限于手機(jī)處理器的能力,因此,如果我們?cè)谏蒊DAT數(shù)據(jù)塊時(shí)仍然使用LZ77壓縮算法, 將會(huì)使效率大打折扣,因此,為了效率,只能使用無壓縮的LZ77算法,關(guān)于LZ77算法的具體實(shí)現(xiàn),此文不打算深究,如果你對(duì)LZ77算法的JAVA實(shí)現(xiàn) 有興趣,可以參考以下兩個(gè)站點(diǎn):
http://jazzlib.sourceforge.net/
http://www.jcraft.com/jzlib/index.html
PNG文件結(jié)構(gòu)分析(下:在手機(jī)上生成PNG文件)
上面我們已經(jīng)對(duì)PNG的存儲(chǔ)格式有了了解,因此,生成PNG圖片只需要按照以上的數(shù)據(jù)塊寫入文件即可。
(由于IHDR、PLTE的結(jié)構(gòu)都非常簡(jiǎn)單,因此,這里我們只是重點(diǎn)講一講IDAT的生成方法,IHDR和PLTE的數(shù)據(jù)內(nèi)容都沿用以上的數(shù)據(jù)內(nèi)容)
問題確實(shí)是這樣的,我們知道,對(duì)于大多數(shù)的圖形文件來說,我們都可以將實(shí)際的圖像內(nèi)容映射為一個(gè)二維的顏色數(shù)組,對(duì)于上面的PNG文件,由于它用的是16色的調(diào)色板(實(shí)際是13色),因此,對(duì)于圖片的映射可以如下:
(調(diào)色板對(duì)照?qǐng)D)
| 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 |
| 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 |
| 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 |
| 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 |
| 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 6 | 5 | 4 | 3 | 2 | 1 | 0 | 0 |
| 5 | 4 | 3 | 2 | 1 | 0 | 0 | 0 |
PNG Spec中指出,如果PNG文件不是采用隔行掃描方法存儲(chǔ)的話,那么,數(shù)據(jù)是按照行(ScanLine)來存儲(chǔ)的,為了區(qū)分第一行,PNG規(guī)定在每一行的前面加上0以示區(qū)分,因此,上面的圖像映射應(yīng)該如下:
| 0 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 |
| 0 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 |
| 0 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 |
| 0 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 |
| 0 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
| 0 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 0 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 0 |
| 0 | 5 | 4 | 3 | 2 | 1 | 0 | 0 | 0 |
另外,需要注意的是,由于PNG在存儲(chǔ)圖像時(shí)為了節(jié)省空間,因此每一行是按照位(Bit)來存儲(chǔ)的,而并不是我們想象的字節(jié)(Byte),如果你沒有忘記的話,我們的IHDR數(shù)據(jù)塊中的色深就指明了這一點(diǎn),所以,為了湊成PNG所需要的IDAT,我們的數(shù)據(jù)得改成如下:
| 0 | 203 | 169 | 135 | 101 |
| 0 | 186 | 152 | 118 | 84 |
| 0 | 169 | 135 | 101 | 67 |
| 0 | 152 | 118 | 84 | 50 |
| 0 | 135 | 101 | 67 | 33 |
| 0 | 118 | 84 | 50 | 16 |
| 0 | 101 | 67 | 33 | 0 |
| 0 | 84 | 50 | 16 | 0 |
最后,我們對(duì)這些數(shù)據(jù)進(jìn)行LZ77壓縮就可以得到IDAT的正確內(nèi)容了。
然而,事情并不是這么簡(jiǎn)單,因?yàn)槲覀冄芯康氖鞘謾C(jī)上的PNG,如果需要在手機(jī)上完成LZ77壓縮工作,消耗的時(shí)間是可想而知的,因此,我們得再想辦法加減少壓縮時(shí)消耗的時(shí)間。好在LZ77也提供了無壓縮的壓縮方法(奇怪吧?),因此,我們只需要簡(jiǎn)單的使用無壓縮的方式寫入數(shù)據(jù)就可以了,這樣雖然浪費(fèi)了空間,卻換回了時(shí)間!
好了,讓我們看一看怎么樣湊成無壓縮的LZ77壓縮塊:
| 字節(jié) | 意義 |
| 0~2 | 壓縮信息,固定為0x78, 0xda, 0x1 |
| 3~6 | 壓縮塊的LEN和NLEN信息 |
| 壓縮的數(shù)據(jù) | |
| 最后4字節(jié) | Adler32信息 |
其 中的LEN是指數(shù)據(jù)的長(zhǎng)度,占用兩個(gè)字節(jié),對(duì)于我們的圖像來說,第一個(gè)Scan Line包含了5個(gè)字節(jié)(如第一行的0, 203, 169, 135, 101),所以LEN的值為5(字節(jié)/行) * 8(行) = 40(字節(jié)),生成字節(jié)為28 00(低字節(jié)在前),NLEN是LEN的補(bǔ)碼,即NLEN = LEN ^ 0xFFFF,所以NLEN的為 D7 FF,Adler32信息為24 A7 0B A4(具體算法見源程序),因此,按照這樣的順序,我們生成IDAT數(shù)據(jù)塊,最后,我們將IHDR、PLTE、IDAT和IEND數(shù)據(jù)塊寫入文件中,就可 以得到PNG文件了,如圖:
至此,我們已經(jīng)能夠采用最快的時(shí)間將數(shù)組轉(zhuǎn)換為PNG圖片了
轉(zhuǎn)自:http://blog.csdn.net/yshen_dublin/article/details/4416209
總結(jié)
以上是生活随笔為你收集整理的png图片结构分析与加密解密原理(转)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 解决IE6下 position的fixe
- 下一篇: apex单人怎么玩