Skip to content
All writing Part 05 of 07 · 裁切、縮放、填滿:圖片處理
Engineering · 1 min read

libvips 把動圖存成一條上下疊起來的長條

libvips 不把多影格圖片存成 N 張影格,而是疊成一條很高的 page-strip:每格上下堆疊,page-height、n-pages、delay 全記在 metadata 裡。

libvips 處理任何多影格圖片(animated GIF/WebP、multi-page TIFF/PDF)時,不是存成 N 張獨立影格,而是把每一格上下疊成一張很高的圖,官方叫 page-strip,我習慣想成一捲攤開的衛生紙。

  • 結構藏在 metadata:page-height(一格的高)、n-pages(幾格)、delay(每格毫秒)。恆等式:n-pages × page-height = 總高
  • 所以 imageRef.Height() 回的是整條的高(全部影格加起來),而 Pages() > 1 就是「我手上是一條長條、不是一張靜圖」的旗標。

我原本猜這些影格尺寸各不相同,結果剛好相反:每格一模一樣大,正因為一樣大,libvips 才能整齊疊成一條。判斷是不是動圖,我就是用 Pages() > 1

影格在長條裡的順序

影格由上往下、照播放順序疊:frame 0 在最上面(y = 0),下一格緊接在下面。把「衛生紙」從上往下攤開,拿到的是 frame 0→4,不是反過來。

one *vips.ImageRef — Height() = 5 × page-height (ph) frame 0 frame 1 frame 2 frame 3 frame 4 頂端 ← 播放起點 底端 ← 最後一格 y = 0 y = 5·ph = Height() Pages() = 5 · PageHeight() = ph · Pages() > 1 ⇒ 是長條,不是靜圖
i 格位在 top = i × page-height——要裁出一格就從這裡切。

要抽第 i 格,就從 y = i × page-height 裁:frame 0 在 y=0、frame 4 在 y=4·ph。這也證明了它是由上往下、不是由下往上。

動圖在 libvips 眼裡是一張高圖,不是一疊影格。


參考來源:

Related: 這正是一條載入路徑,同時吃靜圖與動圖均勻變換 vs 定位變換:在拼接長條上的差別成立的基礎,或回到裁切、縮放、填滿的四堂課總覽

Tags #image-processing #libvips
// connect

Be brave | Be wise | Be grateful

21 BreakinCode

// elsewhere
LinkedInMedium (lang: en)Youtube
wh:~$William Hung· © 2026 Taipei · GMT+8 · Available for collaboration