<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      為什么有的GIF圖片只會播放一遍,而有的會重復播放?關于gif你想知道的一切!

      ? 我們現在說的GIF圖片全稱是Graphics Interchange Format,是一種256色的圖片壓縮(LZW協議)文檔,主要用來存儲動態圖片。GIF圖片有兩種格式分布是1987年的GIF87a和1989年GIF89a版本。
      ? 其中GIF89a擴展了圖形控制區塊、備注、說明、應用程序接口等四個區塊,并提供了對透明色和多幀動畫的支持。這也是我們現在最廣為使用的協議。
      ? GIF89a 設定部分屬性如下圖所示

      ? 1995年Netscape 為了讓GIF可以更好的支持動畫和視頻剪輯,GIF89a又增加了圖像控制擴展功能(Graphics Control Extension (GCE)),每個frame的圖片都可以添加GCE部分,用來描述frame之間的延遲時間和循環次數。
      image
      上圖中 31D行,定義的就是重復播放次數。
      ? 所以,如果你的GIF動畫不循環播放,一種可能是GCE設置成了1就是只播放一次;第二種可能是你的GIF動畫中沒有GCE部分。要是GIF動畫沒有GCE,那就要看播放GIF動畫的工具是怎么處理默認邏輯了,有可能只播放一次,也有可能循環播放。
      ? 網上制作循環GIF圖一般使用PS軟件,我這里補充一段將不循環的GIF圖轉為循環的Python代碼,對于程序員們更友好和便捷。
      ? 這個問題是有實際場景的:知乎目前的文章只允許插入1MB大小以內的GIF圖,而大多數表情包之外的動圖都超出了這個大小,這時就需要對GIF的尺寸進行裁剪,或者進行抽幀。在Mac自帶的預覽功能里可以直接完成這兩個操作,但代價就是原本循環播放的GIF變得只動一次了。使用以下代碼:

      from PIL import ImageSequence, Image
      import imageio, os 
      
      location = "/path/to/"  # 文件目錄
      gif_file = os.path.join(location, "origin.gif")  # 輸入GIF文件名
      out_file = os.path.join(location, "output.gif")  # 輸出GIF文件名
      
      # 這里如果想使用灰度圖,可以使用`.convert("L")`代替,參考https://pillow-cn.readthedocs.io/zh_CN/latest/handbook/concepts.html#mode
      frames = [frame.copy().convert("RGBA") for frame in ImageSequence.Iterator(Image.open(gif_file))]
      imageio.mimsave(out_file, frames, 'GIF', duration=0.5)  # duration表示兩幀之間播放的間隔時間,也可不設,使用默認
      

      ? 再添加一些邏輯,可以將不循環的GIF圖批量轉為循環的。在GIF的保存時,不要使用網上流行的這種(可能會影響循環):

      frames[0].save("out.gif", save_all=True, append_images=frames[1:])
      

      ? 另外,也可以針對知乎的這個1MB場景,配合以上代碼,寫一個自動的GIF壓縮程序~

      導語

      GIF(Graphics Interchange Format)原義是“圖像互換格式”,是CompuServe公司在1987年開發出的圖像文件格式,可以說是互聯網界的老古董了。

      GIF格式可以存儲多幅彩色圖像,如果將這些圖像連續播放出來,就能夠組成最簡單的動畫。所以常被用來存儲“動態圖片”,通常時間短,體積小,內容簡單,成像相對清晰,適于在早起的慢速互聯網上傳播。

      本來,隨著網絡帶寬的拓展和視頻技術的進步,這種圖像已經漸漸失去了市場。可是,近年來流行的表情包文化,讓老古董GIF圖有了新的用武之地。

      圖片圖片

      表情包通常來源于手繪圖像,或是視頻截取,目前有很多方便制作表情包的小工具。

      這類圖片通常具有文件體積小,內容簡單,兼容性好(無需解碼工具即可在各類平臺上查看),對畫質要求不高的特點,剛好符合GIF圖的特性。

      所以,老古董GIF圖有了新的應用場景。

      本文的應用場景

      新的應用場景帶來新的需求,在本文所面臨的場景中,需要批量為用戶推送GIF表情包,希望在運營人員上傳圖包的時候,服務器可以自動完成縮略圖的批量生成工作。

      一批圖像大約有200-500張,以縮略圖列表的形式展示在客戶端。

      根據我們使用測試數據進行的統計GIF圖表情包的尺寸大部分在200k-500k之間,批量推送的一個重要問題就是數據量太大,因此,我們希望能夠在列表里展示體積較小的縮略圖,用戶點擊后,再單獨拉取原圖。

      傳統的GIF縮略圖是靜態的,通常是提取第一幀,但在表情包的情形下,這種方式不足以表達出圖片中信息。比如下面的例子

      圖片

      圖片

      ——第一幀完全看不出重點??!

      所以,我們希望縮略圖也是動態的,并盡可能和原圖相似。

      對于傳統圖片來說,文件大小一般和圖片分辨率(尺寸)正相關,所以,生成縮略圖最直觀的思路就是縮小尺寸,resize大法

      但是在GIF圖的場合,這個方式不再高效,因為GIF圖的文件大小還受到一個重要的因素制約——幀數

      以這張柴犬表情為例,原圖寬度200,尺寸1.44M,等比縮放到150之后,尺寸還是1.37M,等比縮放到100,相當于尺寸變為原來的四分之一,體積還是749K

      圖片

      圖片

      圖片

      可見,resize大法的壓縮率并不理想,收效甚微。

      而且,我們所得到的大部分表情圖素材,分辨率已經很小了,為了保證客戶端展示效果,不能夠過度減少尺寸,不然圖片會變得模糊。

      所以,想要對GIF圖進行壓縮,只能從別的方向入手。

      探尋GIF格式的存儲

      想要壓縮一個文件,首先要了解它是如何存儲的。畢竟,編程的事——萬變不離其宗嘛。

      圖片

      作為一種古老的格式,GIF的存儲規則也相對簡單,容易理解,一個GIF文件主要由以下幾部分組成。

      • 文件頭
      • 圖像幀信息
      • 注釋

      下面我們來分別探究每個部分。

      文件頭

      GIF格式文件頭和一般文件頭差別不大,也包含有

      • 格式聲明
      • 邏輯屏幕描述塊
      • 全局調色盤

      格式聲明

      圖片

      Signature 為“GIF”3 個字符;Version 為“87a”或“89a”3 個字符。

      邏輯屏幕描述塊

      圖片

      前兩字節為像素單位的寬、高,用以標識圖片的視覺尺寸。

      Packet里是調色盤信息,分別來看——

      Global Color Table Flag 為全局顏色表標志,即為1時表明全局顏色表有定義。

      Color Resolution 代表顏色表中每種基色位長(需要+1),為111時,每個顏色用8bit表示,即我們熟悉的RGB表示法,一個顏色三字節。

      Sort Flag 表示是否對顏色表里的顏色進行優先度排序,把常用的排在前面,這個主要是為了適應一些顏色解析度低的早期渲染器,現在已經很少使用了。

      Global Color Table 表示顏色表的長度,計算規則是值+1作為2的冪,得到的數字就是顏色表的項數,取最大值111時,項數=256,也就是說GIF格式最多支持256色的位圖,再乘以Color Resolution算出的字節數,就是調色盤的總長度。

      這四個字段一起定義了調色盤的信息。

      Background color Index 定義了圖像透明區域的背景色在調色盤里的索引。

      Pixel Aspect Ratio 定義了像素寬高比,一般為0。

      什么是調色盤?我們先考慮最直觀的圖像存儲方式,一張分辨率M×N的圖像,本質是一張點陣,如果采用Web最常見的RGB三色方式存儲,每個顏色用8bit表示,那么一個點就可以由三個字節(3BYTE = 24bit)表達,比如0xFFFFFF可以表示一個白色像素點,0x000000表示一個黑色像素點。

      如果我們采用最原始的存儲方式,把每個點的顏色值寫進文件,那么我們的圖像信息就要占據就是3×M×N字節,這是靜態圖的情況,如果一張GIF圖里有K幀,點陣信息就是3×M×N×K。

      下面這張兔子snowball的表情有18幀,分辨率是200×196,如果用上述方式計算,文件尺寸至少要689K。

      圖片

      但實際文件尺寸只有192K,它一定經歷過什么……

      我們可以使用命令行圖片處理工具gifsicle來看看它的信息。

      gifsicle -I snowball.gif > snowball.txt
      

      我們得到下面的文本

          5.gif 19 images
            logical screen 200x196
            global color table [128]
            background 93
            loop forever
            extensions 1
            + image #0 200x196 transparent 93
          disposal asis delay 0.04s
            + image #1 200x188 transparent 93
          disposal asis delay 0.04s
          ........
      

      可以看到,global color table [128]就是它的調色盤,長度128。

      為了確認,我們再用二進制查看器查看一下它的文件頭

      圖片

      可以看到Packet里的字段的確符合我們的描述。

      在實際情況中,GIF圖具有下面的特征

      (1)一張圖像最多只會包含256個RGB值。

      (2)在一張連續動態GIF里,每一幀之間信息差異不大,顏色是被大量重復使用的。

      在存儲時,我們用一個公共的索引表,把圖片中用到的顏色提取出來,組成一個調色盤,這樣,在存儲真正的圖片點陣時,只需要存儲每個點在調色盤里的索引值。

      如果調色盤放在文件頭,作為所有幀公用的信息,就是公共(全局)調色盤,如果放在每一幀的幀信息中,就是局部調色盤。GIF格式允許兩種調色盤同時存在,在沒有局部調色盤的情況下,使用公共調色盤來渲染。

      圖片

      這樣,我們可以用調色盤里的索引來代表實際的顏色值。

      一個256色的調色盤,24bit的顏色只需要用9bit就可以表達了。

      調色盤還可以進一步減少,128色,64色,etc,相應的壓縮率就會越來越大……

      還是以兔子為例,我們還可以嘗試指定它的調色盤大小,對它進行重壓縮

          gifsicle --colors=64 5.gif > 5-64.gif
          gifsicle --colors=32 5.gif > 5-32.gif
          gifsicle --colors=16 5.gif > 5-16.gif
          gifsicle --colors=2 5.gif > 5-2.gif
          ......
      

      依然使用gifsicle工具,colors參數就是調色盤的長度,得到的結果

      圖片

      圖片

      圖片

      圖片

      注意到了2的時候,圖像已經變成了黑白二值圖。

      居然還能看出是個兔子……

      所以我們得出結論——如果可以接受犧牲圖像的部分視覺效果,就可以通過減色來對圖像做進一步壓縮。

      文件頭所包含的對我們有用的信息就是這些了,我們繼續往后看。

      幀信息描述

      幀信息描述就是每一幀的圖像信息和相關標志位,在逐項了解它之前,我們首先探究一下幀的存儲方式。

      我們已經知道調色盤相關的定義,除了全局調色盤,每一幀可以擁有自己的局部調色盤,渲染順序更優先,它的定義方式和全局調色盤一致,只是作用范圍不同

      直觀地說,幀信息應該由一系列的點陣數據組成,點陣中存儲著一系列的顏色值。點陣數據本身的存儲也是可以進行壓縮的,GIF圖所采用的是LZW壓縮算法。
      這樣的壓縮和圖像本身性質無關,是字節層面的,文本信息也可以采用(比如常見的gzip,就是LZW和哈夫曼樹的一個實現)

      基于表查詢的無損壓縮是如何進行的?基本思路是,對于原始數據,將每個第一次出現的串放在一個串表中,用索引來表示串,后續遇到同樣的串,簡化為索引來存儲(串表壓縮法)

      舉一個簡單的例子來說明LZW算法的核心思路。

      有原始數據:ABCCAABCDDAACCDB

      可以看出,原始數據里只包括4個字符A,B,C,D,四個字符可以用2bit的索引來表示,0-A,1-B,2-C,3-D。

      原始字符串存在重復字符,比如AB,CC,都重復出現過。用4代表AB,5代表CC,上面的字符串可以替代表示為45A4CDDAA5DB

      這樣就完成了壓縮,串長度從16縮減到12。對原始信息來說,LZW壓縮是無損的。

      除了采用LZW之外,幀信息存儲過程中還采取了一些和圖像相關的優化手段,以減小文件的體積,直觀表述就是——公共區域排除、透明區域疊加

      這是ImageMagick官方范例里的一張GIF圖。

      圖片

      根據直觀感受,這張圖片的每一幀應該是這樣的。

      圖片

      但實際上,進行過壓縮優化的圖片,每一幀是這樣的。

      圖片

      首先,對于各幀之間沒有變化的區域進行了排除,避免存儲重復的信息。
      其次,對于需要存儲的區域做了透明化處理,只存儲有變化的像素,沒變化的像素只存儲一個透明值。

      這樣的優化在表情包中也是很常見的,舉個栗子

      圖片

      上面這個表情的文件大小是278KB,幀數是14

      我們試著用工具將它逐幀拆開,這里使用另一個命令行圖像處理工具ImageMagick

      gm convert source.gif target_%d.gif
      

      圖片

      可以看出,除了第一幀之外,后面的幀都做了不同程度的處理,文件體積也比第一幀小。

      這樣的壓縮處理也是無損的,帶來的壓縮比和原始圖像的具體情況有關,重復區域越多,壓縮效果越好,但相應地,也需要存儲一些額外的信息,來告訴引擎如何渲染,具體包括

      • 幀數據長寬分辨率,相對整圖的偏移位置
      • 透明彩色索引——填充透明點所用的顏色
      • Disposal Method——定義該幀對于上一幀的疊加方式
      • Delay Time——定義該幀播放時的停留時間

      其中值得額外說明的是Disposal Method,它定義的是幀之間的疊加關系,給定一個幀序列,我們用怎樣的方式把它們渲染成起來。

      詳細參數定義,可以參考該網站的范例
      http://www.theimage.com/animation/pages/disposal.html

      Disposal Method和透明顏色一起,定義了幀之間的疊加關系。在實際使用中,我們通常把第一幀當做基幀(background),其余幀向前一幀對齊的方式來渲染,這里不再贅述。

      理解了上面的內容,我們再來看幀信息的具體定義,主要包括

      • 幀分隔符
      • 幀數據說明
      • 點陣數據(它存儲的不是顏色值,而是顏色索引)
      • 幀數據擴展(只有89a標準支持)

      1和3比較直觀,第二部分和第四部分則是一系列的標志位,定義了對于“幀”需要說明的內容。

      幀數據說明

      圖片

      除了上面說過的字段之外,還多了一個Interlace Flag,表示幀點陣的存儲方式,有兩種,順序隔行交錯,為 1 時表示圖像數據是以隔行方式存放的。最初 GIF 標準設置此標志的目的是考慮到通信設備間傳輸速度不理想情況下,用這種方式存放和顯示圖像,就可以在圖像顯示完成之前看到這幅圖像的概貌,慢慢的變清晰,而不覺得顯示時間過長。

      幀數據擴展是89a標準增加的,主要包括四個部分。

      1、程序擴展結構(Application Extension)主要定義了生成該gif的程序相關信息

      圖片

      2、注釋擴展結構(Comment Extension)一般用來儲存圖片作者的簽名信息

      圖片

      3、圖形控制擴展結構(Graphic Control Extension)這部分對圖片的渲染比較重要

      圖片

      除了前面說過的Dispose Method、Delay、Background Color之外,User Input用來定義是否接受用戶輸入后再播放下一幀,需要圖像解碼器對應api的配合,可以用來實現一些特殊的交互效果。

      4、平滑文本擴展結構(Plain Text Control Extension)

      圖片

      89a標準允許我們將圖片上的文字信息額外儲存在擴展區域里,但實際渲染時依賴解碼器的字體環境,所以實際情況中很少使用。

      以上擴展塊都是可選的,只有Label置位的情況下,解碼器才會去渲染

      需求場景——給表情包減負

      說完了基本原理,來分析一下我們的實際問題。

      給大量表情包生成縮略圖,在不損耗原畫質的前提下,盡可能減少圖片體積,節省用戶流量。

      之前說過,單純依靠resize大法不能滿足我們的要求,沒辦法,只能損耗畫質了,主要有兩個思路,減少顏色減少幀數。

      減少顏色——圖片情況各異,標準難以控制,而且會造成縮略圖和原圖視覺差異比較明顯

      減少幀數——通過提取一些間隔幀,比如對于一張10幀的動畫,提取其中的提取1,3,5,7,9幀。來減少圖片的整體體積,似乎更可行。

      先看一個成果,就拿文章開頭的圖做栗子吧

      圖片圖片

      看上去連貫性不如以前,但是差別不大,作為縮略圖的視覺效果可以接受,由于幀數減小,體積也可以得到明顯的優化。體積從428K縮到了140K

      但是,在開發初期,我們嘗試暴力間隔提取幀,把幀重新連接壓成新的GIF圖,這時,會得到這樣的圖片。

      圖片

      主要有兩個問題。

      1、幀數過快

      2、能看到明顯的殘留噪點

      分析我們上面的原理,不難找到原因,正是因為大部分GIF存儲時采用了公共區域排除和透明區域疊加的優化,如果我們直接間隔抽幀,再拼起來,就破壞了原來的疊加規則,不該露出來的幀露出來了,所以才會產生噪點。

      所以,我們首先要把原始信息恢復出來。

      兩個命令行工具,gifsicle和ImageMagick都提供這樣的命令。

      gm convert -coalesce source.gif target_%d.gif
      gifsicle --unoptimize source.gif > target.gif
      

      圖片

      還原之后抽幀,重建新的GIF,就可以解決問題2了。

      注意重建的時候,可以應用工具再進行對透明度和公共區域的優化壓縮。

      至于問題1,也是因為我們沒有對幀延遲參數Delay Time做處理,直接取原幀的參數,幀數減少了,速度一定會加快。

      所以,我們需要把抽去的連續幀的總延時加起來,作為新的延遲數據,這樣可以保持縮略圖和原圖頻率一致,看起來不會太過鬼畜,也不會太過遲緩。

      提取出每一幀的delay信息,也可以通過工具提供的命令來提取。

      gm identify -verbose source.gif
      gifsicle -I source.gif
      

      在實際應用中,抽幀的間隔gap是根據總幀數frame求出的

      frame<8 gap=1
      9<frame<20 gap=2
      21<frame<30 gap=3
      31<frame<40 gap=4
      frame>40 gap=5
      

      delay值的計算還做了歸一化處理,如果新生成縮略圖的幀間隔平均值大于200ms,則統一加速到均值200ms,同時保持原有節奏,這樣可以避免極端情況下,縮略圖過于遲緩。

      具體實現

      本文介紹的算法主要應用于手Q熱圖功能的后臺管理系統,使用Nodejs編寫。
      ImageMagick是一個較為常用的圖像處理工具,除了gif還可以處理各類圖像文件,有node封裝的版本可以使用。
      gifsicle只有可執行版本,在服務器上重新編譯源碼后,采用spawn調起子進程的方式實現。

      ImageMagick對于圖片信息的解析較為方便,可以直接得到結構化信息。
      gifsicle支持命令管道級聯,處理圖片速度較快。
      實際生產過程中,同時采用了兩個工具。

      const {spawn} = require('child_process');const image = gm("src2/"+file)
        image.identify((err, val) => {    if(!val.Scene){
                console.log(file+" has err:"+err)          return
          }    let frames_count = val.Scene[0].replace(/\d* of /, '') * 1
          let gap = countGap(frames_count)    let delayList = [];    let totaldelay = 0
          if(val.Delay!=undefined){          let i          for (i = 0; i < val.Delay.length; i ++) {
                  delayList[i] = val.Delay[i].replace(/x\d*/, '') * 1
                  totaldelay+=delayList[i]
                }          for (; i < val.Scene.length; i ++) {
                  delayList[i] = 8
                  totaldelay+=delayList[i]
                }
          }else{          for (let i = 0; i < val.Scene.length; i ++) {
                  delayList[i] = 8
                  totaldelay+=delayList[i]
                }
          }    let totalFrame = parseInt(frames_count/gap)    //判斷是否速度過慢,需要進行歸一加速處理
          if(totaldelay/totalFrame>20){          let scale =(totalFrame*1.0*20)/totaldelay          for (let i = 0; i < delayList.length; i ++) {
                  delayList[i] = parseInt(delayList[i] * scale)
                }
          }    let params=[]    params.push("--colors=255")    params.push("--unoptimize")    params.push("src2/"+file)    let tempdelay = delayList[0]    for (let i = 1; i < frames_count; i ++) {          if(i%gap==0){            params.push("-d"+tempdelay)            params.push("#"+(i-gap))
                  tempdelay=0
                }
            tempdelay += delayList[i]
          }    params.push("--optimize=3")    params.push("-o")    params.push("src2/"+file+"gap-keepdelay.gif")
          spawn("gifsicle", params, { stdio: 'inherit' })
      })
      

      測試時,采用該算法隨機選擇50張gif圖進行壓縮,原尺寸15.5M被壓縮到6.0M,壓縮比38%,不過由于該算法的壓縮比率和具體圖片質量、幀數、圖像特征有關,測試數據僅供參考。

      本文到這里就結束了,原來看似簡單的表情包,也有不少文章可做。

      謝謝觀看,希望文中介紹的知識和研究方法對你有所啟發。

      You Don't Know Gif - 分析 gif 文件和一些奇怪的 gif 功能

      發表 - 2022-01-13 | 18分鐘

      他是波平

      是的,我指的是您可以在大型網站(如 Google 擁有的Tenor或 Facebook 擁有的 giphy )上找到的主流常見 gif 。每個人都喜歡的用于共享動畫短片的文件格式。

      大多數人都知道的gif

      大多數人都知道 gif,gif 是一種動畫文件格式。您可能看過 gif 文件并認為這些文件非常大。也許你看著它們會想:哇,這些圖片清晰度很低。但歸根結底,當您想到 gif 時,您可能會將其視為短動畫文件格式。

      然而,這個用例與編寫 gif 的人們期望它的用途截然不同。在這篇文章中,我們將深入剖析 gif 文件,并在此過程中討論它的一些更時髦的特性。

      請注意,這篇文章應該是對如何理解 gif 格式及其一些更深奧的功能的有趣探索。如果您想真正學習如何解析 gif,我會推薦以下資源:

      在這段時間里,我實際上使用這些資源制作了一個幾乎不兼容的 gif 解析器,這些資源稱為awesome-gif,它將解析一些 gif。我不建議使用它。

      反正上貼。

      gif的歷史

      gif 文件格式由 Compuserve 于 1987 年創建。早在 1987 年,gif 是一種相當緊湊的格式!它使用壓縮,而不僅僅是任何壓縮,而是 LZW 壓縮。許多較舊的文件格式(一些由 Compuserve 制作)使用 RLE(運行長度編碼),在許多情況下效率不高。gif 的一大成功因素是其穩定的壓縮比和良好的色域(完整的 256 色,哇?。?a target="_blank" rel="noopener nofollow">1

      兩年后,創建了 gif 文件格式的附錄 (gif89a),其中添加了許多我們今天知道和喜愛的功能。

      通過 gif89a 規范,我們可以快速總結 gif89 與 gif87a 支持的所有功能。

      Appendix A. Quick Reference Table. Block Name                  Required   Label       Ext.   Vers. Application Extension       Opt. (*)   0xFF (255)  yes    89a Comment Extension           Opt. (*)   0xFE (254)  yes    89a Global Color Table          Opt. (1)   none        no     87a Graphic Control Extension   Opt. (*)   0xF9 (249)  yes    89a Header                      Req. (1)   none        no     N/A Image Descriptor            Opt. (*)   0x2C (044)  no     87a (89a) Local Color Table           Opt. (*)   none        no     87a Logical Screen Descriptor   Req. (1)   none        no     87a (89a) Plain Text Extension        Opt. (*)   0x01 (001)  yes    89a Trailer                     Req. (1)   0x3B (059)  no     87a Unlabeled Blocks Header                      Req. (1)   none        no     N/A Logical Screen Descriptor   Req. (1)   none        no     87a (89a) Global Color Table          Opt. (1)   none        no     87a Local Color Table           Opt. (*)   none        no     87a Graphic-Rendering Blocks Plain Text Extension        Opt. (*)   0x01 (001)  yes    89a Image Descriptor            Opt. (*)   0x2C (044)  no     87a (89a) Control Blocks Graphic Control Extension   Opt. (*)   0xF9 (249)  yes    89a Special Purpose Blocks Trailer                     Req. (1)   0x3B (059)  no     87a Comment Extension           Opt. (*)   0xFE (254)  yes    89a Application Extension       Opt. (*)   0xFF (255)  yes    89a legend:           (1)   if present, at most one occurrence                  (*)   zero or more occurrences                  (+)   one or more occurrences 
      

      對于以前沒有閱讀過整個規范的人來說,其中大部分內容都是胡言亂語,所以讓我們討論一下 gif 是如何組合在一起的,我們將在此過程中討論它的一些奇怪之處。

      在我們從規范開始之前有一些樂趣:

      Appendix D. Conventions. Animation - The Graphics Interchange Format is not intended as a platform for animation, even though it can be done in a limited way. 
      

      無論如何,讓我們開始吧;)

      gif的解剖結構

      我將通過一個示例來完成此操作,因此,如果您想繼續學習,請隨意!右鍵單擊并下載,一切順利。

      網絡安全向日葵2

      如果您在家里跟隨,您所需要的只是一臺安裝了 hexdump 工具的機器。我將使用預裝在大多數 unix(Linux、macOS)上的 xxd,或者可以與包 vim-common一起安裝。

      gif 標題

      每個 gif 都以一個標題開頭,其中的魔法位表示它是什么類型的 gif,以及一些額外的信息,提供有關圖像的基本細節。

      ?? xxd Sunflower_as_gif_websafe_89a.gif | head -1 # and some arrows 00000000: -> 4749 4638 3961 <- dc00 0501 f700 0002 0102  GIF89a.......... 
      

      xxd 使前幾個字節的工作對我們來說很容易,如果有意義的話,它會嘗試將字節解碼為 ascii。看看那個,GIF89a!這是經過認證的有效 gif!

      每個字母代表一個字節,所以我們在這里尋找的神奇字節是:0x47, 0x49, 0x46, 0x38, 0x39, 0x61.

      可選的最后三個字節可能是:0x38, 0x37, 0x61如果只支持 gif87a 文件格式。我們不會像 gif89 那樣進入舊版本的格式。

      標頭中沒有什么其他有趣的東西,因為它只是靜態字節,所以讓我們繼續前進。

      繞道:嘿,誰接受 gif87a?

      在研究 gif 時,我想看看是否有任何一個主要的 gif 托管服務提供商會接受并保留 gif87a。他們會工作,還是會出錯?

      這是我們之前看到的向日葵的 gif87a 版本。此圖像的 87a 版本將僅用于本節。

      舊標準

      讓我們將圖像上傳到 4 個主要的 gif 托管提供商:

      這是我們開始的:

      ?? xxd Sunflower_as_gif_websafe_gif87a.gif | head -1 00000000: 4749 4638 3761 fa00 2901 f500 00ff cc33  GIF87a..)......3 
      

      這是重新下載我剛剛上傳的圖像后的結果。

      Tenor 重新編碼為 gif89a:

      ?? Downloads xxd tenor.gif | head -1 00000000: 4749 4638 3961 a401 f201 f700 0006 0406  GIF89a.......... 
      

      giphy 重新編碼為 gif89a:

      ?? Downloads xxd giphy.gif | head -1 00000000: 4749 4638 3961 fa00 2901 f525 0000 0000  GIF89a..)..%.... 
      

      其實這有點不誠實,giphy 只接受動畫 gif,所以我們必須點擊編輯(顯示幀編輯器)并點擊完成。允許在 gif87a 規范中存儲多個圖像,但它們不能有延遲(因此沒有動畫3)。我想我不確定我在這里期望什么。


      imgur 保留原始文件?。?!

      ?? Downloads xxd aUxm3NN.gif | head -1 00000000: 4749 4638 3761 fa00 2901 f500 00ff cc33  GIF87a..)......3 
      

      至于 gfycat,它在最后 20 分鐘內一直停留在“編碼”的最后階段。希望我沒有在周末提醒一位可憐的工程師。


      這個簡短的分析表明,由世界上最大的兩家科技公司制造或擁有的兩家最大的托管服務提供商不尊重我的舊 gif 并完全重寫它。事實上,對于一家名為 giphy 的公司來說,它似乎只尊重一種 gif。周二我得和 giphy 團隊談談……

      無論如何回到探索文件格式。

      邏輯屏幕描述符

      嘿,您的圖像如何以特定分辨率顯示?假設我們在 macOS 的預覽中使用“獲取信息”功能,它怎么知道這張圖片是 220x261 的?

      從預覽中獲取信息

      不管你信不信,這是內置在文件格式中的!4

      字節 0x6-0xA 有這個和更多的信息。字節 0x6 和 0x8 指的是長度和寬度。

      ?? xxd Sunflower_as_gif_websafe_89a.gif | head -1 # and some arrows 00000000:  4749 4638 3961 -> dc00 0501 <- f700 0002 0102  GIF89a.......... 
      

      每個維度都有兩個字節來指定大小。同樣重要的是要記住 gif 文件格式的所有字節都指定為 little endian 5。

      首先是寬度,即 0x00dc(從 dc00 重新排序)=> 220 十進制

      然后是長度為 0x0105(從 0501 重新排序)=> 261 十進制

      Detour:這是否意味著我們對 gif 文件有分辨率限制?

      這是正確的!由于我們每個只得到兩個字節,因此沒有一個分辨率、寬度或長度可以大于 65535。我們可以通過嘗試在 gimp 中創建一個 1x65536 的新 gif 來確認這一點:

      哇哇太大了

      其他文件格式在這方面也不盡如人意。如果您想下載現有最寬的 png,您可以在 此處下載。這是一個小下載,但如果您打開它可能會導致您的圖像查看器崩潰。Firefox 努力打開它并說即使它符合規范也有錯誤。

      說謊者

      回到邏輯屏幕描述符

      邏輯屏幕描述符還沒有完成,接下來是一組打包字段。使用規范中的圖表更容易解釋:

           <Packed Fields>  =      Global Color Table Flag       1 Bit                             Color Resolution              3 Bits                             Sort Flag                     1 Bit                             Size of Global Color Table    3 Bits 
      

      如果設置了全局顏色表位,這將具有關于全局顏色表的信息,該信息將出現在邏輯屏幕描述符之后。

      顏色分辨率決定全局顏色表中每種顏色的字節數。

      排序標志應該是一個標志,它通過以最有用到最不有用的方式對顏色進行排序,告訴解碼器較早的顏色更重要。

      全局顏色表的大小,嗯,顏色表有多大。

      在我們的向日葵字節 0xA 中,我們得到結果 0xF7

      ?? xxd Sunflower_as_gif_websafe_89a.gif | head -1 00000000: 4749 4638 3961 dc00 0501 -> f7 <- 00 0002 0102  GIF89a.......... 
      

      或者二進制是:1111 0111

      這基本上意味著我們有一個完全加載的 gif,除了 GCT 沒有排序。

                                   ┌──────────GCT not sorted                             ▼          by importance                        1111 0111                        ▲───  ───      GCT set───────────┘ ▲    ▲                          │    │    3 bytes per           │    └─────GCT is 768 bytes    color    ─────────────┘          (max size) (max resolution) 
      

      我想我們已經走到了這一步,甚至還沒有討論過全局顏色表是什么。全局顏色表保存每個字節部分中使用的顏色。它們是 0-255 的標準 RGB 值,您可以插入任何現代 RGB 顏色選擇器。

      Detour:嘿等一下,全局顏色表是可選的嗎?

      如果您有敏銳的眼光,您可能已經注意到字節 0xA 中的第一位表示 GCT 可以是可選的。嗯,這很有趣。我們如何在不指定所需顏色的情況下渲染圖像?

      根據規范:

      顏色表 - 全局和局部顏色表都是可選的;如果存在,全局顏色表將用于數據流中未給出局部顏色表的每個圖像;如果存在,本地顏色表將覆蓋全局顏色表。但是,如果兩個顏色表都不存在,則應用程序可以自由使用任意顏色表。

      嗯,這很整潔。嘿,如果我們將全局顏色表刪除到圖像中,現代渲染器會對我們的圖像做什么?我敢肯定,有什么了不起的。

      我們的圖像指定顏色表大小為 768 字節。它從字節 0xA 開始……假設我們只是將字節 0xA 的最高有效位清零,就像這樣。

      然后刪除直到字節 789(不包括):

      ?? xxd Sunflower_as_gif_89a-no-gct.gif | head -1 00000000: 4749 4638 3961 dc00 0501 007f 8121 f904  GIF89a.......!.. 
      

      現在第一行就這樣結束了,它仍然是一個完全有效的 gif??雌饋碓趺礃樱?/p>

      這里沒有gct!

      驚人的!太棒了!精彩的!截至撰寫本文時,它只是一個完美的黑色方塊。我嘗試過的每一個渲染器都是這種情況。Gimp、Chrome、Firefox、Preview、gifiddle,應有盡有。

      這有點無聊,但我不確定我的預期。

      無論如何回到邏輯屏幕描述符

      邏輯屏幕描述符繼續

      在描述全局顏色表的字節之后,有兩個描述屏幕描述符的最后字節。

      字節 B 是背景顏色,它是指全局顏色表的索引,字節 C 是像素縱橫比,描述像素的方形度。

      ?? xxd Sunflower_as_gif_websafe_89a.gif | head -1 00000000: 4749 4638 3961 dc00 0501 f700 0002 0102  GIF89a..........                                     ^  ^                                     |  | Background color is color in index 0 of | GCT                                     |                                        Pixel aspect ratio is 0:0 or host                                        pixel aspect ratio. 
      

      Detour:嘿等一下,像素長寬比,那是什么?

      像素并不總是方形的!字節也不總是 8 位,但這是我不會討論的切線。

      Gif,實際上還有一些其他最流行的現代圖像格式支持非方形像素。

      嘿!我想知道我們最流行的 gif 渲染器在渲染非方形像素時的兼容性如何??赡芊浅:弦?。讓我們在 Firefox 和 Chrome 中嘗試一個流行的測試,看看它們的外觀: http: //frs.badcoffee.info/PAR_AcidTest/

      最好的渲染器

      呃…… 嗯,那是東西。這些是按順序排列的:jpg、png 和 gif。Firefox、Chrome 和 Preview 都忽略了縱橫比。

      不幸的是,這廣泛不受支持,目前 Firefox 中有一個 16 年前的錯誤:https ??/bugzilla.mozilla.org/show_bug.cgi?id=333377

      即使是Gifiddle,我發現的最兼容的 gif 查看器也不支持非方形像素: https ??/github.com/ata4/gifiddle/issues/1

      當我們嘗試更多模糊的 gif 功能時,我們將更多地討論 gifiddle。

      如果你真的想顯示非方形像素,你可以通過按摩 gimp 來做到這一點。grafx2 顯然也可以處理非常具體的奇數分辨率的像素。雖然我自己沒有測試過。

      在全局顏色表上

      全局顏色表 (GCT) 很容易成為 gif 中最無聊的部分。您以 3 的倍數從 0 跳轉到全局顏色表的大小。這里真的沒有什么值得討論的。

      說明這一點的最好方法是指向我的 可怕 gif項目,它將輸出向日葵 GCT 中的所有顏色(可能還有其他圖像)。

      GCT 解析就 在這里 ,您可以看到它并沒有什么特別之處。

      運行:

      cargo run --quiet -- --gif-file ./experiments/Sunflower_as_gif_websafe.gif 
      

      可選的圖形控制擴展

      現在我們有了圖形控制擴展(GCE),由擴展引入者引入:0x21(引入擴展),然后是 0xF9(?。?/p>

      有許多擴展,但至少在現代使用中,圖形控制擴展可以說是最重要的擴展之一。GCE 需要通過允許幀之間的延遲時間等來使 gifs “動畫化”。

      ?? xxd Sunflower_as_gif_websafe_89a.gif | head -50 | tail -2 00000300: 88ae b091 a5b1 a4b9 be94 887f 81 -> 21 f904  .............!.. 00000310: 0000 0000 <- 0021 fe51 4669 6c65 2073 6f75  .....!.QFile sou 
      

      這個 gif 不是完全動畫,所以這里沒有太多內容。如您所見,有很多零,但我們仍將進入每個字節。

      第一個字節是塊大小,在本例中為 0x04,但實際上根據規范始終為0x04。

      Detour:等一下,我們可以擺脫塊大小嗎?

      啊,如果塊大小總是一個靜態常量,那么它并不重要,是嗎?從技術上講,它是規范的一部分,但實際上并沒有做任何事情。讓我們玩我們最喜歡的在最受歡迎的圖像查看器中打開它的游戲。

      對于這些測試,我將使用一個更簡單的 gif 來更容易地查看發生了什么:

      簡單的gif

      對于以下測試,我對其進行了修改以刪除 GCE。修改后的版本保存在下面的 xxd 格式中。要重新組裝它:

      00000000: 4749 4638 3961 2000 3400 f0ff 00ff ffff  GIF89a .4....... 00000010: 0000 0021 f903 0500 0002 002c 0000 0000  ...!.......,.... 00000020: 2000 3400 0002 788c 8fa9 cb0b 0fa3 94ed   .4...x......... 00000030: cc7b abc1 1cea d075 5fc8 8d64 a69d 68a5  .{.....u_..d..h. 00000040: 4e66 eba5 702c 3675 cddc a5bd e34e bfcb  Nf..p,6u.....N.. 00000050: 0131 ace1 ea47 0405 9128 9f42 9714 2667  .1...G...(.B..&g 00000060: a70d 3564 bd1a b52e 25b7 f905 8729 de31  ..5d....%....).1 00000070: cd1c c9a2 016a 74db fc1e c7c3 f36f 9d7b  .....jt......o.{ 00000080: d7e6 af7b 6a7f f607 13d8 32a8 5258 55e6  ...{j.....2.RXU. 00000090: 9608 b728 d748 f768 1789 f751 b950 0000  ...(.H.h...Q.P.. 000000a0: 3b                                       ; 
      

      將其保存到一個名為 invalid.hex 的文本文件并執行以下操作:xxd -r invalid.hex > invalid.gif

      (如果找不到更新的字節,它位于字節:0x16 并從 0x4 -> 0x03 更改)


      第一個 macOS 預覽:

      預覽測試

      預覽符合標準!我們喜歡看!即使對于可能永遠不會得到更新的文件格式在技術上并不重要。


      接下來讓我們試試火狐:

      火狐測試

      Firefox 知道它是一個靜態值并忽略它的結果。不完全符合標準,但可能是最明智的做法。


      鉻測試

      刪除塊大小時,Chrome 會變得有點麻煩。不完全顛倒它,它確實……我不確定。Chrome 肯定是最不符合這里標準的。

      回到圖形控制擴展

      在我們讀取塊大小之后,我們得到一個打包字段,描述為:

            <Packed Fields>  =     Reserved                      3 Bits                             Disposal Method               3 Bits                             User Input Flag               1 Bit                             Transparent Color Flag        1 Bit 
      

      在我們的圖像中,所有這些字段都設置為 0,所以我將對其進行解釋。

      保留是為 gif22a 出來的時候設置的,我們需要這三個位來做一些好事。

      用戶輸入用于獲取用戶輸入以通過鼠標單擊或鍵盤按下將 gif 推進到下一個圖像。

      透明索引用于設置我們是否應該允許透明。

      繞道:等待第二個 GIFS 可以接受用戶輸入???

      是的,你沒看錯。Gifs可以接受用戶輸入以前進到下一幀。這個可憐的家伙圍繞使用 png 重新創建此功能構建了一個完整的站點??上裎乙粯颖焕г诶锩?2 年后沒有閱讀 gif 規范。

      雖然我們在 gif 的怪異功能部分,但我們不妨討論一下 gif 支持的其他怪異功能,即純文本擴展。

      純文本擴展允許 gif 制作者在任何他們喜歡的地方嵌入等寬文本,并直接在圖像上使用一些基本樣式。

      像用戶輸入擴展這樣的純文本擴展,可能從來沒有被任何 gif 查看器實現過,除了那些古怪的人為了好玩而制作的那些,比如制作gifiddle的人。

      BOB_89A.gif,很可能是互聯網上發布的第一個 gif,它是一個使用兩者的 gif 示例。

      這里 BOB_89A.gif 在現代瀏覽器中呈現(您自己的瀏覽器,如果不是現代瀏覽器,請更新它):

      BOB_89A

      然而,如果你把它放入gifiddle,你會得到一個非常不同的結果,最后一條消息是一個非常重要的事實。

      我不會破壞這個驚喜,給它一個右鍵單擊下載,然后把那個 gif 放到gifiddle看看會發生什么。

      Gifiddle鏈接:http ??/ata4.github.io/gifiddle/

      任何現代瀏覽器或 gif 查看器都不支持這些功能。

      如果您想了解更多關于純文本擴展的信息,可以在 此處進行。

      可選的評論擴展

      接下來是評論擴展,實際上可能出現在塊開始的任何地方。然而,它最常出現在 gif 的這一部分中。

      注釋部分只允許包含 7 位 ascii,供人類閱讀。

      由于注釋部分只是 ascii,因此您可以觸發字符串并在輸出中找到注釋:

      ?? strings Sunflower_as_gif_websafe_89a.gif | head -7 | tail -1 QFile source: https://commons.wikimedia.org/wiki/File:Sunflower_as_gif_websafe.gif 
      

      在這張圖片中,它從我們圖片中的字節 0x310 開始:

      ?? xxd Sunflower_as_gif_websafe_89a.gif | head -55 | tail -6 00000310: 0000 0000 0021 fe51 4669 6c65 2073 6f75  .....!.QFile sou 00000320: 7263 653a 2068 7474 7073 3a2f 2f63 6f6d  rce: https://com 00000330: 6d6f 6e73 2e77 696b 696d 6564 6961 2e6f  mons.wikimedia.o 00000340: 7267 2f77 696b 692f 4669 6c65 3a53 756e  rg/wiki/File:Sun 00000350: 666c 6f77 6572 5f61 735f 6769 665f 7765  flower_as_gif_we 00000360: 6273 6166 652e 6769 6600 2c00 0000 00dc  bsafe.gif.,..... 
      

      再次由 BANG (!)(引入擴展)和 0xfe 評論擴展表示。之后,注釋應該被讀取 255 個字節或直到讀取 0x00。

      其余圖像數據

      之后就沒什么好說的了。該圖像跳過了大多數其他 gif 功能,例如本地顏色表和動畫,因此 gif 的其余大部分只是數據和終止符。

      老實說,lzw 壓縮并不難學,但解釋它不是這篇博文的目的。如果您想了解它,Matthew Flickinger在他的網站上有一篇很棒的帖子。

      附加部分:真彩色 gif

      你知道 GIF 可以是真彩色嗎?它需要一點點瘋狂,但如果你記得世界“本地顏色表”,那么它可能是有道理的。每個數據段都可以有自己的本地顏色表,因此如果你將 gif 分解成足夠多的部分,你就會得到真實的顏色!

      真彩色gif

      大多數 gif 不這樣做有幾個原因。

      首先,生成的圖像將是巨大的。每個 256 色的新調色板將額外消耗 768 個字節。

      其次,現在的渲染器不會“正確”渲染圖像。默認情況下,如果未指定,瀏覽器通常會在幀之間設置 0.1 的延遲。

      然而,真正兼容的 gif 渲染器將正確顯示真彩色 gif。因此,如果您有空間、內存和備用 CPU,為什么不擁有一個真彩色 gif 呢?我們都可以有一點本色作為一種享受。

      如果您想了解有關真彩色 gif 的更多信息,維基百科有一個完整的部分,稱為真彩色。

      原文鏈接:https://blog.darrien.dev/posts/you-dont-know-gif/

      Darrien's technical blog

      Documenting the technical stuff I do in my spare time

      Home All Posts About

      You Don't Know Gif - An analysis of a gif file and some weird gif features

      Published - 2022-01-13 | 18min

      He’s boppin

      Yes I am referring to the mainstay common gif you’ll find all over the web at large sites like Google’s owned Tenor or Facebook’s owned giphy. Everyone’s favorite file format for sharing short animated snippets.

      The gif as most people know it

      As most people know gif, gif is an animated file format. You might have looked at gif files and thought wow these files are pretty large. Perhaps you looked at them and thought: wow, these pictures are low definition. But at the end of the day, when you think of gif, you probably think of it as the short animated file format.

      However this use case is drastically different from what the folks who wrote gif to expected it to be used for. In this post we’ll dive into the anatomy of a gif file and discuss some of its funkier features along the way.

      Note that this post is supposed to be a fun exploration of how to understand the gif format and some of its more esoteric features. If you want to actually learn how to parse gifs, I would recommend these resources:

      During this time I actually made a barely compliant gif parser using these resources called awful-gif which will parse some gifs. I don’t recommend using it.

      Anyway onto the post.

      The history of gif

      The gif file format was created in 1987 by Compuserve. Back in 1987 gif was a rather compact format! It used compression, and not just any compression, but LZW compression. Many older file formats (some made by Compuserve) used RLE (Run Length Encoding) which in many cases isn’t nearly as efficient. One of the big win factors for gif was its solid compression ratio and good color gamut (a full 256 colors, wow!).1

      Two years later an addendum to the gif file format was created (gif89a) which added many of the features we know and love today.

      Via the gif89a spec we can get a quick summary of all of the features supported in gif89 vs gif87a.

      Appendix A. Quick Reference Table. Block Name                  Required   Label       Ext.   Vers. Application Extension       Opt. (*)   0xFF (255)  yes    89a Comment Extension           Opt. (*)   0xFE (254)  yes    89a Global Color Table          Opt. (1)   none        no     87a Graphic Control Extension   Opt. (*)   0xF9 (249)  yes    89a Header                      Req. (1)   none        no     N/A Image Descriptor            Opt. (*)   0x2C (044)  no     87a (89a) Local Color Table           Opt. (*)   none        no     87a Logical Screen Descriptor   Req. (1)   none        no     87a (89a) Plain Text Extension        Opt. (*)   0x01 (001)  yes    89a Trailer                     Req. (1)   0x3B (059)  no     87a Unlabeled Blocks Header                      Req. (1)   none        no     N/A Logical Screen Descriptor   Req. (1)   none        no     87a (89a) Global Color Table          Opt. (1)   none        no     87a Local Color Table           Opt. (*)   none        no     87a Graphic-Rendering Blocks Plain Text Extension        Opt. (*)   0x01 (001)  yes    89a Image Descriptor            Opt. (*)   0x2C (044)  no     87a (89a) Control Blocks Graphic Control Extension   Opt. (*)   0xF9 (249)  yes    89a Special Purpose Blocks Trailer                     Req. (1)   0x3B (059)  no     87a Comment Extension           Opt. (*)   0xFE (254)  yes    89a Application Extension       Opt. (*)   0xFF (255)  yes    89a legend:           (1)   if present, at most one occurrence                  (*)   zero or more occurrences                  (+)   one or more occurrences 
      

      Much of this is going to be gibberish to folks who haven’t read the whole spec before, so let’s discuss how a gif is put together a bit, and we’ll talk about some of its oddities along the way.

      Some fun before we begin from the spec:

      Appendix D. Conventions. Animation - The Graphics Interchange Format is not intended as a platform for animation, even though it can be done in a limited way. 
      

      Anyway let’s begin ??

      The anatomy of a gif

      I’m going to walk through this with an example, so if you’d like to follow along, feel free! Right click and download and you’re good to go.

      Websafe sunflower2

      If you’re following along at home, all you need is a machine with a hexdump tool installed. I’ll be using xxd which is preinstalled on most unixes (Linux, macOS), or can be installed with the package vim-common.

      The gif header

      Every gif starts with a header where the magic bits signifying what what type of gif it is and a little extra information giving basic details about the image.

      ?? xxd Sunflower_as_gif_websafe_89a.gif | head -1 # and some arrows 00000000: -> 4749 4638 3961 <- dc00 0501 f700 0002 0102  GIF89a.......... 
      

      xxd makes the work easy for us for the first few bytes and tries to decode bytes to ascii if it makes sense. Take a look at that, GIF89a! It’s a certified valid gif!

      Each letter stands out as one byte, so the magic bytes we’re looking for here are: 0x47, 0x49, 0x46, 0x38, 0x39, 0x61.

      Optionally last three bytes may be: 0x38, 0x37, 0x61 if only supporting the gif87a file format. We won’t go into the older version of the format as much as gif89.

      There isn’t much else interesting in the header since it’s just static bytes, so let’s keep moving on.

      Detour: Hey who accepts gif87a?

      While looking into gifs I wanted to see if either of the major gif hosting providers would accept and retain gif87a. Would they work, or just err out?

      Here’s a gif87a version of the sunflower we were looking at earlier. The 87a version of this image will only be used for this section.

      The old standard

      Let’s upload the image to 4 of the major gif hosting providers:

      This is what we start with:

      ?? xxd Sunflower_as_gif_websafe_gif87a.gif | head -1 00000000: 4749 4638 3761 fa00 2901 f500 00ff cc33  GIF87a..)......3 
      

      Here are the results after redownloading the image I just uploaded.

      Tenor re-encodes to gif89a:

      ?? Downloads xxd tenor.gif | head -1 00000000: 4749 4638 3961 a401 f201 f700 0006 0406  GIF89a.......... 
      

      giphy re-encodes to a gif89a:

      ?? Downloads xxd giphy.gif | head -1 00000000: 4749 4638 3961 fa00 2901 f525 0000 0000  GIF89a..)..%.... 
      

      Actually that’s a little disingenuous, giphy ONLY accepts animated gifs, so we have to click edit (which shows the frame editor) and click done. Multiple images are allowed to be stored in the gif87a spec, but they cannot have a delay (hence no animation3). I guess I’m not sure what I expected here.


      imgur preserves the original file!!!

      ?? Downloads xxd aUxm3NN.gif | head -1 00000000: 4749 4638 3761 fa00 2901 f500 00ff cc33  GIF87a..)......3 
      

      As for gfycat, well it’s been stuck in the last phase of “encoding” the for last 20 minutes. Hopefully I didn’t make an alert for a poor engineer on the weekend.


      This short analysis shows the two biggest hosting providers made or owned by two of the largest tech companies in the world don’t respect my old gif and completely rewrite it. In fact for a company called giphy it only seems to respect one kind of gif. I’ll have to have a talk with the giphy team on Tuesday…

      Anyway back to exploring the file format.

      The logical screen descriptor

      Hey how does your image show up in a certain resolution? Say we use the “get info” function in preview on macOS, how does it know this image is 220x261?

      get info from preview

      Well believe it or not, that’s built into the file format4

      Bytes 0x6-0xA have this and a little more info in it. Bytes 0x6 and 0x8 refer to the length and width.

      ?? xxd Sunflower_as_gif_websafe_89a.gif | head -1 # and some arrows 00000000:  4749 4638 3961 -> dc00 0501 <- f700 0002 0102  GIF89a.......... 
      

      Each dimension gets two bytes to specify size. Also it’s important to remember that all bytes in gif file format are specified as little endian5.

      First comes width, which is 0x00dc (reordered from dc00) => 220 in decimal

      Then comes length which is 0x0105 (reordered from 0501) => 261 in decimal

      Detour: Does that mean we have a resolution limit on our gifs?

      That’s right! Since we only get two bytes each, no one resolution, width or length can be larger than 65535. We can confirm that by trying to make a new gif of 1x65536 in gimp:

      waaaay too big

      Other file formats don’t fall short to this regard. If you’d like to download the widest png in existence you may do so here. It’s a small download, but may crash your image viewer if you open it. Firefox struggles to open it and says there is an error even though it’s spec compliant.

      LIAR

      Back to the logical screen descriptor

      The logical screen descriptor isn’t done yet though, next come a set of packed fields. It’s easier to explain with the diagram from the spec:

           <Packed Fields>  =      Global Color Table Flag       1 Bit                             Color Resolution              3 Bits                             Sort Flag                     1 Bit                             Size of Global Color Table    3 Bits 
      

      This has information on the global color table which will come after the logical screen descriptor if the global color table bit is set.

      Color resolution decides how many bytes there are per color in the global color table.

      The sort flag is supposed to be a flag that tells the decoder earlier colors are more important by sorting colors in a most to least useful fashion.

      And the size of the global color table is, well, how big the color table is.

      In our sunflower at byte 0xA, we have the result 0xF7

      ?? xxd Sunflower_as_gif_websafe_89a.gif | head -1 00000000: 4749 4638 3961 dc00 0501 -> f7 <- 00 0002 0102  GIF89a.......... 
      

      Or in binary that is: 1111 0111

      That basically means we have a fully loaded gif except that the GCT is not sorted.

                                   ┌──────────GCT not sorted                             ▼          by importance                        1111 0111                        ▲───  ───      GCT set───────────┘ ▲    ▲                          │    │    3 bytes per           │    └─────GCT is 768 bytes    color    ─────────────┘          (max size) (max resolution) 
      

      I suppose we’ve got this far and haven’t even discussed what the global color table is. The global color table holds the colors used in each section of bytes. They’re standard RGB values from 0-255 you could plug into any modern RGB color picker.

      Detour: Hey hold on, is that global color table optional?

      If you had a sharp eye, you might have noticed that the first bit in byte 0xA says the GCT can be optional. Well that’s interesting. How do we render an image without it specifying what colors it needs?

      According to the spec:

      Color Tables - Both color tables, the Global and the Local, are optional; if present, the Global Color Table is to be used with every image in the Data Stream for which a Local Color Table is not given; if present, a Local Color Table overrides the Global Color Table. However, if neither color table is present, the application program is free to use an arbitrary color table.

      Well that’s neat. Hey what if we remove the global color table to an image, what might modern renders do with our image? Something amazing I’m sure.

      Our image specifies color table size is 768 bytes. It starts on byte 0xA… say we just zero out the most significant bit of byte 0xA like so.

      And then delete until byte 789 (exclusive):

      ?? xxd Sunflower_as_gif_89a-no-gct.gif | head -1 00000000: 4749 4638 3961 dc00 0501 007f 8121 f904  GIF89a.......!.. 
      

      Now the first line ends like this which is still a perfectly valid gif. How might that look?

      no gct here!

      Amazing! Stupendous! Wonderful! As of the time of this writing, it’s just a perfectly black square. And this is the case in every single renderer I’ve tried. Gimp, Chrome, Firefox, Preview, gifiddle, you name it.

      That’s a little boring, but I’m not sure what I expected.

      Anyway back to the logical screen descriptor

      The logical screen descriptor continued

      After the bytes that describe the global color table, there are two final bytes describing the screen descriptor.

      Byte B is the background color which refers to an index into the global color table, and byte C is the pixel aspect ratio, describing the squareness of the pixel.

      ?? xxd Sunflower_as_gif_websafe_89a.gif | head -1 00000000: 4749 4638 3961 dc00 0501 f700 0002 0102  GIF89a..........                                     ^  ^                                     |  | Background color is color in index 0 of | GCT                                     |                                        Pixel aspect ratio is 0:0 or host                                        pixel aspect ratio. 
      

      Detour: Hey wait a second, pixel aspect ratio, what’s that?

      Pixels weren’t always square! Also bytes weren’t always 8 bits, but that’s a tangent I won’t go into.

      Gif, and actually some of the other most popular modern image formats support non-square pixels.

      Hey! I wonder how compliant our most popular gif renderers are when rendering non-square pixels. Probably very compliant. Let’s try a popular test in Firefox and Chrome and see how they look: http://frs.badcoffee.info/PAR_AcidTest/

      The finest renderers

      Uhh…. well that’s something. Those are in order: jpg, png, and gif. And Firefox, Chrome, and Preview all ignore the aspect ratio.

      Unfortunately this is widely unsupported and there is currently a 16 year old bug in Firefox for it: https://bugzilla.mozilla.org/show_bug.cgi?id=333377

      Even Gifiddle, the most compliant gif viewer I’ve found doesn’t support non-square pixels: https://github.com/ata4/gifiddle/issues/1

      We’ll discuss gifiddle more as we try out more obscure gif features.

      If you really want to display non-square pixels, you can sort of massage gimp to do it. Also grafx2 can apparently handle very specific odd resolutions of pixels. I have not tested it myself though.

      Onto the global color table

      The global color table (GCT) is easily the most boring part of the gif. You jump through the image in multiples of 3 from 0 to the size of the global color table. There really isn’t anything worth talking about here.

      The best way for me to illustrate this is to point to my awful-gif project and it’ll output all of the colors in the GCT of the sunflower (and maybe other images too).

      The GCT parsing is right here and you can see there really isn’t anything special about it.

      Run with:

      cargo run --quiet -- --gif-file ./experiments/Sunflower_as_gif_websafe.gif 
      

      The optional graphic control extension

      Now we have the graphic control extension (GCE), introduced by the extension introducer: 0x21 (extension introduced) and then 0xF9 (!)

      There are a number of extensions, but the graphic control extension is arguably one of the most important at least in modern day usage. The GCE is required to make gifs “animated” by allowing a delay time between frames, among other things.

      ?? xxd Sunflower_as_gif_websafe_89a.gif | head -50 | tail -2 00000300: 88ae b091 a5b1 a4b9 be94 887f 81 -> 21 f904  .............!.. 00000310: 0000 0000 <- 0021 fe51 4669 6c65 2073 6f75  .....!.QFile sou 
      

      This gif isn’t exactly animated, so there isn’t a lot going on here. Lots of zeroes as you can see, but we’ll still go into each byte.

      The first byte is the block size, which in this case is 0x04, but actually according to the spec is always 0x04.

      Detour: Hold on just a second, can we get rid of the block size then?

      Ah if the block size is always a static constant, it isn’t really important then, is it? Technically it’s part of the spec, but it doesn’t actually do anything. Let’s play our favorite game of opening it in the most popular image viewers.

      For these tests I’ll be using a much simpler gif to make it easier to see what happens:

      simple gif

      For the following tests I’ve modified it to remove the GCE. The modified version is kept in xxd format below. To can reassemble it:

      00000000: 4749 4638 3961 2000 3400 f0ff 00ff ffff  GIF89a .4....... 00000010: 0000 0021 f903 0500 0002 002c 0000 0000  ...!.......,.... 00000020: 2000 3400 0002 788c 8fa9 cb0b 0fa3 94ed   .4...x......... 00000030: cc7b abc1 1cea d075 5fc8 8d64 a69d 68a5  .{.....u_..d..h. 00000040: 4e66 eba5 702c 3675 cddc a5bd e34e bfcb  Nf..p,6u.....N.. 00000050: 0131 ace1 ea47 0405 9128 9f42 9714 2667  .1...G...(.B..&g 00000060: a70d 3564 bd1a b52e 25b7 f905 8729 de31  ..5d....%....).1 00000070: cd1c c9a2 016a 74db fc1e c7c3 f36f 9d7b  .....jt......o.{ 00000080: d7e6 af7b 6a7f f607 13d8 32a8 5258 55e6  ...{j.....2.RXU. 00000090: 9608 b728 d748 f768 1789 f751 b950 0000  ...(.H.h...Q.P.. 000000a0: 3b                                       ; 
      

      save it to a text file called invalid.hex and do: xxd -r invalid.hex > invalid.gif

      (If you can’t find the updated byte, it’s at byte: 0x16 and changed from 0x4 -> 0x03)


      First macOS Preview:

      preview tests

      Preview being standards compliant! We love to see it! Even if it technically doesn’t matter for a file format that will probably never get an update.


      Next let’s try Firefox:

      firefox tests

      Firefox knows it’s a static value and ignores the result of it. Not exactly standards compliant, but probably the smartest thing to do.


      chrome tests

      Chrome goes a little bananas when the block size is removed. Not exactly inverting it, it does… something which I’m not certain. Chrome is most certainly the least compliant with the standards here.

      Back to the graphics control extension

      After we read past the block size, we then get a packed field described as:

            <Packed Fields>  =     Reserved                      3 Bits                             Disposal Method               3 Bits                             User Input Flag               1 Bit                             Transparent Color Flag        1 Bit 
      

      All of these fields are set to 0 in our image, so I will just explain them.

      Reserved is set for when gif22a comes out and we need those three bits for something good.

      User input is for taking user input to advance a gif to the next image with a mouse click or keyboard press.

      The transparent index is for setting whether or not we should allow transparency.

      Detour: WAIT A SECOND GIFS CAN TAKE USER INPUT ???

      Yeah you read that right. Gifs can take user input to advance to the next frame. This poor guy built a whole site around recreating this feature with pngs. A shame he didn’t read the gif spec after being stuck inside for 2 years like me.

      While we’re in the weird features section of gif, we might as well discuss the other weird feature gif supports, the plain text extension.

      The plain text extension allows the gif maker to embed monospace text wherever they like with some basic styling directly on the image.

      The plain text extension like the user input extension, was likely never implemented by any gif viewer besides ones made by eccentric folk for fun like the guy who made gifiddle.

      BOB_89A.gif, likely the first gif ever posted on the internet is an example of a gif that uses both.

      Here BOB_89A.gif rendered in a modern browser (your own browser, if it’s not modern please update it):

      BOB_89A

      However if you put it into gifiddle, you get a very different result, with the last message being a very important truth.

      I won’t spoil the surprise though, give it a right click download and then put that gif into gifiddle to see what happens.

      Gifiddle link: http://ata4.github.io/gifiddle/

      Neither of these features are supported by any modern browser or gif viewer.

      If you’d like to read more about the plain text extension, you can do so here.

      The optional comment extension

      The comment extension to come next, and actually may appear anywhere a block may begin. However it most often appears in this part of a gif.

      The comment section is only allowed to contain 7 bit ascii and is intended for humans to read.

      Since the comment section is just ascii, you can just fire off strings and find the comment in the output:

      ?? strings Sunflower_as_gif_websafe_89a.gif | head -7 | tail -1 QFile source: https://commons.wikimedia.org/wiki/File:Sunflower_as_gif_websafe.gif 
      

      In this image it starts around byte 0x310 in our image:

      ?? xxd Sunflower_as_gif_websafe_89a.gif | head -55 | tail -6 00000310: 0000 0000 0021 fe51 4669 6c65 2073 6f75  .....!.QFile sou 00000320: 7263 653a 2068 7474 7073 3a2f 2f63 6f6d  rce: https://com 00000330: 6d6f 6e73 2e77 696b 696d 6564 6961 2e6f  mons.wikimedia.o 00000340: 7267 2f77 696b 692f 4669 6c65 3a53 756e  rg/wiki/File:Sun 00000350: 666c 6f77 6572 5f61 735f 6769 665f 7765  flower_as_gif_we 00000360: 6273 6166 652e 6769 6600 2c00 0000 00dc  bsafe.gif.,..... 
      

      Signified again by a BANG (!) (extension introduced) and 0xfe comment extension. Afterwards the comment should be read for 255 bytes or until a 0x00 is read.

      The rest of the image data

      There isn’t much more to talk about after that. This image skips out on most other gif features like the local color table and animations, so the majority of the rest of the gif is just data and terminators.

      I’ll be perfectly honest, lzw compression isn’t terribly difficult to learn, but explaining it is not the purpose of this blogpost. If you’d like to learn it, Matthew Flickinger has a great post on his site about it.

      Bonus section: True color gifs

      Did you know that gifs can be true color? It requires a little insanity, but if you remember the world “local color table” then it might make sense. Each data segment is allowed to have its own local color table, and thus if you break a gif up into enough pieces, you get true color!

      true color gif

      Most gifs don’t do this for a couple of reasons.

      First, the resulting image will be gigantic. Each new palette of 256 colors will consume an additional 768 bytes.

      Second, renderers nowadays will not “properly” render the image. Browsers by default will often put a delay of 0.1 between frames if not specified.

      However a truly compliant gif renderer will properly display a truecolor gif. So if you have the space, and the memory, and the spare CPU for it, why not have a true color gif? We can all have a little true color as a treat.

      If you’d like more information on truecolor gifs, Wikipedia has a whole section on it called True color.

      Wrapping things up

      Anyway if you made it this far, I appreciate it. This was much longer than my average post here. But there was a lot to say!

      In fact there’s really a lot more to say too. There are more parts of the gif spec I didn’t go over, and nuances to the format that I could, I dunno, write a whole spec about.

      If you’re interested in learning more about gif, I recommend checking out the spec and all of the other links I added at the top of the post.

      Otherwise, thanks for reading ??


      1. https://en.wikipedia.org/wiki/GIF#history ??
      2. Sunflower rehosted from Wikipedia article on gifs (see footnote #1) ??
      3. Ok gif87a technically supports animation in a more limited format. But I’ve already written almost 3500 words at the time of this footnote and I don’t want to get into every detail. For more information you can try out the gif87a animation examples on the gifiddle repo: https://github.com/ata4/gifiddle ??
      4. For more information, see section 18 (Logical screen descriptor) of the gif spec. ??
      5. For more info, see section 4, About the Document. from the gif spec:https://www.w3.org/Graphics/GIF/spec-gif89a.txt ??
      posted @ 2022-04-28 15:08  輕狂書生han  閱讀(7626)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 久久精品国产99精品国产2021| 亚洲综合一区二区三区在线| 亚洲高清 一区二区三区| 美姑县| 亚洲男人的天堂av手机在线观看 | 色综合国产一区二区三区| 国产精品午夜福利片国产| 亚洲综合一区二区三区| 国产成人综合色视频精品| 国产精品久久中文字幕网| 国内精品伊人久久久久AV一坑 | 中文字幕无码免费不卡视频| 久青草视频在线免费观看| 激情国产av做激情国产爱| 日本a在线播放| 国产av熟女一区二区三区| 中国美女a级毛片| 91亚洲国产三上悠亚在线播放| 国产精品日韩av一区二区| 无码国产偷倩在线播放老年人| 国产精品久久欧美久久一区| 亚洲av伦理一区二区| 成人国产乱对白在线观看 | 国产色a在线观看| 伊人成伊人成综合网222| 襄樊市| 精品亚洲国产成人av| 久久天天躁狠狠躁夜夜婷| 亚洲色偷偷偷网站色偷一区| 亚洲日韩精品无码一区二区三区| 亚洲国产精品人人做人人爱| 丰满少妇熟乱xxxxx视频| 成人av午夜在线观看| 国产精品免费无遮挡无码永久视频| 亚洲狠狠狠一区二区三区| 亚洲精品一区二区制服| 久久婷婷五月综合色精品| 久久久久久av无码免费网站| 亚洲成人av在线系列| 激情综合色五月六月婷婷| 久国产精品韩国三级视频|