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

GdkPixbuf で画像プログラミングの実験

Sep.17.2001

GdkPixbuf で画像プログラミングをする方法。


・ フレームバッファと画面

OS の UI が Window System になってからというもの、アマチュアが 日曜プログラミングでちょこっといじって何か絵を出すという行為が 複雑で難しいものになりました。 昔の PC だと、CPUアドレスにマッピングされている VRAM に直接データを 書き込めば画面上に点が現れたりして、簡易に画面を操作することが できたものです。
何か画像処理の実験とか称して短いプログラムを組んだような時でも、 それを容易に画面に表示出来たという手軽さがあったものです。
基本的には今でも変わってはいないのですが、Window System を介さなくては いけないために最初の敷居が高くなっているのが事実です。
そこで、GdkPixbuf を用いてあの頃のようなちょっとした画像実験を やってみようと言うのが今回のお題です。
幸にしてこれまでに幾つか組んできたプログラムを用いれば、GdkPixbuf から 先、画面に表示する部分は難無くできてしまうので本当にフレームバッファを いじる部分に集中出来る状態になっています。まだよくわかっていないんだよ という方は、今回のサンプルプログラムをたたき台にすると良いのではない かと思います。

・ GdkPixbuf へのダイレクトアクセス

GdkPixbuf の簡単なおさらいから。
GdkPixbuf はピクセルバッファ(表示を持たないフレームバッファ)と、 そこに画像をロードする複数の画像ローダから構成されています。 ピクセルバッファ自体は単にメモリーを確保するだけの仕組みで、 その確保されたメモリーを表示されない画面として、そこに画像を 描いたりします。で、ある程度描き終わったら「えいやっ」とレンダリング して、Gdk 経由で X Server に渡し、必要に応じて画面に表示したり します。
GdkPixbuf は簡易な画像加工場といった雰囲気です。

GdkPixbuf が確保するピクセルバッファは 24bit color オンリーで固定です。 (現在のところ) ですから、RGB それぞれが 8bit であることは決定で、アライメントも 固定されています(0-7Bit=R, 8-15Bit=G, 16-23Bit=B)。
通常時は 24bit で 3byte 1Pixel なのですが、α値を付与すると、 ARGB の 32bit で 4byte 1Pixel になります。
αを追加するには gdk_pixbuf_add_alpha を用います。

GdkPixbuf*  gdk_pixbuf_add_alpha (const GdkPixbuf *pixbuf,
                                  gboolean substitute_color,
                                  guchar r,
                                  guchar g,
                                  guchar b);
substisute_color を TRUE に設定すると、画像内の特定色(後につづくrgb) をα上でヌキ色(表示無し)に指定します。

GdkPixbuf が準備し保持しているピクセルバッファへとアクセスするには gdk_pixbuf_get_pixels() を用います。

guchar*     gdk_pixbuf_get_pixels(const GdkPixbuf *pixbuf);
戻り値は guchar* ですが、これが *pixbuf の保持しているピクセルバッファ へのアドレスとなります。このアドレスを元にポインタを駆使して読み書き することでピクセルバッファに画像を描くことができるわけです。
また、このピクセルバッファアクセスのサポートとして gdk_pixbuf_get_rowstride() が存在します。これは、ピクセルバッファ 横一列で何byteあるか、その数値を返します。αがあるときと無いときでは 1pixel あたりの bit が変わりますので gdk_pixbuf_get_rowstride の 値も変わってきます。
これらを使ってピクセルバッファ内の特定座標 (x, y) のピクセルアドレスを 求め出すには。
  (guchar*)ptr = gdk_pixbuf_get_pixels(pixbuf) +
                   (gdk_pixbuf_get_rowstride(pixbuf) * y) + (x * 4);
といった感じの式になります。

ここまで出来たらあとはもう画像処理のルーチンを書くだけですね。

・ 画像エフェクトを作ろう

さてここからが本番です(笑)。
映画にCG合成が取り入れ始めたころに流行っていた効果に「パーティクル分割」 というものがあります。人間とかがはじっこからバラバラと粒子になって 散っていき、姿を消すようなエフェクトというと通じるでしょうか。
まあある程度のビデオ編集ソフトだとそういった機能がついていそうな気が するのですが、所詮はしがないアマチュアの身、手元には安価な編集ソフト しかありません。ですが、プログラムを作ることは出来ますので、似たような 事を日曜大工的にやっちゃいましょう。

というか、そういうエフェクトが欲しくてやり始めた事だったり。

・ パーティクルエフェクト

てなわけで、以下のような感じのものを作りました。

細部は略、ソースの方を見てくださいということで。

元となる画像はキャラクター以外のところをαマスクでヌキにしておきます。 出力画像もαマスク付きですので、内部では始終 32bit で扱います。
画面は今 640x480 固定とし、これを 8x8 のサイズで扱います。すると、 80x60 chip になるのですが、絵の無いところ(αのヌキだけのチップ)は 無視します。絵のあるチップは全部座標を保持し、スプライトとして扱います。 サンプルの例だと 1100個のスプライトの組合わせでキャラクターになって おり、これをばらばらに散らせる様に動かすことで効果を出しています。
飛び散り始めたチップは徐々にα値を高めて(透明になっていく)やがて 消えます。

サンプルでは画面に表示していくだけです。そんなに遅くないのでほとんど リアルタイムに近い感じで表示されると思います。
これを1コマ生成・表示する度に画像ファイルとしてセーブし、後で ひとつに束ねるとムービーになります。ソース内では rr_main.c の中に PNGSAVE という名の define がありますが、これを 1 にすると1コマづつ PNGファイルとしてセーブします(そのかわりとても重いです)。
サンプルでは 600枚の PNGファイルを生成します。 これをムービーにするには、適当なツールを使ってくださいな。

サンプルコード

particle_test-0.1.tar.gz -- 232kb

pat00.mpg -- 889kb (MPEG1 サンプルムービー)

・ 使用例

こうして作ったパーティクルエフェクト(もどき)の使用例です。 (どっちが目的かはいわずもがなだが)

ランゲージ娘\0 プロモーション風ムービー

glang_demo.mpg(ふつー版) --5.56mb

glang_demo_s.mpg(ちっちゃい版) --2.87mb

パーティクルエフェクト以外は全部 Windows上のアプリ(MediaStudio)で 作成しているあたりが申し訳なかったりするのですが。
MainActor あたりは それなりに良くできてるビデオ編集システムですので、これを使えば Linux 上でも同等のムービーが作成できると思います。


rero2@yuumu.org