Chrome圖片解碼與ImageDecodingHint

rogeryi發表於2018-10-16

我在之前的一篇文章Chrome 圖片解碼與 Image.decode API,說明了為什麼圖片解碼可能會導致非合成器動畫的阻塞和如何使用 Image.decode API 來避免動畫的阻塞。不過雖然 Image.decode API 給頁端提供了更靈活的控制圖片解碼時機的能力,但是使用起來較為複雜,也容易誤用,而 Image Decoding Hint 屬性則更簡單,容易使用,同時也滿足了大部分場景下的效能需求。

Chrome 從 65 版本開始支援 Image Decoding Hint,其它瀏覽器 Firefox,Edge 也是支援的,Safari 年初時的狀態是開發中,不清楚現在是否已經支援。

以下描述來自於 HTML 規範文件

In order to aid the user agent in deciding whether to perform synchronous or asynchronous decode, the decoding attribute can be set on img elements. The possible values of of the decoding attribute are the following image decoding hint keywords:

sync: Indicates a preference to decode this image synchronously for atomic presentation with other content.
async: Indicates a preference to decode this image asynchronously to avoid delaying presentation of other content.
auto: Indicates no preference in decoding mode (the default).

直接翻譯過來就是 —— 頁端可以通過 Image.decoding 屬性來控制圖片解碼的行為,sync 會強制同步解碼,圖片會和其它內容同時顯示,async 則強制非同步解碼,可能會造成圖片和其它內容不同時顯示,預設值 auto 則讓瀏覽器自己來決定。

上述描述的“和其它內容同時顯示或者不同時顯示”究竟是什麼意思呢?這個跟瀏覽器合成器的光柵化策略有關,當圖片元素和其它元素同時進入當前網頁的可見區域,或者已經在可見區域內並且內容同時發生變化,合成器需要安排這些元素所在的圖層光柵化,並且在所有發生變更的圖層都光柵化完畢之後再顯示完整的一幀(請參考瀏覽器渲染流水線解析與網頁動畫效能優化對非合成器動畫渲染流水線的解釋)。

在不支援 Image Decoding Hint 之前,Chrome 會強制圖片先解碼,然後再光柵化,這樣保證了包括圖片在內的所有同時變更的元素都是同時顯示的,也就是所謂的 atomic presentation。而在支援 Image Decoding Hint 之後,Chrome 的光柵化策略就會變為:

  1. 如果 Decoding Hint 是 sync,則跟之前是一樣的;
  2. 如果 Decoding Hint 是 async,則圖片不會在光柵化之前解碼,而是放到另外一個後臺 Worker 執行緒解碼,並在圖片解碼完成後重新光柵化圖片所在區域並重繪視窗。這樣造成的結果就是在圖片所在區域光柵化的時候可能因為圖片還未完成解碼而無法繪製圖片導致空白,跟其它非圖片元素的顯示不完全同步,也就是說如果有圖片元素和非圖片元素同時發生變更,非圖片元素可能會提前顯示;
  3. 如果 Decoding Hint 是 auto,目前 Chrome 的處理是等同於 sync;

即使設定成 async,Chrome 還是會根據一些條件來決定最後的結果是 sync 還是 async,如果執行裝置,網頁和圖片滿足下面條件,最後還是會被強制為 sync:

  1. 如果圖片還未下載完畢;
  1. 或者是 gif 這樣的動圖;
  2. 或者在 Chrome for Android 或者 Android WebView 上裝置不支援 GPU 光柵化,或者網頁無法開啟 GPU 光柵化;
  3. 或者在 Chrome for Android 或者 Android WebView 上解碼後佔用記憶體小於 512k(相當於解析度小於 512 x 256),桌面版本為 1024k;
  4. 或者解碼後佔用記憶體大於合成器的解碼快取池的上限(也就是說小圖片和超大圖片,都強制為同步解碼,無論是什麼設定);

在 AMP 裡面的 amp-img 自定義元素,AMP-runtime 在生成對應的 img 元素時,會指定 decoding 屬性為 async,在現實中,大部分情況下指定為 async 應該都是合理的。

Chrome 圖片解碼與 Image.decode API之前演示過的一個例程,如果我們修改會導致動畫阻塞的 sync.html 的程式碼如下:

function prepareImage() {
  var img = new Image();
  img.src = "nebula.jpg";
  img.decoding = "async";
  img.onload = function() { document.body.appendChild(img); };
}

上述程式碼增加了一句 img.decoding = “async”;,其執行的結果跟另外一個使用 Image.decode API 的 async.html 就基本一樣了,時針的動畫不會被阻塞,並且在實際的應用中要比使用 Image.decode API 簡單的多。(上面的程式碼 async 不會在 Chrome for Android 或者 Android WebView 上生效,是因為網頁無法開啟 GPU 光柵化)


相關文章