在J2ME平臺上PNG圖片格式幾乎成為了標(biāo)準(zhǔn),無數(shù)臺手持設(shè)備上運(yùn)行的J2ME程序幾乎都選用PNG來顯示圖像,包括大量的手機(jī)游戲以及手機(jī)應(yīng)用,所以對PNG文件格式的了解,可以更有效的減少Jar Size,保護(hù)自有知識產(chǎn)權(quán)。 PNG文件格式: 文件頭: IHDR塊: PLTE塊: tRNS塊: IEND塊: 了解了PNG的文件結(jié)構(gòu),壓縮就有的放矢了。壓縮有6個級別,可以根據(jù)需要選擇。 Level1:讀取PNG文件,將除六大塊之外的所有區(qū)塊都過濾掉 Level2:文件頭是固定的0x89504e47 0x0d0a1a0a,文件尾是固定的0x00000000 0x49454e44 0xae426082,去掉! Level3:每個區(qū)塊的Chunk Type我們是否需要呢?很明顯,我們自己寫的壓縮格式自己應(yīng)該清楚是按照什么樣的順序,去掉! Level4:每個區(qū)塊的Chunk Length我們是否需要呢? IHDR塊:定長13個字節(jié),明顯不需要,去掉。 PLTE塊:最多128個顏色,為撒要用4byte來記錄區(qū)塊長度而不是用1byte來記錄顏色數(shù)呢? tRNS塊:既然有顏色數(shù),tRNS又是調(diào)色盤顏色表的對應(yīng)表,既數(shù)量與顏色數(shù)相同,為撒還需要呢? IDAT塊:我想這個是唯一需要4byte來記錄長度的區(qū)塊。 Level5:每個區(qū)塊的Chunk CRC是否需要呢? 因?yàn)橛嬎鉉RC需要一些時間,但對于字節(jié)較少的區(qū)塊一般可以忽略不計,所以對于這個問題還是由程序員自己決定吧。對于CRC的計算可以參看CoCoMo的另一篇Blog“PNG文件的CRC碼計算” Level6:每個區(qū)塊我們是否要原封不動的保存期數(shù)據(jù)呢? IHDR塊:除了寬、高、色深是需要的,后面那4byte的信息是固定的0x03000000 PLTE塊:為撒要用3byte來表示RGB而不是2byte的565格式?壓縮方法可以參看CoCoMo的另一篇Blog“關(guān)于PNG圖像壓縮的一點(diǎn)感悟” tRNS塊:我想tRNS塊是冗余最多的區(qū)塊了吧,大段大段的0xFF明顯沒有必要,一般的PNG文件只有一個透明色,為撒要用對應(yīng)表的方法而不是一個索引來記錄到底哪個是透明色呢?由于顏色數(shù)最多128,所以只需1byte就可以代替tRNS那么多0xFF啦。 IDAT塊:么想法,如果你夠變態(tài),把zlib加進(jìn)來吧! PNG圖像解壓: 創(chuàng)建了自定義的文件,J2ME端讀取后,就面臨解壓的問題了。我們可以利用此函數(shù)來創(chuàng)建Image: static Image createImage(byte[] imageData, int imageOffset, int imageLength) 前提是傳入的imageData與PNG未被壓縮前的一致。因?yàn)镻NG文件格式是固定的,所以讀取自定義的壓縮文件后,開始將那些默認(rèn)的數(shù)據(jù)再添加進(jìn)去,實(shí)現(xiàn)解壓的目的。下面就開始解壓之旅吧! 首先要創(chuàng)建一個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); //壓縮時舍掉的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ù)寫入有錯誤,經(jīng)常會出現(xiàn)創(chuàng)建Image失敗的異常,而且非常不好調(diào)試,不過只要自定的壓縮格式定下來后,對應(yīng)的創(chuàng)建Image的函數(shù)只要寫一次,以后基本不會出問題哈。 PNG圖像加解密: 很多人都擔(dān)心自己辛苦創(chuàng)作的漂亮的美術(shù)圖片很easy就被別人拿到了,究其原因是由于PNG文件格式是固定的,稍微了解的人用UltraEdit很容易就能找到IHDR,PLTE等標(biāo)識了。CoCoMo就經(jīng)??碐ameLoft的圖像文件,哈哈。一般是2byte的Length,然后緊接著圖片數(shù)據(jù),都放在一個文件里,直接拷貝2進(jìn)制然后粘貼到一個新文件里就是一幅圖。后來的加密技術(shù)會把PNG分塊,例如前100個字節(jié)一塊,緊接著1K一塊,最后剩余字節(jié)一塊,然后把塊順序打亂,用2byte來記錄總長度,1byte記錄順序,但是這并沒有從根本上消除IHDR,IEND這些顯眼的定位標(biāo)識,好像在對破解者說:嘿,看,我就在這里! 現(xiàn)在了解了之前的壓縮和解壓技術(shù),這個問題也就迎刃而解了,因?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)該是一張黑白圖。 |
|