「りも」トップページへ
TIPS index へ

GdkPixbuf を使う

Jan.04.2000

GdkPixbuf の基本とその使用方法。
主に画像ファイルのローダーとしての GdkPixbuf と、Imlib と比べて なにができるライブラリなのかの把握。


・ GdkPixbuf と Imlib

GdkPixbuf は Gnome プロジェクトにおいて現在使用されている Imlib の 代換となるべく開発されたライブラリです。つまり、次の Gnome では Imlib を使わなくて済むようにしようと、Gnome のお膝もとで作られた ライブラリということです。
これを書いている時点(Mar.06.2000)での GdkPixbuf のバージョンは 0.6.0 で、まだ Imlib と同等の全機能を備えているわけではありませんが、 必要な部分はおおよそそろっています。
さて GdkPixbuf、そして Imlib は何をしてくれるライブラリなのでしょうか。
基本的にはフレームバッファの提供とその操作です。X client Apprication に 対し、client 側で管理する 24bit Color (α付きで32bit)のメモリーワークと いったところです。client 側で操作するフレームバッファなので画像の加工や 直接的な操作が出来ます。その分いちいち server へ変換・転送しなくては なりませんから表示速度は若干弱くなります。
基本はフレームバッファ間での画像加工なのですが、Imlib が持っている 優れた特色に画像ファイルの読み込みがあります。これがとても便利な事が Imlib が使われ続けて来た理由だと思われます。アプリケーションは Imlib にファイル名を渡すだけで、Imlib が適した画像lib を選んで使い、 フレームバッファに画像を展開してくれます。
もちろん GdkPixbuf もこの画像ファイル読み込み部分をばっちり持っています。 画像を読んで表示するだけならばかなり少ないコードで記述できてしまいます。

・ GdkPixbuf の特色

実は Gdk には GdkImage というフレームバッファ管理の関数群が存在します。 なのに GdkPixbuf はそれを用いずにまた新たなフレームバッファ機構を 提供してしまいました。GdkImage と GdkPixbuf の違いはなんでしょうか?
私も完全に把握はしていないのですが、以下の点が違うようです。

GdkImage も GdkPixbuf も GdkRGB を用いて Pixmap(X Server window)に レンダリングするところは同じようです。
1チャネルあたり 8bit (RGB で 24bit)固定というのが制限っぽいですけれども。 管理やアクセスの簡潔さから欠点ではないでしょう。それ以上にαマスクを 持ち得るあたり用途によっては大変なアドバンテージだと思います。

・フレームバッファの生成

GdkPixbuf の扱うフレームバッファを生成するには gdk_pixbuf_new を 使います。これで GdkPixbuf* を生成したあとはそのバッファに対して 色々と操作をします。
gdk_pixbuf_get_pixels(GdkPixbuf*) でフレームバッファのポインタを 獲得し、直接バッファ内を書き換えてデータ操作とかしても良いでしょう。

・フレームバッファのレンダリング

フレームバッファを X server に表示するためには、X server 側の Drawable に適切な color depth に変換しつつ転送しなくてはなりません。 この作業を「レンダリング」といいます。
レンダリングするには gdk_pixbuf_render_to_drawable_alpha, gdk_pixbuf_render_to_drawable, gdk_pixbuf_render_threshold_alpha, gdk_pixbuf_render_pixmap_and_mask といった関数を使います。用途は頭から「α付きのレンダリング」 「α無しのレンダリング」「αから2値化されたbitmapマスクのレンダリング」 「pixmap, bitmap へのレンダリング」となっています。

・画像ファイルの読み込み

GdkPixbuf はファイル名を与えるだけでそのファイルを読み込み、型を 判別し、展開した後に GdkPixbuf* ポインタ(フレームバッファ)を返して くれます。らくちんですね。
具体的には gdk_pixbuf_new_from_file(*filename) をコールするだけ。 おしまい。
画像ファイルの読み込みの際には、その画像フォーマットに対応した lib が必要になります。(libtiff, libjepg, libpng など)それらは GdkPixbuf の インストール前に入れておきましょう。
以上の仕組みを「りも時計」内にて実装したソースです

