GIFデコード処理を作る際に、注意しなければいけないポイント
2. 画像を展開した後、余ったデータを読み飛ばさなければいけない
前のページで説明したように、GIFデコーダのプログラムは、終了コードを読み込む前に、画像の展開終了を判断します。
画像が一枚だけ入ったGIFファイルならば、それで処理終了としてしまってOKなのですが、アニメーションGIFファイルの場合は問題があります。
一つ目の画像データの後に、次のアニメーションフレームの画像データが続いているので、処理を継続して、次の画像データをデコードする必要があるからです。
プログラムは、終了コードを読み込む前に展開終了を判断したので、まだ、一つ目の画像データの終了コードが読み込まれずに残っています。
そこで、次のアニメーションフレームの画像データを読み込み始める前に、一つ目の画像データの終了コードを読み飛ばさなければなりません。
終了コードを読み飛ばすためには、データブロックの残りのビットを捨ててしまえばよいのです。
前のページのGIFファイルを例に挙げると、プログラムは、6バイトのデータブロックのうち、青色で囲んだ範囲まで読み込んで、展開終了と判断します。
終了コードの4ビット分が、まだ読み込まれずに残っています。
プログラムは、残っている終了コードのことは気にせずに、6バイトのデータブロックの末尾まで、読み込みポインタを進めてしまえば良いのです。
変数pBlockBaseにデータブロックの先頭アドレス、変数BlockSizeにデータブロックのバイト数が入っているとすれば、
・
・
・
一つ目の画像の展開終了
pBlockBase += BlockSize; // 終了コードを読み飛ばして、現在のデータブロックの末尾まで進める。
pBlockBase += 1; // 画像データ終了を表す「00」を読み飛ばす。
二つ目の画像の展開開始
・
・
・
たいていは上述の方法でOKなのですが、稀に、2フレーム目の画像がデコードエラーになってしまうGIFファイルがありました。
たとえば、このようなGIFファイルです。
15×15ピクセル・2フレームのアニメーションGIFファイルで、1フレーム目は全ピクセルが違う色、2フレーム目は白一色で塗りつぶしてあります。
GIFファイルの内容の一部を、HEXエディタでダンプしてみました。
緑色の部分が、一つ目の画像データです。
画像データの部分だけを抜き出して、説明を加えてみました。
ちょっと長いですが・・・
上図の右下あたりに注目してください。
GIFのデータブロックは、各々が最大255バイトまでという制限があります。
画像全体のピクセルに相当するコードは、最初のデータブロックに収まっているのですが、終了コードだけが収まりきらず、次のデータブロックに分割されてしまっています。
プログラムは、最初のデータブロックの末尾にある、終了コードを読み込む前に、展開終了を判断します。
一つ目の画像データの終了コードを読み飛ばすために、最初のデータブロックの末尾まで読み込みポインタを進め、引き続き、次のアニメーションフレームのデコードを開始しようとします。
ところが実際には、読み込みポインタは、まだ、一つ目の画像データの、二つ目のデータブロックを指しています。
プログラムは、一つ目の画像データの終了コードの残り3ビットの部分を、次のアニメーションフレームの画像データと見なしてデコード処理を開始してしまって、デコードエラーになるのです。
エラーを避けるためには、一つ目の画像データの終了コードを読み飛ばす際、終了コードの一部のビットが、次のデータブロックにも分割されて格納されていないかを調べます。
もし、終了コードの一部のビットが、次のデータブロックにも分割されて格納されていたら、次のデータブロックも読み飛ばせば良いのです。
・
・
・
一つ目の画像の展開終了
pBlockBase += BlockSize; // 終了コードを読み飛ばして、現在のデータブロックの末尾まで進める。
BlockSize = *pBlockBase++; // 画像データ終了を表す「00」、または、次のデータブロックのバイト数を読み込む。
if(BlockSize != 0x00) { // 終了コードの一部のビットが、次のデータブロックに分割されて格納されていたら・・・
pBlockBase += BlockSize; // 次のデータブロックも読み飛ばす。
pBlockBase += 1; // 画像データ終了を表す「00」を読み飛ばす。
}
二つ目の画像の展開開始
・
・
・
Sun Apr 12 20:48:20 JST 2009 Naoyuki Sawa (nsawa@piece-me.org)