Slide 1

Slide 1 text

Go ならわかるGIF Umeda.go 2019 Spring 2019.04.20 @decafe09

Slide 2

Slide 2 text

自己紹介 @decafe09 小野寺 俊也 シナジー マー ケティング株式会社 最近、 サー バー サイドKotlin をはじめました。

Slide 3

Slide 3 text

今日の話 Umeda.go 2018 Autumn であまり話せなかった GIF アニメー ションについて話します。 よくあるWeb サー ビスのAPI を作ったらいろいろ勉強になった話 https://speakerdeck.com/decafe09/webapplicationapitips?slide=20

Slide 4

Slide 4 text

今日の話 GIF アニメー ションの仕様を image/gif のコー ドを読みながら 理解する話。

Slide 5

Slide 5 text

広告で使うGIF の入稿規定の例 GIF89a であること。 幅●px 以内、 高さ●px 以内であること。 無限ルー プしないこと。 再生秒数が● 秒以内であること。( 今回は割愛)

Slide 6

Slide 6 text

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 / / ★ 幅, 高さ }

Slide 7

Slide 7 text

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 }

Slide 8

Slide 8 text

使うだけならほぼここまで。 今日の話はここから 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 }

Slide 9

Slide 9 text

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 }

Slide 10

Slide 10 text

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 . ;

Slide 11

Slide 11 text

GIF アニメー ションの構成例 意味 ブロック ヘッダー Header, Logical Screen Descriptor GIF アニ& 繰り返し回数 Application Extention ‑‑‑ ‑‑‑ 遅延、 透過など* Graphic Control Extension 画像* Image Descriptor, Local Color Table, Table Based Image Data ‑‑‑ ‑‑‑ ファイル終端 Trailer ※ 遅延、 透過など*, 画像* は繰り返し。 ※ 再生秒数は、Graphic Control Extension の情報を使います。

Slide 12

Slide 12 text

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 ) } } }

Slide 13

Slide 13 text

こぼれ話 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

Slide 14

Slide 14 text

ヘッダー 項目 詳細 内容 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 . . . . . . . ! . .

Slide 15

Slide 15 text

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 }

Slide 16

Slide 16 text

項目 詳細 内容 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 . . . . .

Slide 17

Slide 17 text

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 ) } }

Slide 18

Slide 18 text

/ / 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 } } }

Slide 19

Slide 19 text

今日はここまで。 続きは image/gif で!

Slide 20

Slide 20 text

まとめ GIF アニメー ションもGo ならわかる。 バイトコー ドをGo の実装と一緒に読んでいける。 読み取りで使うときは、DecodeAll() で GIF を取得すればOK。 GIF87a と GIF89a などのバー ジョンは GIF には入っていないので、 バイトコー ドで。

Slide 21

Slide 21 text

ご静聴ありがとうございました。 参考: https://www.w3.org/Graphics/GIF/spec‑gif89a.txt