批量修改GIF的循环次数


前言

最近收获了不少APNG格式的兔子表情包,用APNG2GIF工具批量转成gif后,发现在微信上,只能播放一次!于是便去研究如何批量修改gif的循环次数。
搜索引擎输入:批量修改gif循环次数,并没有找到合适的教程。搜到的结果要么是用PS改次数的教程,要么是针对单个gif的工具,无法批量。虽然我得到了300多张可爱的兔兔,但是要我一张一张手动去改,怕不也要累死……

研究

于是便开始从GIF格式本身开始研究,在维基百科 界面,找到了很多有用的信息。

从维基百科中得以知道:gif的循环次数实现在GIF89A标准的Application Extension的block中,截取百科中的相关片段如下:


byte#  hexadecimal  text or
(hex)               value     Meaning
0:     47 49 46
       38 39 61     GIF89a    Header
                              Logical Screen Descriptor
6:     90 01        400        - width in pixels
8:     90 01        400        - height in pixels
A:     F7                      - GCT follows for 256 colors with resolution 3 x 8bits/primary
B:     00           0          - background color #0
C:     00                      - default pixel aspect ratio
D:                            Global Color Table
:
30D:   21 FF                  Application Extension block
30F:   0B           11         - eleven bytes of data follow
310:   4E 45 54
       53 43 41
       50 45        NETSCAPE   - 8-character application name
       32 2E 30     2.0        - application "authentication code"
31B:   03           3          - three more bytes of data
31C:   01           1          - index of the current data sub-block (always 1 for the NETSCAPE block)
31D:   FF FF        65535      - unsigned number of repetitions
31F:   00                      - end of App Extension block
320:   21 F9                  Graphic Control Extension for frame #1
322:   04           4          - four bytes in the current block
323:   04           000.....   - reserved; 5 lower bits are bit field
                    ...001..   - disposal method 1: do not dispose
                    ......0.   - no user input
                    .......0   - transparent color is not given
324:   09 00                   - 0.09 sec delay before painting next frame
326:   FF                      - transparent color index (unused in this frame)
327:   00                      - end of GCE block
328:   2C                     Image Descriptor of frame #1
329:   00 00 00 00  (0,0)      - NW corner of frame at 0, 0
32D:   90 01 90 01  (400,400)  - Frame width and height: 400 × 400
331:   00                      - no local color table; no interlace
332:   08           8         LZW min code size; Image Data of frame #1 beginning 
333:   FF           255        - 255 bytes of LZW encoded image data follow
334:                data
433:   FF           255        - 255 bytes of LZW encoded image data follow
                    data
                     :
92C0:  00                      - end of LZW data for this frame
92C1:  21 F9                  Graphic Control Extension for frame #2
 :                                                            :
EDABD: 21 F9                  Graphic Control Extension for frame #44
 :
F48F5: 3B                     File terminator

我们可以看到,其中的30D行,定义Application Extension头。31D行,定义循环次数。

再去相关网站查看对Application Extension的定义,截取如下:

            i) Extension Introducer - Defines this block as an extension. This
            field contains the fixed value 0x21.

            ii) Application Extension Label - Identifies the block as an
            Application Extension. This field contains the fixed value 0xFF.

            iii) Block Size - Number of bytes in this extension block,
            following the Block Size field, up to but not including the
            beginning of the Application Data. This field contains the fixed
            value 11.

            iv) Application Identifier - Sequence of eight printable ASCII
            characters used to identify the application owning the Application
            Extension.

从这我们得知,我们可以通过0x21 0xFF 0x0B 这三个byte来定位到Application Extension的位置。(或者通过Identifier,一般为字符NETSCAPE)

修改与验证

由上面的信息,我们可以得到以下修改步骤:

  1. 通过0x21 0xFF 0x0B byte定位Application Extension的位置
  2. 通过Application Extension的位置找到gif的循环次数,偏移量为+16
  3. 修改循环次数为0xFF 0xFF,实现无限循环

简单的验证代码:

data, _ := ioutil.ReadFile("兔兔.gif")
index := bytes.Index(data, []byte{0x21, 0xFF, 0x0B})
fmt.Println(data[index+16 : index+18])

得到的结果为[3,0],即0x03 0x00,表明这个gif只循环了4次

修改代码

func UnlimitedGifRepetitions(path string) ([]byte, error) {
    data, err := ioutil.ReadFile(path)
    if err != nil {
        return nil, err
    }
    index := bytes.Index(data, []byte{0x21, 0xFF, 0x0B})
    if index < 0 {
        return nil, fmt.Errorf("cannot Found Gif Application Extension in %s", path)
    }
    data[index+16] = 255
    data[index+17] = 255
    return data, nil
}

修改完后,再次发微信验证下,完美无限循环!

来欣赏下兔兔:

声明:Asteroid B612|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - 批量修改GIF的循环次数


人生未必要有意义