iOS減包實戰:Compress PNG Files作用分析
減包這個詞大家應該都不陌生,在減包過程中,圖片資源的優化這項應該是必經之路了,畢竟在包大小中,圖片資源佔的比重是很大,而且是可優化空間較大的一項,而在Xcode中有一個build setting就叫做"Compress PNG Files",翻譯一下就是“壓縮PNG檔案”,看上去簡直是完美,有木有!
然而,當把這個配置改為YES之後,打包之後發現ipa的大小不但沒有減小,反而增大了。說好的Compress呢!!!
這就必須要好好研究下這個"Compress PNG Files"到底是幹啥的了!先找到/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/iphoneos-optimize這個指令碼,其中有如下一段:
Xcode就是執行了這條命令來進行圖片優化的,command line tools中也自帶了這麼一個命令列工具,通過這個工具,我們可以看看圖片經過所謂的Compress之後發生了什麼變化。
以如下這張LUT圖為例,為什麼是這張圖片呢,一是因為這種圖片在影象處理的App中特別常見,是用來做濾鏡調色時使用的,二是因為這種型別的圖片差別特別明顯。
從結果上來看,經過這個“優化”之後,圖片大小從207KB,變成了750KB,整整大了500多K,為了排除偶然性,又選擇了另一張圖片處理
結果還是增大了非常多,說好的Compress呢!
接下來就要詳細的分析下為什麼會出現這種情況了。
首先先簡單介紹一下PNG這種圖片格式,PNG由檔案標誌和多個數據塊組成,檔案標誌是固定的如下圖所示:
資料塊的格式如下:
名稱 |
位元組數 |
說明 |
---|---|---|
Length (長度) |
4位元組 |
指定資料塊中資料域的長度,其長度不超過(231 -1)位元組 |
Chunk Type Code (資料塊型別碼) |
4位元組 |
資料塊型別碼由ASCII字母(A-Z和a-z)組成 |
Chunk Data (資料塊資料) |
可變長度 |
儲存按照Chunk Type Code指定的資料 |
CRC (迴圈冗餘檢測) |
4位元組 |
儲存用來檢測是否有錯誤的迴圈冗餘碼 |
PNG中常見的資料塊型別如下:
PNG檔案格式中的資料塊 |
||||
---|---|---|---|---|
資料塊符號 |
資料塊名稱 |
多資料塊 |
可選否 |
位置限制 |
IHDR |
檔案頭資料塊 |
否 |
否 |
第一塊 |
cHRM |
基色和白色點資料塊 |
否 |
是 |
在PLTE和IDAT之前 |
gAMA |
影象γ資料塊 |
否 |
是 |
在PLTE和IDAT之前 |
sBIT |
樣本有效位資料塊 |
否 |
是 |
在PLTE和IDAT之前 |
PLTE |
調色盤資料塊 |
否 |
是 |
在IDAT之前 |
bKGD |
背景顏色資料塊 |
否 |
是 |
在PLTE之後IDAT之前 |
hIST |
影象直方圖資料塊 |
否 |
是 |
在PLTE之後IDAT之前 |
tRNS |
影象透明資料塊 |
否 |
是 |
在PLTE之後IDAT之前 |
oFFs |
(專用公共資料塊) |
否 |
是 |
在IDAT之前 |
pHYs |
物理畫素尺寸資料塊 |
否 |
是 |
在IDAT之前 |
sCAL |
(專用公共資料塊) |
否 |
是 |
在IDAT之前 |
IDAT |
影象資料塊 |
是 |
否 |
與其他IDAT連續 |
tIME |
影象最後修改時間資料塊 |
否 |
是 |
無限制 |
tEXt |
文字資訊資料塊 |
是 |
是 |
無限制 |
zTXt |
壓縮文字資料塊 |
是 |
是 |
無限制 |
fRAc |
(專用公共資料塊) |
是 |
是 |
無限制 |
gIFg |
(專用公共資料塊) |
是 |
是 |
無限制 |
gIFt |
(專用公共資料塊) |
是 |
是 |
無限制 |
gIFx |
(專用公共資料塊) |
是 |
是 |
無限制 |
IEND |
影象結束資料 |
否 |
否 |
最後一個數據塊 |
還是用之前那張圖片作為例子來看,用pngcheck工具看一下檔案資訊:
這個圖片包含了IHDR、tEXt、iTXt、IDAT、IEND等五個資料塊,其中比較關鍵的資料塊是IHDR、IDAT、IEND三個,而當我們同pngcheck檢視處理之後圖片的話,就會出現如下提示:
這裡我們就會發現,這裡的Compress PNG Files做的並不是單純的壓縮資料,而是把檔案格式也做了修改,當我們去查閱相關資料的時候,可以發現其實Apple是將png圖片轉換成了一種CgBI格式:
These modifications cause the generated images to be invalid as per the current version of the PNG standard.
- extra critical chunk (CgBI)
- byteswapped (RGBA -> BGRA) pixel data, presumably for high-speed direct blitting to the framebuffer
- zlib header, footer, and CRC removed from the IDAT chunk
- premultiplied alpha (color' = color * alpha / 255)
明顯的改動就是在IHDR塊之前插入了CgBI塊來表示這種格式,同時修改了IDAT塊中的資料,原因就是在iPhone中,影象是以BGRA格式在記憶體中處理的,到這裡就可以發現,其實這個所謂的Compress PNG Files,最主要的目的並不是壓縮圖片的大小,而是將圖片轉換成iPhone能更方便處理的格式,加快處理速度。
接下來就要解釋下為什麼LUT這種圖片的大小會變化這麼大了,首先要看下Compress後的PNG的資料格式,這裡通過hexdump可以查詢到各個資料塊的關鍵字
這裡推薦一個檢視CgBI檔案資料的命令列工具,pngdefry,這個工具可以用來檢視CgBI檔案資訊,同時可以用來將CgBI檔案還原成png檔案,當然這裡還原之後的png檔案和原始檔案還是會有區別的,這裡後續會有提到。
可以發現,這處理增加了CgBI資料塊之外,還增加了一個iDot資料塊,這裡資料塊是Apple自定義的資料塊,在網上也沒有找到相關的文件,所以暫時無法分析其作用,變化最大的就是IDAT資料塊了,IDAT資料塊中存放的是實際的影象資料,由於png本身就是一種壓縮格式,所以IDAT資料塊的資料本身就是經過壓縮的,其大小取決於Filter方式、zlib的壓縮方式等等。
關於filter方式http://www.libpng.org/pub/png/book/chapter09.html這篇文章講的非常詳細,有興趣的同學可以看下。而Apple預設的filter方式就是filter0,也就是None這種方式,不同的filter方式對資料的壓縮性是有很大影響的,同樣是這張圖片,使用不用的filter方式處理之後的大小如下:
不同的filter處理之後的圖,放到實際工程中使用的話,效果上我測試下來是沒有什麼區別的,也就是說這幾種型別,iPhone都可以解碼,不過可以發現,即使是最小的檔案也是217KB,還是比原始檔207KB要大一些,這又是為什麼呢?
xcrun -sdk iphoneos pngcrush -revert-iphone-optimizations lutf0.png lut_revert.png
通過上面的命令,我們可以將圖片還原成正常的png格式,然而還原出來的png圖片是246KB,比原來的207KB還是大了不少,通過pngcheck,我們可以發現原因:
因為CgBI的IDAT是BGRA格式的,所以不管之前的IDAT是否有Alpha通道,在處理的時候,都會增加alpha通道,其次就是因為每一行資料的filter不同,apple處理的時候,預設每一行都使用相同的filter,而原始檔案則可以通過更好的演算法,對不同的資料行使用不同的filter,為後面的資料壓縮提供更容易壓縮的資料。
以上就是對Compress PNG Files這個編譯選項的一些分析,當然並不是所有圖片經過處理都會變得巨大,也有些圖片會變小的,所以當Compress PNG Files這個屬性已經開啟,並且不確定關閉會不會對現有工程產生大量影響的時候,可以通過如下操作將這張圖片排除出compress的方法,尤其是對於LUT圖片:
修改為:
參考文獻:
【1】http://iphonedevwiki.net/index.php/CgBI_file_format
【2】https://www.cnblogs.com/lidabo/p/3701197.html
作者簡介:ash, 天天P圖iOS工程師
文章後記: 天天P圖是由騰訊公司開發的業內領先的影象處理,相機美拍的APP。歡迎掃碼或搜尋關注我們的微信公眾號:“天天P圖攻城獅”,那上面將陸續公開分享我們的技術實踐,期待一起交流學習!
加入我們: 天天P圖技術團隊長期招聘: (1) AND / iOS 開發工程師 (2) 影象處理演算法工程師 期待對我們感興趣或者有推薦的技術牛人加入我們(base 上海)!聯絡方式:[email protected]