abox.tools

語言: 繁體中文

一個 GIF 裡到底裝著什麼

GIF 是一疊長方形,每個都帶著一隻計時器和一張調色盤,而人們對這個格式的抱怨,幾乎都出自這三樣裡的某一樣。下面講每個部分各做什麼,以及怎麼弄清楚你的檔案把體積花在了哪一樣上。

開啟GIF 分析 幀、延時、調色盤,以及每個位元組的去向。

以 Markdown 檢視

簡短的回答

一個 GIF 是一塊畫布、一串往上畫的長方形,外加一張調色盤,說明那些長方形裡的數字各代表什麼顏色。每個長方形帶著三樣東西:它該停留多久、之後該拿它怎麼辦,以及可有可無的一張自己的調色盤。

人們覺得這個格式奇怪的地方,幾乎都出自那串長方形。你的 GIF 大得離譜,要麼是那些長方形每次都是整塊畫布,要麼是裡面塞了三百張調色盤。它播得太慢,是因為延時低到了任何瀏覽器都不肯往下走的那條地板以下。它拖出尾巴,那是那個叫處置的欄位。

想知道某個具體檔案屬於哪一種,開啟 GIF 分析把它拖進去。這一頁剩下的部分,講的是那些數字都是什麼意思。

一個 GIF 的摘要卡片:版本、畫布尺寸、檔案大小、幀數、循環次數和用了多少種顏色。
播放器從不告訴你的一切,全從一個檔案裡讀出來。

幀是長方形,不是畫

只見過 GIF 播出來的人,在這裡最容易吃驚。一幀並不是那一刻動畫的畫面。它是一個長方形,有自己的位置和自己的大小,畫在前面幾幀留下的東西之上。

那個長方形可以是整塊畫布,而在做得糟的檔案裡它每次都是。但 GIF 是允許只存下自上一幀起變過的那部分的——而在畫面大半不動的地方,這就是一個 12 MB 的檔案和一個 900 KB 的檔案之間的差別。一段幾乎靜止的視窗錄屏能做得很小,而同一段錄屏從一個馬虎的轉換器裡出來就不小,原因也在這裡。

光看動畫,你分不出自己手上是哪一種,兩者看起來一模一樣。唯一的辦法是看每一幀存了什麼,而分析器專為此備了一個檢視:把它切到每幀實際存的東西,你要麼看到透明底上一排小形狀,說明編碼器做了它該做的事;要麼一遍又一遍看到整張畫面,說明它沒做。

這個格式裡任何地方都沒有運動補償。不會有東西被存成「和上次一樣,只是往左挪了四個畫素」,影片編碼器才那麼做。變化區域這個招數是 GIF 僅有的一點省法,而它非常值錢。

延時,以及每個瀏覽器都強推的那條地板

每一幀都存著自己要停多久,單位是百分之一秒。這個格式只有這一種單位,所以一個檔案能要的最快是 0.01 秒——每秒一百幀——最長約 655 秒。

它不會得到每秒一百幀。每個瀏覽器都把低於 0.02 秒的延時抬到 0.10。這條規矩 1996 年寫進了 Netscape Navigator,為的是那個年代旋轉的地球儀和會動的「施工中」牌子,之後每個瀏覽器都照抄了下來。從來沒人刪掉它,也不會有人刪。

所以一個每幀都寫著 0.01 秒的 GIF,播出來是每秒十幀,不是一百幀。它比造它的那個程式打算的慢十倍,而檔案對此毫無提示:裡面的延時正是當初要求的值。這是這個格式裡最常見的意外,也是分析器要報兩個時長的原因——檔案說的那個,和瀏覽器真正會照辦的那個。

無論檔案是在哪裡造的,解法都是寫 0.02 而不是 0.01。那是每秒 50 幀,也是真正的上限,比任何用途需要的都快。實際上 0.05 秒——每秒二十幀——差不多就是值得去要的最快了。

延時還能告訴你另一件事。要是它們全都一樣,這個檔案是由一組固定幀率的幀做出來的。要是它們參差不齊——這裡 0.04,那裡 0.11——那是某個東西轉了一段影片、丟掉了一些幀,再把鄰居拉長去補上窟窿。而要是最後一幀比其餘的長得多,那是有意為之:讓動畫在循環回去之前停一下,就是這麼做的。

處置:決定它會不會拖尾巴的欄位

每一幀都寫明自己的時間到了之後,螢幕上該留下什麼。可能的答案有四個,值得知道,因為動畫看起來不對的四種方式裡,有三種就是這個欄位填錯了。

