前言:
我們在PC端用瀏覽器看圖片的時候,經常是先看到一張模糊圖,然後再漸漸的變得清晰,這種情況在看漫畫的時候尤其常見(模糊圖如下),這種效果就叫做漸進式載入.漸進式載入能夠大大的提升體驗感,我們先來了解一下漸進式載入的原理.
(圖片來自網路)
1.JPEG
要做到漸進式載入,我們的圖片需要是JPEG格式,而JPEG格式的圖片又分為兩種,我們要做到漸進式載入的話,需要的是Progressive JPEG.
(1)Baseline JPEG(標準型)
這種格式的圖片在儲存資訊的時候,是從上往下,將每一行的資料順序的儲存起來的,所以讀一部分就展示的話,那麼效果就會像是從上往下一點一點展示.
(圖片來自網路)
(2)Progressive JPEG(漸進式)
這種格式的圖片在儲存資訊的時候,是一幀一幀的儲存的,如果逐幀逐幀的讀的話,就會先看到模糊圖,然後一點一點變清晰
(圖片來自網路)
(圖片來自網路)
2.解碼
如何判斷是否JPEG格式的圖片呢?下面引用一段Glide框架的程式碼
//ImageHeaderParser.java
private static final int EXIF_MAGIC_NUMBER = 0xFFD8;
// JPEG.
if (firstTwoBytes == EXIF_MAGIC_NUMBER) {
return JPEG;
}複製程式碼
我們可以看出,JPEG是以FFD8開頭的
其實JPEG是以FFD8開頭,FFD9結尾,FFDA代表一個幀的開頭
FFD8 ... FFDA ... FFDA ... FFDA ... FFD9複製程式碼
Baseline JPEG 裡面只有一個FFDA
Progressive JPEG 裡面含有多個FFDA
比較完整的資料結構如下
(圖片來自Wiki)
en.wikipedia.org/wiki/JPEG
3.如何儲存或者轉換成JPEG
(以下轉換方法來自網路,由於非java程式碼,所以沒有做驗證,特此說明一下)
1、PhotoShop
在photoshop中有“儲存為web所用格式”,開啟後選擇“連續”就是漸進式JPEG。
2、Linux
檢測是否為progressive jpeg : identify -verbose filename.jpg | grep Interlace(如果輸出 None 說明不是progressive jpeg;如果輸出 Plane 說明是 progressive jpeg。)
將basic jpeg轉換成progressive jpeg:> convert infile.jpg -interlace Plane outfile.jpg
3、PHP
使用imageinterlace和imagejpeg函式我們可以輕鬆解決轉換問題。
<?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.效果
明白了漸進式載入的原理後,我們就能想辦法在app端也做到漸進式載入的效果了.
(大概就是判斷是否JPEG圖片,然後根據每一幀的節點來判斷並決定是否需要載入)
下面展示一下效果圖
(1)原圖
(Progressive JPEG的圖一打水印就變成Baseline JPEG,應該是CSDN打水印儲存的時候處理了)
(2)解碼到第一個FFDA與第二個FFDA的中間
(3)剛好解碼到第二個FFDA
(4)解碼到第五個FFDA
需要看圖片二進位制結構的,可以下載一些工具(如hex-editor-neo)
hex-editor-neo下載
在後面的文章裡面我們將具體講解如何在app端做漸進式載入