Upgrade to Pro — share decks privately, control downloads, hide ads and more …

GoならわかるGIF

 GoならわかるGIF

GIFアニメーションの仕様・構造をimage/gifの実装を読みながら理解する話。

Avatar for decafe09

decafe09

April 19, 2019
Tweet

More Decks by decafe09

Other Decks in Programming

Transcript

  1. 今日の話 Umeda.go 2018 Autumn であまり話せなかった GIF アニメー ションについて話します。 よくあるWeb サー

    ビスのAPI を作ったらいろいろ勉強になった話 https://speakerdeck.com/decafe09/webapplicationapitips?slide=20
  2. t y p e G I F s t r

    u c t { I m a g e [ ] * i m a g e . P a l e t t e d D e l a y [ ] i n t s e c o n d . L o o p C o u n t i n t / / ★ ルー プ回数 D i s p o s a l [ ] b y t e C o n f i g i m a g e . C o n f i g B a c k g r o u n d I n d e x b y t e } t y p e C o n f i g s t r u c t { C o l o r M o d e l c o l o r . M o d e l W i d t h , H e i g h t i n t / / ★ 幅, 高さ }
  3. f u n c D e c o d e

    A l l ( r i o . R e a d e r ) ( * G I F , e r r o r ) { v a r d d e c o d e r i f e r r : = d . d e c o d e ( r , f a l s e , t r u e ) ; e r r ! = n i l { r e t u r n n i l , e r r } g i f : = & G I F { I m a g e : d . i m a g e , L o o p C o u n t : d . l o o p C o u n t , / / ★ ルー プ回数 D e l a y : d . d e l a y , D i s p o s a l : d . d i s p o s a l , C o n f i g : i m a g e . C o n f i g { C o l o r M o d e l : d . g l o b a l C o l o r T a b l e , W i d t h : d . w i d t h , / / ★ 幅 H e i g h t : d . h e i g h t , / / ★ 高さ } , B a c k g r o u n d I n d e x : d . b a c k g r o u n d I n d e x , } r e t u r n g i f , n i l }
  4. 使うだけならほぼここまで。 今日の話はここから GIF アニメー ションの仕様を image/gif のコー ドを読みながら 理解する話。 v

    a r d d e c o d e r i f e r r : = d . d e c o d e ( r , f a l s e , t r u e ) ; e r r ! = n i l { r e t u r n n i l , e r r }
  5. t y p e d e c o d e

    r s t r u c t { r r e a d e r / / F r o m h e a d e r . v e r s s t r i n g / / ★ バー ジョン w i d t h i n t / / ★ 幅 h e i g h t i n t / / ★ 高さ l o o p C o u n t i n t / / ★ ルー プ回数 d e l a y T i m e i n t b a c k g r o u n d I n d e x b y t e d i s p o s a l M e t h o d b y t e / / F r o m i m a g e d e s c r i p t o r . i m a g e F i e l d s b y t e / / F r o m g r a p h i c s c o n t r o l . t r a n s p a r e n t I n d e x b y t e h a s T r a n s p a r e n t I n d e x b o o l / / C o m p u t e d . g l o b a l C o l o r T a b l e c o l o r . P a l e t t e / / U s e d w h e n d e c o d i n g . d e l a y [ ] i n t d i s p o s a l [ ] b y t e i m a g e [ ] * i m a g e . P a l e t t e d t m p [ 1 0 2 4 ] b y t e / / m u s t b e a t l e a s t 7 6 8 s o w e c a n r e a d c o l o r t a b l e }
  6. GIF アニメー ションのバイトコー ド例 0 0 0 0 0 0

    0 0 : 4 7 4 9 4 6 3 8 3 9 6 1 c 8 0 0 c 8 0 0 0 0 0 0 0 0 2 1 f f 0 b G I F 8 9 a . . . . . . . ! . . 0 0 0 0 0 0 1 0 : 4 e 4 5 5 4 5 3 4 3 4 1 5 0 4 5 3 2 2 e 3 0 0 3 0 1 0 3 0 0 0 0 N E T S C A P E 2 . 0 . . . . . 0 0 0 0 0 0 2 0 : 2 1 f 9 0 4 0 0 0 a 0 0 0 0 0 0 2 c 0 0 0 0 0 0 0 0 c 8 0 0 c 8 ! . . . . . . . , . . . . . . . : : 0 0 0 0 6 7 5 0 : 9 b 0 2 0 4 7 5 d e c 7 9 3 2 f 7 f 3 2 2 0 0 0 3 b . . . u . . . / . 2 . ;
  7. GIF アニメー ションの構成例 意味 ブロック ヘッダー Header, Logical Screen Descriptor

    GIF アニ& 繰り返し回数 Application Extention ‑‑‑ ‑‑‑ 遅延、 透過など* Graphic Control Extension 画像* Image Descriptor, Local Color Table, Table Based Image Data ‑‑‑ ‑‑‑ ファイル終端 Trailer ※ 遅延、 透過など*, 画像* は繰り返し。 ※ 再生秒数は、Graphic Control Extension の情報を使います。
  8. f u n c ( d * d e c

    o d e r ) d e c o d e ( r i o . R e a d e r , c o n f i g O n l y , k e e p A l l F r a m e s b o o l ) e r r o r { i f r r , o k : = r . ( r e a d e r ) ; o k { d . r = r r } e l s e { d . r = b u f i o . N e w R e a d e r ( r ) } d . l o o p C o u n t = - 1 / / ★ ルー プ回数の初期化 e r r : = d . r e a d H e a d e r A n d S c r e e n D e s c r i p t o r ( ) / / その1 ヘッダー f o r { c , e r r : = r e a d B y t e ( d . r ) s w i t c h c { c a s e s E x t e n s i o n : i f e r r = d . r e a d E x t e n s i o n ( ) / / その2 G I F アニ& 繰り返し回数 c a s e s I m a g e D e s c r i p t o r : i f e r r = d . r e a d I m a g e D e s c r i p t o r ( k e e p A l l F r a m e s ) c a s e s T r a i l e r : i f l e n ( d . i m a g e ) = = 0 { r e t u r n f m t . E r r o r f ( " g i f : m i s s i n g i m a g e d r e t u r n n i l d e f a u l t : r e t u r n f m t . E r r o r f ( " g i f : u n k n o w n b l o c k t y p e : 0 x % . 2 x " , c ) } } }
  9. こぼれ話 Go 1.10 まではLoopCount の初期化が正しくされていなかったため、 未指定で作成しようとすると、 無限ルー プ(loopCount = 0)

    となっていました。 Go 1.11 で修正されています。 Non‑looping animated GIFs are now supported. They are denoted by having a LoopCount of ‑1. https://golang.org/doc/go1.11#image/gif https://go‑review.googlesource.com/c/go/+/93076/4/src/image/gif/reader.go#b563
  10. ヘッダー 項目 詳細 内容 Byte/Bit Header Signature GIF 3 Bytes

    Version 87a、89a 3 Bytes Logical Screen Descriptor Logical Screen Width •px 2 Bytes Logical Screen Height •px 2 Bytes Global Color Table Flag ‑ 1 Bit Color Resolution ‑ 3 Bits Sort Flag ‑ 1 Bit Size of Global Color Table ‑ 3 Bits Background Color Index ‑ 1 Byte Pixel Aspect Ratio ‑ 1 Byte 0 0 0 0 0 0 0 0 : [ 4 7 4 9 4 6 ] [ 3 8 3 9 6 1 ] [ c 8 0 0 ] [ c 8 0 0 ] [ 0 0 ] [ 0 0 ] [ 0 0 ] 2 1 f f 0 b G I F 8 9 a . . . . . . . ! . .
  11. f u n c ( d * d e c

    o d e r ) r e a d H e a d e r A n d S c r e e n D e s c r i p t o r ( ) e r r o r { e r r : = r e a d F u l l ( d . r , d . t m p [ : 1 3 ] ) i f e r r ! = n i l { r e t u r n f m t . E r r o r f ( " g i f : r e a d i n g h e a d e r : % v " , e r r ) } d . v e r s = s t r i n g ( d . t m p [ : 6 ] ) / / ★ バー ジョン i f d . v e r s ! = " G I F 8 7 a " & & d . v e r s ! = " G I F 8 9 a " { r e t u r n f m t . E r r o r f ( " g i f : c a n ' t r e c o g n i z e f o r m a t % q " , d . v e r s ) } d . w i d t h = i n t ( d . t m p [ 6 ] ) + i n t ( d . t m p [ 7 ] ) < < 8 / / ★ 幅 d . h e i g h t = i n t ( d . t m p [ 8 ] ) + i n t ( d . t m p [ 9 ] ) < < 8 / / ★ 高さ i f f i e l d s : = d . t m p [ 1 0 ] ; f i e l d s & f C o l o r T a b l e ! = 0 { d . b a c k g r o u n d I n d e x = d . t m p [ 1 1 ] / / r e a d C o l o r T a b l e o v e r w r i t e s t h e c o n t e n t s o f d . t m p , b u t t h a t ' s O K . i f d . g l o b a l C o l o r T a b l e , e r r = d . r e a d C o l o r T a b l e ( f i e l d s ) ; e r r ! = n i l r e t u r n e r r } } / / d . t m p [ 1 2 ] i s t h e P i x e l A s p e c t R a t i o , w h i c h i s i g n o r e d . r e t u r n n i l }
  12. 項目 詳細 内容 Byte/Bit Application Extension Extension Introducer 0x21 1

    Byte Extension Label 0xFF 1 Byte Block Size ‑ 1 Byte Application Identifier NETSCAPE 8 Bytes Application Authentication Code 2.0 3 Bytes Block Size* 3 1 Byte Data Value* 0x01 + ルー プ回数(2 Bytes) 3 Bytes Block Terminator ‑ 1 Byte 0 0 0 0 0 0 0 0 : 4 7 4 9 4 6 3 8 3 9 6 1 c 8 0 0 c 8 0 0 0 0 0 0 0 0 [ 2 1 ] [ f f ] [ 0 b ] G I F 8 9 a . . . . . . . ! . . 0 0 0 0 0 0 1 0 : [ 4 e 4 5 5 4 5 3 4 3 4 1 5 0 4 5 ] [ 3 2 2 e 3 0 ] [ 0 3 ] [ 0 1 0 3 ] 0 0 0 0 N E T S C A P E 2 . 0 . . . . .
  13. f u n c ( d * d e c

    o d e r ) r e a d E x t e n s i o n ( ) e r r o r { e x t e n s i o n , e r r : = r e a d B y t e ( d . r ) i f e r r ! = n i l { r e t u r n f m t . E r r o r f ( " g i f : r e a d i n g e x t e n s i o n : % v " , e r r ) } s i z e : = 0 s w i t c h e x t e n s i o n { c a s e e T e x t : s i z e = 1 3 c a s e e G r a p h i c C o n t r o l : r e t u r n d . r e a d G r a p h i c C o n t r o l ( ) c a s e e C o m m e n t : / / n o t h i n g t o d o b u t r e a d t h e d a t a . c a s e e A p p l i c a t i o n : b , e r r : = r e a d B y t e ( d . r ) i f e r r ! = n i l { r e t u r n f m t . E r r o r f ( " g i f : r e a d i n g e x t e n s i o n : % v " , e r r ) } / / T h e s p e c r e q u i r e s s i z e b e 1 1 , b u t A d o b e s o m e t i m e s u s e s 1 0 . s i z e = i n t ( b ) d e f a u l t : r e t u r n f m t . E r r o r f ( " g i f : u n k n o w n e x t e n s i o n 0 x % . 2 x " , e x t e n s i o n ) } i f s i z e > 0 { i f e r r : = r e a d F u l l ( d . r , d . t m p [ : s i z e ] ) ; e r r ! = n i l { r e t u r n f m t . E r r o r f ( " g i f : r e a d i n g e x t e n s i o n : % v " , e r r ) } }
  14. / / A p p l i c a t

    i o n E x t e n s i o n w i t h " N E T S C A P E 2 . 0 " a s s t r i n g a n d 1 i n d a t a m e a n s / / t h i s e x t e n s i o n d e f i n e s a l o o p c o u n t . i f e x t e n s i o n = = e A p p l i c a t i o n & & s t r i n g ( d . t m p [ : s i z e ] ) = = " N E T S C A P E 2 . 0 " { n , e r r : = d . r e a d B l o c k ( ) i f e r r ! = n i l { r e t u r n f m t . E r r o r f ( " g i f : r e a d i n g e x t e n s i o n : % v " , e r r ) } i f n = = 0 { r e t u r n n i l } i f n = = 3 & & d . t m p [ 0 ] = = 1 { d . l o o p C o u n t = i n t ( d . t m p [ 1 ] ) | i n t ( d . t m p [ 2 ] ) < < 8 / / ★ ルー プ回数 } } f o r { n , e r r : = d . r e a d B l o c k ( ) i f e r r ! = n i l { r e t u r n f m t . E r r o r f ( " g i f : r e a d i n g e x t e n s i o n : % v " , e r r ) } i f n = = 0 { r e t u r n n i l } } }
  15. まとめ GIF アニメー ションもGo ならわかる。 バイトコー ドをGo の実装と一緒に読んでいける。 読み取りで使うときは、DecodeAll() で

    GIF を取得すればOK。 GIF87a と GIF89a などのバー ジョンは GIF には入っていないので、 バイトコー ドで。