有一處規範和現實分了家。「清回背景」指的是檔案頭裡寫著的那個背景色,而每個瀏覽器都無視它,改成清成透明。這樣已經二十五年了。一個指望那個背景色會出現的檔案,在做它的人、用做它的那個軟體看來是對的,在別處一律是錯的。

調色盤,以及它們要的 768 位元組

GIF 的一個畫素不是顏色。它是一個數字,指向一張最多 256 色的表,每種顏色佔三個位元組。所以一張滿的表是 768 位元組,而一個檔案可以讓所有幀共用一張,也可以一幀一張,或者兩者都有。

兩種安排都合法,只是換來的東西不同:

還有一筆更安靜的開銷。調色盤的長度必須是 2 的冪,所以用九種顏色的幀照樣拿到十六格的表,用 130 種的照樣拿到 256 格。一定程度的往上湊是躲不掉的。但要是一個檔案的表裡宣告瞭五千種它的畫素從沒引用過的顏色,那就是另一回事了:那是為另一張畫配的調色盤,而最終進到幀裡的並不是那張畫。分析器會把沒用上的格子標出來,讓這個形狀一眼可見。

位元組實際去了哪裡

GIF 的每個位元組都落在為數不多的幾個地方,在斷定一個檔案太大之前,值得知道這幾個地方是什麼。

要看這張表而不是猜,是因為答案因檔案而異,而解法直接從答案裡長出來。一個 95 % 都是壓縮畫素的檔案,就是畫面本身多,只有減幀、縮小尺寸或者減顏色才有用。一個 30 % 是調色盤、或者 40 % 是 XMP 的檔案,問題便宜得多。

一條按位元組去向拆解 GIF 的條形圖,每一幀一行,給出各自的大小和在檔案中的佔比。
位元組到底去了哪裡,一幀一幀地看。太大的 GIF 幾乎總是因為一個看了這張圖就明白的原因才大的。

循環並不屬於這個格式

GIF 規範裡沒有任何欄位說動畫會重複。循環來自 Netscape 在 1995 年發明的一個塊——一個裡面寫著 NETSCAPE2.0 的「應用程式擴充區塊」——所有人還是都實現了它,如今網際網路上每一個動圖 GIF 裡都有它。

這也意味著,沒有這個塊的檔案在每個瀏覽器裡都是恰好播一遍就停,在做它的人看來像是壞了。要是一段動畫只走一遍,就是這個塊不見了;這是最值得先查的幾件事之一,而且在任何看圖程式裡都看不見。

這個塊也能寫一個次數——播五遍然後停。零表示永遠,而幾乎所有檔案寫的都是零。

GIF 還能捎帶的別的東西

有三種塊不裝畫面,而且所有看圖程式都跳過:

這三樣在把檔案發出去之前都值得知道:它們是一個 GIF 裡可能透露你的部分,而且除非有東西專門把它們剝掉,否則每一次複製、每一次重新上傳,它們都活著。

讀一個損壞的檔案

GIF 會被截斷——中途停掉的下載、從快壞的硬碟上救回來的檔案、某個程式只寫了一半的東西。因為這個格式是一串塊,而不是一整個帶索引的結構,被截斷的 GIF 通常仍然能讀到它斷掉的那一點為止:斷口之前的每一幀都是完整無損的。

這值得知道,因為大多數軟體會乾脆拒收這個檔案。一個儘量往下讀、並且告訴你在哪裡停住的分析器,至少能讓你知道有多少活了下來,以及丟掉的是一幀還是最後兩百幀。

反過來的毛病也存在:坐在檔案結束標記之後的那些位元組。每個解碼器都在那個標記處停下,所以它們從不會被讀到、也不會被畫出來,而它們通常是某次出錯時被接在第一個檔案後面的第二個檔案。它們是純粹的重量,切掉不損失任何東西。

這些都不需要上傳

讀一個 GIF 的結構不是什麼費勁的工作——走一遍塊的清單,再加一個小小的解壓器——而且從來沒有任何技術上的理由,非要把檔案送到伺服器上去做它。這裡的 GIF 分析把整件事都放在頁面裡做完:走塊、LZW、畫到螢幕上的幀,還有那份位元組帳。

這件事在這裡比在多數工作上更要緊,因為人們最想拆開看的檔案,往往正是他們最拿不準要不要拿出去的那些——救回來的東西、別人發來的東西、裡面有一段自己還沒讀過的註釋的東西。關於上傳檔案的那篇更長的說理,在這裡和在本站任何地方一樣成立。