Regressive JPEGs:透過漸進式 JPEG 掃描建立動畫

Regressive JPEGs:透過漸進式 JPEG 掃描建立動畫

利用漸進式 JPEG 掃描進行動畫製作

透過操縱漸進式 JPEG 的結構方式,可以建立一個在載入過程中會在不同圖像之間切換的單一圖像檔案。這是因為漸進式 JPEG 的設計初衷是先儲存低頻成分,讓瀏覽器能夠先渲染出低解析度的預覽圖,並隨著更多數據的到達而逐漸精細化。藉由串聯多張圖像並移除特定的標記(markers),開發者可以強迫解碼器用新的圖像內容覆蓋先前渲染的數據。

漸進式 JPEG 的運作原理

標準的漸進式 JPEG 將壓縮數據拆分為多個「掃描」(scans),每個掃描都帶有一個標頭。這些掃描允許圖像在下載過程中以低解析度顯示,而不是從上到下被切斷。

掃描的結構

典型的掃描標頭包含:

  • Start of Scan (SOS) Marker: FF DA
  • Length Field: 一個大端序(big-endian)值,表示掃描的大小。
  • Channel Information: 通道數量(通常為 YCbCr)及其相關的 Huffman 表索引。
  • Spectral Range: 起始與結束的離散餘弦變換(DCT)分箱(bins)。例如,僅包含 DC (0) 分箱的掃描提供最低頻率成分。
  • Precision: 精細度(例如,半精度或全精度)。

色彩空間與亮度

JPEG 使用 YCbCr 色彩空間而非 RGB。亮度(Y)因品質需求而獲得優先權,因為人類眼睛對亮度的敏感度高於色彩。色度(Cb 和 Cr)可以以較低解析度儲存(半解析度/四分之一像素數量),這意味著完整的色度數據通常佔用的空間比亮度數據更少。

實作「回歸式」JPEG

為了建立會變動內容的圖像,作者串聯了多張解析度相同的圖像,並過濾掉了 Start-of-Image (SOI)、Start-of-Frame (SOF) 以及 End-of-Image (EOI) 標記。

克服解碼器限制

大多數瀏覽器解碼器會限制掃描的數量,以防止「zip bomb」風格的資源耗盡。直接串聯完整的漸進式 JPEG 通常在經過大約九幀之後就會失敗。

為了增加幀數(在 Chrome 中最高可達 90 幀),作者透過使用 DC-only scans 優化了每一幀。在漸進式模式下,一個掃描不能同時包含 AC(分箱大於 0)和 DC(分箱為 0)數據。藉由建立僅由單一 DC-only scan 組成的幀,作者達成了兩個目標:

  1. 增加幀數:減少每幀的掃描數量,讓更多幀能符合解碼器的限制。
  2. 消除殘影:因為 AC 掃描旨在精細化現有數據,使用僅 DC 掃描可以防止新幀與前一幀混合。

生成方法

可以使用 jpegtran 透過指定僅包含 Y、Cb 和 Cr 通道的 DC 分箱掃描配置來生成 DC-only 幀:

cat > frame.scans << EOF
# DC only scan: 0,1,2:0-0,0,0; # and nothing else
EOF
jpegtran -scans frame.scans -outfile out.jpg in.jpg

實務上的限制與瀏覽器行為

由於 JPEG 格式不包含時間資訊,這些「動畫」的播放速度完全取決於網路下載速度。

瀏覽器相容性

  • Chrome: 在停止前大約會渲染約 90 幀。
  • Firefox: 通常對高數量的掃描比 Chrome 更有耐心。
  • Safari: 據報導會卡在原地直到圖像完全下載完成,從而使效果失效。

針對時間控制的潛在解決方案

社群討論建議了幾種模擬受控播放的方法:

  • Server-side Timing: 網頁伺服器可以將圖像數據以定時分塊的方式傳送給用戶端。
  • Service Workers: 可以使用 Service Worker 來模擬慢速連接,以控制掃描的傳遞。

社群技術洞察

社群專家指出,雖然漸進式 JPEG 對感知載入時間有益,但它們可能會引入解碼開銷。一位用戶提到,啟用漸進式模式在配合使用 JPEG Turbo 和 OpenGL 等函式庫時,會顯著降低解碼速度,這暗示了在現代高速網路環境下,漸進式構建的視覺效益已不再那麼重要。

Sources