void       ChangeFace(gchar *fname)
{
  GdkPixbuf *chr;
  GdkGC     *gc;
  gint  w, h;

  chr = gdk_pixbuf_new_from_file(fname);
  if (chr != NULL) {
    w = gdk_pixbuf_get_width(chr);
    h = gdk_pixbuf_get_height(chr);
    RRMainWindowSize(w, h);
    RRScreenFree();
    gtk_widget_set_usize (RRScreenWindow(), fw, fh);
    RRScreenInit(RRScreenWindow(), RRScreenCanvas(), fw, fh, TRUE);
    if (pic != NULL) gdk_pixmap_unref(pic);

    pic = gdk_pixmap_new(RRScreenCanvas()->window, w, h, -1);
    gc = gdk_gc_new(pic);
    gdk_pixbuf_render_to_drawable(chr, pic, gc,
				  0, 0, 0, 0, w, h,
				  GDK_RGB_DITHER_NONE, 0, 0);
    RRScreenPutPixmap(pic, 0, 0);
    disp_time();
    gdk_gc_destroy(gc);
    gdk_pixbuf_unref(chr);
  }
}

・ちょっと高度な読み込み

ゲーム作成の実際を考えます。今、データが個別になっておらず、 拙作の rrda にてアーカイブ管理されているとします。この形式では 読みだした画像データはメモリ上にありポインタ経由で渡されます。 当然ファイルに落ちていませんから gdk_pixbuf_new_from_file は 使えません。
そんなときは GdkPixbufLoader を使います。
これはストリーム経由で順次画像を読み込み生成する機構で、WEBブラウザ みたいに読み込み途中の絵が徐々に表示されるといった構造を実現できます。
これがメモリーバッファ内の画像データを解析してくれるので先のように 画像データがメモリーバッファ上にしかなくても画像を得ることができます。
先の「りも時計」の画像ロード部分を強引に GdkPixbufLoader 型に書き換えて みた実験コードです。

void       ChangeFace(gchar *fname)
{
  GdkPixbuf *chr;
  GdkGC     *gc;
  gint  w, h;
  FILE  *fp;
  long  size;
  gchar *work;
  GdkPixbufLoader *loader;

  fp = fopen(fname, "r");
  fseek(fp, 0, SEEK_END);
  size = ftell(fp);
  work = malloc(size);
  fseek(fp, 0, SEEK_SET);
  fread(work, sizeof(char), size, fp);
  fclose(fp);
  loader = gdk_pixbuf_loader_new();
  if (gdk_pixbuf_loader_write(loader, work, size) == FALSE) {
    printf("error -=-=-\n");
  }
  chr = gdk_pixbuf_loader_get_pixbuf(loader);
  if (chr != NULL) {
    w = gdk_pixbuf_get_width(chr);
    h = gdk_pixbuf_get_height(chr);
    RRMainWindowSize(w, h);
    RRScreenFree();
    gtk_widget_set_usize (RRScreenWindow(), fw, fh);
    RRScreenInit(RRScreenWindow(), RRScreenCanvas(), fw, fh, TRUE);
    if (pic != NULL) gdk_pixmap_unref(pic);

    pic = gdk_pixmap_new(RRScreenCanvas()->window, w, h, -1);
    gc = gdk_gc_new(pic);
    gdk_pixbuf_render_to_drawable(chr, pic, gc,
				  0, 0, 0, 0, w, h,
				  GDK_RGB_DITHER_NONE, 0, 0);
    RRScreenPutPixmap(pic, 0, 0);
    disp_time();
    gdk_gc_destroy(gc);
    gdk_pixbuf_unref(chr);
  }
}

・画像加工について

今回はあまり詳しく取り上げませんが、画面を拡大縮小したり、コピーしたり することができます。拡大縮小はどちらかと言えばサイズを変えながら 他の Pixbuf にコピーといった感じですので、転送結果保持用にもうひとつ Pixbuf が必要です。もちろん、そのサイズ変更コピーと同時にαブレンディング が使えますので色々とできそうです。
この画像加工周りは、MPU がインテルのもので MMX が使える場合 MMX を 利用してくれる様です。(実際にどこに使っているかまでは覗いていない)

・注意点

GdkPixbuf はレンダリングに GdkRGB を使うというのは先に書いた通りです。 故に gdk_rgb_init(); を実行して GdkRGB を初期化していないとレンダリング が実行できません。
実はここではまって一時間強を費した…(--;

・その他の機能について

コマがPixbuf内に敷き詰められている画像ファイルを用いてアニメーション とするような管理構造体がありますけど、単に存在するだけで実際に用いる ためには自分でコーディングしなければならないようです。おそらく アニメーションGIF の代わりとなるように設計されているのでしょう。 (逆に言うとその程度の実装ですが)
Imlib とか GdkImage にもありますが、X server 側の Drawable を client 側 のフレームバッファに持って来る機能があります。フォント描画などは X server に行わせると楽ですので、その結果を逆輸入してフレームバッファに 加工・適用とかいった使い方ができます。


rero2@yuumu.rim.or.jp