前言:
我們?cè)赑C端用瀏覽器看圖片的時(shí)候,經(jīng)常是先看到一張模糊圖,然后再漸漸的變得清晰,這種情況在看漫畫(huà)的時(shí)候尤其常見(jiàn)(模糊圖如下),這種效果就叫做漸進(jìn)式加載.漸進(jìn)式加載能夠大大的提升體驗(yàn)感,我們先來(lái)了解一下漸進(jìn)式加載的原理.
(圖片來(lái)自網(wǎng)絡(luò))
1.JPEG
要做到漸進(jìn)式加載,我們的圖片需要是JPEG格式,而JPEG格式的圖片又分為兩種,我們要做到漸進(jìn)式加載的話,需要的是Progressive JPEG.
(1)Baseline JPEG(標(biāo)準(zhǔn)型)
這種格式的圖片在保存信息的時(shí)候,是從上往下,將每一行的數(shù)據(jù)順序的保存起來(lái)的,所以讀一部分就展示的話,那么效果就會(huì)像是從上往下一點(diǎn)一點(diǎn)展示.
(圖片來(lái)自網(wǎng)絡(luò))
(2)Progressive JPEG(漸進(jìn)式)
這種格式的圖片在保存信息的時(shí)候,是一幀一幀的存儲(chǔ)的,如果逐幀逐幀的讀的話,就會(huì)先看到模糊圖,然后一點(diǎn)一點(diǎn)變清晰
(圖片來(lái)自網(wǎng)絡(luò))
(圖片來(lái)自網(wǎng)絡(luò))
2.解碼
如何判斷是否JPEG格式的圖片呢?下面引用一段Glide框架的代碼
//ImageHeaderParser.java
private static final int EXIF_MAGIC_NUMBER = 0xFFD8;
// JPEG.
if (firstTwoBytes == EXIF_MAGIC_NUMBER) {
return JPEG;
}
我們可以看出,JPEG是以FFD8開(kāi)頭的
其實(shí)JPEG是以FFD8開(kāi)頭,FFD9結(jié)尾,FFDA代表一個(gè)幀的開(kāi)頭
FFD8 ... FFDA ... FFDA ... FFDA ... FFD9
Baseline JPEG 里面只有一個(gè)FFDA
Progressive JPEG 里面含有多個(gè)FFDA
比較完整的數(shù)據(jù)結(jié)構(gòu)如下
(圖片來(lái)自Wiki)
https://en.wikipedia.org/wiki/JPEG
3.如何保存或者轉(zhuǎn)換成JPEG
(以下轉(zhuǎn)換方法來(lái)自網(wǎng)絡(luò),由于非java代碼,所以沒(méi)有做驗(yàn)證,特此說(shuō)明一下)
1峭梳、PhotoShop
在photoshop中有“存儲(chǔ)為web所用格式”,打開(kāi)后選擇“連續(xù)”就是漸進(jìn)式JPEG。
2摘昌、Linux
檢測(cè)是否為progressive jpeg : identify -verbose filename.jpg | grep Interlace(如果輸出 None 說(shuō)明不是progressive jpeg谬盐;如果輸出 Plane 說(shuō)明是 progressive jpeg。)
將basic jpeg轉(zhuǎn)換成progressive jpeg:> convert infile.jpg -interlace Plane outfile.jpg
3、PHP
使用imageinterlace和imagejpeg函數(shù)我們可以輕松解決轉(zhuǎn)換問(wèn)題轴踱。
<?php
$im = imagecreatefromjpeg('pic.jpg');
imageinterlace($im, 1);
imagejpeg($im, './php_interlaced.jpg', 100);
imagedestroy($im);
?>
4、Python
import PIL
from exceptions import IOError
img = PIL.Image.open("c:\\users\\biaodianfu\\pictures\\in.jpg")
destination = "c:\\users\\biaodianfu\\pictures\\test.jpeg"
try:
img.save(destination, "JPEG", quality=80, optimize=True, progressive=True)
except IOError:
PIL.ImageFile.MAXBLOCK = img.size[0] * img.size[1]
img.save(destination, "JPEG", quality=80, optimize=True, progressive=True)
5谚赎、jpegtran
jpegtran -copy none -progressive <inputfile> <outputfile>
6淫僻、C#
using (Image source = Image.FromFile(@"D:\temp\test2.jpg")) {
ImageCodecInfo codec = ImageCodecInfo.GetImageEncoders().First(c => c.MimeType == "image/jpeg");
EncoderParameters parameters = new EncoderParameters(3);
parameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
parameters.Param[1] = new EncoderParameter(System.Drawing.Imaging.Encoder.ScanMethod, (int)EncoderValue.ScanMethodInterlaced);
parameters.Param[2] = new EncoderParameter(System.Drawing.Imaging.Encoder.RenderMethod, (int)EncoderValue.RenderProgressive);
source.Save(@"D:\temp\saved.jpg", codec, parameters);
}
4.效果
明白了漸進(jìn)式加載的原理后,我們就能想辦法在app端也做到漸進(jìn)式加載的效果了.
(大概就是判斷是否JPEG圖片,然后根據(jù)每一幀的節(jié)點(diǎn)來(lái)判斷并決定是否需要加載)
下面展示一下效果圖
(1)原圖
(Progressive JPEG的圖一打水印就變成Baseline JPEG,應(yīng)該是CSDN打水印保存的時(shí)候處理了)
(2)解碼到第一個(gè)FFDA與第二個(gè)FFDA的中間
(3)剛好解碼到第二個(gè)FFDA
(4)解碼到第五個(gè)FFDA
需要看圖片二進(jìn)制結(jié)構(gòu)的,可以下載一些工具(如hex-editor-neo)
hex-editor-neo下載
在后面的文章里面我們將具體講解如何在app端做漸進(jìn)式加載