引言
在上篇重拾Android之路之圖片(高效加載大圖、多圖解決方案呐萨,有效避免程序OOM)中,我提到了移動端加載圖片網(wǎng)絡(luò)資源該選擇什么樣的格式。
本節(jié)轉(zhuǎn)自https://blog.ibireme.com/2015/11/02/mobile_image_benchmark/
摘取些節(jié)點
來跟大家分享谴分。
摘錄
圖片通常是移動端流量耗費最多的部分,并且占據(jù)著重要的視覺空間镀脂。合理的圖片格式選用和優(yōu)化可以為你節(jié)省帶寬牺蹄、提升視覺效果。在這里我會分析一下目前主流和新興的幾種圖片格式的特點薄翅、性能分析沙兰、參數(shù)調(diào)優(yōu)氓奈,以及相關(guān)開源庫的選擇。
幾種圖片格式的簡介
首先談一下大家耳熟能詳?shù)膸追N老牌的圖片格式吧:
JPEG
是目前最常見的圖片格式鼎天,它誕生于 1992 年舀奶,是一個很古老的格式。它只支持有損壓縮
训措,其壓縮算法可以精確
控制壓縮比
伪节,以圖像質(zhì)量換得存儲空間。由于它太過常見绩鸣,以至于許多移動設(shè)備的 CPU 都支持針對它的硬編碼與硬解碼怀大。
PNG
誕生在 1995 年,比 JPEG 晚幾年呀闻。它本身的設(shè)計目的是替代 GIF
格式化借,所以它與 GIF 有更多相似的地方。PNG 只支持無損壓縮
捡多,所以它的壓縮比是有上限的蓖康。相對于 JPEG 和 GIF 來說,它最大的優(yōu)勢在于支持完整的透明通道垒手。
GIF
誕生于 1987 年蒜焊,隨著初代互聯(lián)網(wǎng)流行開來。它有很多缺點科贬,比如通常情況下只支持 256 種顏色泳梆、透明通道只有 1 bit、文件壓縮比不高榜掌。它唯一的優(yōu)勢就是支持多幀動畫优妙,憑借這個特性,它得以從 Windows 1.0 時代流行至今憎账,而且仍然大受歡迎套硼。
在上面這些圖片格式誕生后,也有不少公司或團體嘗試對他們進行改進胞皱,或者創(chuàng)造其他更加優(yōu)秀的圖片格式邪意,比如 JPEG 小組的 JPEG 2000、微軟的 JPEG-XR反砌、Google 的 WebP抄罕、個人開發(fā)者發(fā)布的 BPG、FLIF 等于颖。它們相對于老牌的那幾個圖片格式來說有了很大的進步,但出于各種各樣的原因嚷兔,只有少數(shù)幾個格式能夠流行開來森渐。下面三種就是目前實力比較強的新興格式了:
APNG
是 Mozilla 在 2008 年發(fā)布的一種圖片格式做入,旨在替換掉畫質(zhì)低劣的 GIF 動畫。它實際上只是相當(dāng)于 PNG 格式的一個擴展同衣,所以 Mozilla 一直想把它合并到 PNG 標(biāo)準里面去竟块。然而 PNG 開發(fā)組并沒有接受 APNG 這個擴展,而是一直在推進它自己的 MNG 動圖格式耐齐。MNG 格式過于復(fù)雜以至于并沒有什么系統(tǒng)或瀏覽器支持浪秘,而 APNG 格式由于簡單容易實現(xiàn),目前已經(jīng)漸漸流行開來埠况。Mozilla 自己的 Firefox 首先支持了 APNG耸携,隨后蘋果的 Safari 也開始有了支持, Chrome 目前也已經(jīng)嘗試開始支持 辕翰,可以說未來前景很好夺衍。
WebP
是 Google 在 2010 年發(fā)布的圖片格式,希望以更高的壓縮比替代 JPEG喜命。它用 VP8 視頻幀內(nèi)編碼作為其算法基礎(chǔ)沟沙,取得了不錯的壓縮效果。它支持有損和無損壓縮壁榕、支持完整的透明通道矛紫、也支持多幀動畫,并且沒有版權(quán)問題牌里,是一種非常理想的圖片格式颊咬。借由 Google 在網(wǎng)絡(luò)世界的影響力,WebP 在幾年的時間內(nèi)已經(jīng)得到了廣泛的應(yīng)用二庵√叭荆看看你手機里的 App:微博、微信催享、QQ杭隙、淘寶、網(wǎng)易新聞等等因妙,每個 App 里都有 WebP 的身影痰憎。Facebook 則更進一步,用 WebP 來顯示聊天界面的貼紙動畫攀涵。
BPG
是著名程序員 Fabrice Bellard 在去年 (2014年) 發(fā)布的一款超高壓縮比的圖片格式铣耘。這個程序員有些人可能感覺面生,但說起他的作品 FFmpeg以故、QEMU 大家想必是都知道的蜗细。BPG 使用 HEVC (即 H.265) 幀內(nèi)編碼作為其算法基礎(chǔ),就這點而言,它毋庸置疑是當(dāng)下最為先進的圖片壓縮格式炉媒。相對于 JP2踪区、JPEG-XR、WebP 來說吊骤,同等體積下 BPG 能提供更高的圖像質(zhì)量缎岗。另外,得益于它本身基于視頻編碼算法的特性白粉,它能以非常小的文件體積保存多幀動畫传泊。 Fabrice Bellard 聰明的地方在于,他知道自己一個人無法得到各大瀏覽器廠商的支持鸭巴,所以他還特地開發(fā)了 Javascript 版的解碼器眷细,任何瀏覽器只要加載了這個 76KB 大小的 JS 文件,就可以直接顯示 BPG 格式的圖片了奕扣。目前阻礙它流行的原因就是 HEVC 的版權(quán)問題和它較長的編碼解碼時間薪鹦。盡管這個圖片格式才剛剛發(fā)布一年,但已經(jīng)有不少廠子開始試用了惯豆,比如阿里和騰訊池磁。
移動端圖片類型的支持情況
目前主流的移動端對圖片格式的支持情況如何呢?我們分別來看一下 Android 和 iOS 目前的圖片編解碼架構(gòu)吧:
Android 的圖片編碼解碼是由 Skia 圖形庫負責(zé)的楷兽,Skia 通過掛接第三方開源庫實現(xiàn)了常見的圖片格式的編解碼支持地熄。目前來說,Android 原生支持的格式只有 JPEG
芯杀、PNG
端考、GIF
、BMP
和 WebP
(Android 4.0 加入)揭厚,在上層能直接調(diào)用的編碼方式也只有 JPEG却特、PNG、WebP 這三種筛圆。目前來說 Android 還不支持直接的動圖編解碼裂明。
iOS 底層是用 ImageIO.framework 實現(xiàn)的圖片編解碼。目前 iOS 原生支持的格式有:JPEG太援、JPEG2000闽晦、PNG、GIF提岔、BMP仙蛉、ICO、TIFF碱蒙、PICT荠瘪,自 iOS 8.0 起,ImageIO 又加入了 APNG、SVG哀墓、RAW 格式的支持鞭莽。在上層,開發(fā)者可以直接調(diào)用 ImageIO 對上面這些圖片格式進行編碼和解碼麸祷。對于動圖來說,開發(fā)者可以解碼動畫 GIF 和 APNG褒搔、可以編碼動畫 GIF阶牍。
兩個平臺在導(dǎo)入第三方編解碼庫時,都多少對他們進行了一些修改星瘾,比如 Android 對 libjpeg 等進行調(diào)整以更好的控制內(nèi)存走孽,iOS 對 libpng 進行了修改以支持 APNG,并增加了多線程編解碼的特性琳状。除此之外磕瓷,iOS 專門針對 JPEG 的編解碼開發(fā)了 AppleJPEG.framework,實現(xiàn)了性能更高的硬編碼和硬解碼念逞,只有當(dāng)硬編碼解碼失敗時困食,libjpeg 才會被用到。
選型
在移動端翎承,圖片一直是流量大頭硕盹,一個簡單的運營網(wǎng)頁,圖片大小動不動就以MB為單位叨咖,為了加快網(wǎng)頁呈現(xiàn)的速度瘩例,我們必須使用最適合圖片質(zhì)量,這里所說的合適指圖片的清晰度和大小達到合格的要求甸各。
前端常常會碰到這種情況垛贤,一個網(wǎng)頁都是圖片,需要你壓縮圖片到適合的分辨率趣倾,分辨率低了容易失真用戶體驗不好聘惦,高了圖片質(zhì)量太大導(dǎo)致加載慢,所以經(jīng)常會找一個合適的臨界點來選擇圖片的分辨率誊酌。
我們選擇了某個分辨率來作為合適臨界點部凑,卻發(fā)現(xiàn)圖片依然很大,希望它可以再小些碧浊,給用戶更快的呈現(xiàn)速度涂邀。對于 JPEG、PNG 和 GIF 這些圖片格式的優(yōu)化幾乎已經(jīng)達到了極致箱锐, 若想改變現(xiàn)狀開辟新局面比勉,便要有釜底抽薪的膽量和氣魄,而 Google 給了我們一個新選擇:WebP。
什么是webP?
WebP(發(fā)音 weppy)浩聋,是一種支持有損壓縮
和無損壓縮
的圖片文件格式
观蜗,派生自圖像編碼格式 VP8。根據(jù) Google 的測試衣洁,無損壓縮后的 WebP 比 PNG 文件少了 45% 的文件大小墓捻,即使這些 PNG 文件經(jīng)過其他壓縮工具壓縮之后,WebP 還是可以減少 28% 的文件大小坊夫。相比JPEG文件“在質(zhì)量相同的情況下砖第,WebP格式圖像的體積要比JPEG格式圖像小40%。2010 年發(fā)布的 WebP 已經(jīng)不算是新鮮事物了环凿,在Google 的明星產(chǎn)品如 Youtube梧兼、Gmail、Google Play 中都可以看到 WebP 的身影智听,而 Chrome 網(wǎng)上商店甚至已完全使用了 WebP羽杰。國外公司如 Facebook、ebay 和國內(nèi)公司如騰訊到推、淘寶考赛、美團等也早已嘗鮮。目前 WebP 也在我廠很多的項目中得到應(yīng)用环肘,如騰訊新聞客戶端欲虚、騰訊網(wǎng)、QQ空間等悔雹,同時也有一些針對 WebP 的圖片格式轉(zhuǎn)換工具复哆,如智圖,iSparta(http://isparta.github.io/)等腌零。
知道了webP
圖片格式梯找,我們再來看以下兩組數(shù)據(jù):
webP支持情況:
安卓手機系統(tǒng)分布:
從上面的兩組數(shù)據(jù)我們可以得到:
1: webP在安卓4.3以上瀏覽器中已經(jīng)完全支持,其中4.0以上部分大部分支持(這組數(shù)據(jù)為展示益涧,如有需要查詢它的兼容性可以定位這個網(wǎng)站:http://caniuse.com/#search=webp)
2:安卓系統(tǒng)大于4.0占95.7%锈锤,大于等于4.3的占64.7%,從這可觀的數(shù)據(jù)來說我們?yōu)槭裁床贿x擇webP的格式來加載圖片闲询。
說到這里久免,可能有人開始要噴水了,為了圖片極致加載扭弧,你放棄了部分用戶阎姥,導(dǎo)致這部分用戶連你的圖片都無法顯示,這個方式根本不行鸽捻,我們需要的是在圖片完全兼容的條件下呼巴,再選擇最優(yōu)圖片格式泽腮。的確,我們不應(yīng)該為了最優(yōu)圖片格式衣赶,而放棄圖片兼容性诊赊,那么這個問題就需要我們給出一個解決方案。我們是不是可以通用用戶的系統(tǒng)版本來選擇使用哪種圖片府瞄?這個回答是肯定的碧磅,我們完全可以通過判斷用戶的系統(tǒng)版本來選擇加載哪種類型的圖片格式。
navigator.userAgent
JS的這個方法作為前端我們再熟悉不過了遵馆。Chrome瀏覽器在移動端調(diào)試環(huán)境下console中輸入:
var userAgent = navigator.userAgent;
alert(userAgent);
不同瀏覽器中续崖,彈窗內(nèi)容都不相同,其中內(nèi)容數(shù)據(jù)所代表什么意思团搞,這里不再做解釋,如有不懂的可以訪問這個地址多艇,有很好的解釋:http://www.jb51.net/article/48532.htm逻恐;
如何使用webP圖片
用戶的版本我能已經(jīng)獲得了,那么什么情況下使用webP圖片的格式峻黍,是不是也可以通過JS來判斷呢复隆。
這里隨便code舉個例子:
html:
<body>
<img src="" alt="" data-url="11.jpg" data-original="11.webp"/>
<img src="" alt="" data-url="21.jpg" data-original="21.webp"/>
</body>
js:
var userAgent = navigator.userAgent;
var Android = userAgent.indexOf("Android");
var AppleWebKit=userAgent.indexOf('AppleWebKit');
var androidVersion = parseFloat(userAgent.slice(Android+8));
var $img=document.getElementsByTagName('img');
window.onload= function () {
if(Android >= 0 && AppleWebKit>=0&&androidVersion>=4){
forImg('data-original');
}else{
forImg('data-url');
}
}
function forImg(data){
for(var i=0;i<$img.length;i++){
$img[i].setAttribute('src',$img[i].getAttribute(data));
}
}
這段代碼用于測試。
結(jié)果自己通過控制臺去查看姆涩,可以選擇控制臺中的network來對比加載的時間挽拂。
圖片優(yōu)化的拓展
有關(guān)圖片的優(yōu)化,通常我們會用到LruCache(使用軟引用骨饿、強制回收的辦法)亏栈,會用到SoftReference(使用url做key,bitmap做value的方法)宏赘,會用到根據(jù)手機屏幕來縮放圖片绒北,會及時回收圖片所占用的內(nèi)存等方法。但說實在的察署,這些方法治標(biāo)不治本闷游,圖片該多大還多大,從軟件上我們基本上能做到處理圖片的極限贴汪,那么只剩下考慮從硬件來上優(yōu)化圖片脐往,這就講到了今天所要說的webp。
其中webp不僅僅能應(yīng)用在Android上扳埂,同樣IOS和web端也同樣可以使用业簿。
有關(guān)webp的簡介,騰訊同學(xué)有詳細介紹聂喇,濃縮的精華辖源!從零開始帶你認識最新的圖片格式WEBP蔚携,我不再多說。
一張279k的png圖片可以轉(zhuǎn)換成67.5k的webp圖片克饶,而且不失真
step_1
添加webp支持酝蜒,添加so包和lib包
step_2
添加WebpUtils文件,里面有通過so包來處理webp文件成為byte數(shù)組的方法
step_3
應(yīng)用
效果圖:
附安卓SDK文檔給出的官方壓縮圖片算法:
public static Bitmap getBitmapBySize(String path, int width, int height) {
BitmapFactory.Options option = new BitmapFactory.Options();
option.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, option);
option.inSampleSize = computeSampleSize(option, -1, width * height);
option.inJustDecodeBounds = false;
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeFile(path, option);
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
public static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels);
int roundedSize;
if (initialSize <= 8) {
roundedSize = 1;
while (roundedSize < initialSize) {
roundedSize <<= 1;
}
} else {
roundedSize = (initialSize + 7) / 8 * 8;
}
return roundedSize;
}
private static int computeInitialSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
double w = options.outWidth;
double h = options.outHeight;
int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(Math.floor(w / minSideLength), Math.floor(h / minSideLength));
if (upperBound < lowerBound) {
// return the larger one when there is no overlapping zone.
return lowerBound;
}
if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
return 1;
} else if (minSideLength == -1) {
return lowerBound;
} else {
return upperBound;
}
}