giflibを使ってrawファイルに変換する

http://diary.awm.jp/~yoya/data/2008/09/16/gif_dump.c?
のプログラムなのですが、
画像のヘッダ情報はうまくとれるのですが
肝心の画像の先頭ピクセルにうまくアクセスできていない様に思います。

といった質問を見つけました。
教えて!goo でログインしても解答出来なかったので、ここで回答。

回答

以下に抜粋する gif_dump.c のコードで参照している変数、
RasterBits が画像の先頭pixel に相当します。

printf("RasterBits:\n");
for (y = 0 ; y < desc.Height ; y++) {
    printf("  y=%d:", y + desc.Top);
    for(x = 0 ; x < desc.Width ; x++) {
        printf(" %02x", image.RasterBits[ii] & 0xff);
        ii++;
    }
    printf("\n");
}

でも、これ 1 byte なのに? RGB は 3bytes じゃないの?
という疑問があると思いますので、パレット形式について以下に説明します。

ビットマップとパレット

ビットマップ画像は、RGB の要素を持つ縦横のマトリックスなので、
データとしては、RGB(24bit)の配列(←縦x横の長さの)として表現出来ます。

上記の質問者はこの形式のデータが、GIFLIB の構造体の何処に埋まっているんだろう。
といった意図での質問だと思われますが、
GIF はパレット形式で画像を表現していて giflib は、パレット形式のまま取り出す
API しかないので、ビットマップ形式には自分で変換する必要があります。

パレットとカラーマップ

パレット形式というのは、まず画像で使われている色を並べたテーブル
(カラーマップ)を用意して、画像データの方は RGB(24bit)でなく、
色インデックス(GIF は最大 256色なので、8bit)のリストで表現します。

比較すると、

  • 表現する画像
 +--+--+
 |赤|緑|
 +--+--+
 |青|黒|
 +-----+
  • ビットマップ形式
 255,0,0, 0,255,0, 0,0,255, 0,0,0
 <-- ラスタデータ  ------------->
  • パレット形式
 255,0,0, 0,255,0, 0,0,255, 0,0,0, 0, 1, 2, 3
 <-- カラーマップ --------------> <ラスタデータ->

giflib が取り出せるのは、このカラーマップと色インデックス値の配列です。

上の例だとビットマップ画像よりデータが膨らんでしまっていますが、
画像のピクセル数が増えるほど、パレット形式の方が有利になります。
(1pixel につき 1 byte しか増えないので)

giflib の構造体的には?

↑これを元に説明すると、
ColorMapObject *ColorMap;
で表現されているのが、カラーマップ (SColorMap と ImageDesc.ColorMap があるのは後述)で、

printf("RasterBits:\n");
for (y = 0 ; y < desc.Height ; y++) {
    printf("  y=%d:", y + desc.Top);
    for(x = 0 ; x < desc.Width ; x++) {
        printf(" %02x", image.RasterBits[ii] & 0xff);
        ii++;
    }
    printf("\n");
}

で表示しているのが、パレット形式のラスタデータ(カラーマップへのインデックスの配列)です。

SColorMap と ImageDesc.ColorMap ?

GIF は複数枚の画像を入れる事ができて、それを一つの共通したカラーマップで見るのが
SColorMap(グローバルカラーマップ)で、一枚一枚独立してカラーマップを持つのが
ImageDesc.Colormap (ローカルカラーマップ)です。

この、グローバル/ローカル・カラーマップはアニメーションGIF で意識する事になります。

util/gif2rgb.c

尚、giflib の中に util/gif2rgb.c というプログラムがあって、
GIF のデータを RGB の RAW なデータに変換してくれます。

変換の入力にあたるのが、パレット形式のラスター情報
(カラーマップへのインデックスに配列)が GifRow で、
ColorMapEntry がインデックスに対応する色情報。
出力側は、BufferP で RGB の形式でのラスタデータとなります。

for (j = 0, BufferP = Buffer; j < ScreenWidth; j++) {
    ColorMapEntry = &ColorMap->Colors[GifRow[j]];
    *BufferP++ = ColorMapEntry->Red;
    *BufferP++ = ColorMapEntry->Green;
    *BufferP++ = ColorMapEntry->Blue;
}

教えて!goo で解答出来ない?

gooID でログインして、教えて!goo の中で、

「この質問に回答します」のボタンを押したら会員IDの設定画面が出てきて、

これに入力しようとすると、

このメールアドレスはすでに登録されています 

とか出てきて、次進めません。

教えて!goo の ID 連携まわりが残念すぎます。