はじめに
以前、GIFアニメでハマった記事を書く際にGIFフォーマットの詳細という素晴らしい記事が見つけました。
GIFファイルの中の画像にはそれぞれディレイタイムが2バイトで設定されており、0.01秒単位で設定できるようです。
目標は【Graphic Control Extension】
GIFファイルの中には画像データがあるのは当然ですが、画像データの前にはGraphic Control Extensionという8バイトのブロックがあるそうです。
Graphic Control Extension
- Extension Introducer(1B) = 0x21 <= 印 1
- Graphic Control Label(1B) = 0xf9 <= 印 2
- Block Size(1B) = 0x04 <= 印 3
- R(3b),DM(3b),UIF(1b),TCF(1b)
- Delay Time(2B) <= ここがディレイタイム
- Transparent Color Index(1B)
- Block Terminator(1B) = 0x00
バイナリエディタで確認
ディレイタイムを1秒にセットしたGIFアニメをバイナリエディタで確認してみました。
ちゃんと印1~30x21f904
に続いてディレイタイム0x6400
があります。
1秒で設定してるので0.01で割ると100なので、100 = 0x0064
となり、リトルエンディアンで格納すると0x6400
で間違いありません。
この値をゴニョゴニョすればコマごとの表示時間を変更できるということですね。
やってみよう
ChangeDelay.py
# GIFファイルをバイナリデータとして開く。
FileName = 'test.gif'
with open(FileName,'rb') as f:
Bin = f.read()
# Graphic Control Extentionを探して位置とディレイ値をListに記録する。
GCE = b'\x21\xf9\x04'
DelayList = []
point = 0
while point < len(Bin):
start = Bin.find(GCE,point)
if start<0:break
delay = int.from_bytes(Bin[start+4:start+6],byteorder="little")
DelayList.append([start+4,delay])
point = start+1
# 最初の1枚を1秒に、半分以降を5倍の長さに変更する。
DelayList[0][1]=100
for i in range(1,len(DelayList)//2):
DelayList[-i][1]*=5
# バイナリデータを書き換える。
for point,delay in DelayList:
Bin = Bin[:point] + delay.to_bytes(2,byteorder="little") + Bin[point+2:]
# バイナリデータをGIFとして保存。
with open('temp.gif','wb') as f:
f.write(Bin)