GTK+でアニメーションGIFを表示する

gtkmm には画像を表示するためのウィジェット Gtk::Image があります。Gtk::Image はアニメーション GIF にも対応しているので アニメーション GIF ファイルを読み込むだけで簡単にアニメーションを実現することができます。

Gtk::Image にはいくつかのコンストラクタがあります。

  • Gtk::Image(const std::string& file)
  • Gtk::Image(const Glib::RefPtr<Gdk::Pixbuf>& pixbuf)
  • Gtk::Image(const Glib::RefPtr<Gdk::PixbufAnimation>& animation)

ファイルパス文字列を引数に取るコンストラクタの使い方は簡単です。このコンストラクタはアニメーションと非アニメーションの両方に対応しており 指定されたファイルの中身に応じて自動的に 非アニメーション Gdk::Pixbuf とアニメーション Gdk::PixbufAnimation を使い分けてくれます。画像を外部ファイルから読み込むのであればこのファイルパスを指定するコンストラクタで充分です。

しかし 画像を外部ファイルから読み込むのではなく 実行ファイル内のリソースとして画像を読み込みたいということもありますよね。そのときは Glib::RefPtr<Gdk::Pixbuf> または Glib::RefPtr<Gdk::PixbufAnimation> を引数に取るコンストラクタを使うことになります。

JPEG PNG を使うときには Glib::RefPtr<Gdk::Pixbuf> を引数にとるコンストラクタを使います。アニメーション GIF を使うときには Glib::RefPtr<Gdk::PixbufAnimation> を引数にとるコンストラクタを使います。Gdk::Pixbuf Gdk::PixbufAnimation は自分で用意する必要があります。

Gdk::Pixbuf の作り方

まず JPEG PNG など非アニメーション画像を実行ファイル内部から読み込む方法を説明します。

Gdk::Pixbuf には create_from_ で始まるスタティック関数がいくつかあります。

  • create_from_file(const std::string& filename)
  • create_from_inline(int data_length, const guint8* data, bool copy_pixels = false)

create_from_file は外部ファイルを読み込んで Gdk::Pixbuf を作成する関数です。create_from_inline はバイト列 unsigned char の配列 から Gdk::Pixbuf を作成する関数です。この create_from_inline 関数を使えば外部ファイルから読み取ることなく 実行ファイル内のリソース バイト配列 から画像を作成することができます。

Gdk::Pixbuf 用のバイト配列の作り方

GTK+には gdk-pixbuf-csource というコマンドが付属しています。この gdk-pixbuf-csouce を使って画像ファイルを C 言語のソースコードに変換することができます。

たとえば sample.png という画像ファイルを変換する場合は以下のようにします。

gdk-pixbuf-csource --raw --name=sample_png sample.png > sample.png.h

これで sample.png.h というファイルが出力されます。拡張子は .h ではなく .c としてもいいかもしれません。このファイルの中身は以下のようになっています。

sample.png.h
/* GdkPixbuf RGBA C-Source image dump */ #ifdef __SUNPRO_C #pragma align 4 (sample_png) #endif #ifdef __GNUC__ static const guint8 sample_png[] __attribute__ ((__aligned__ (4))) = #else static const guint8 sample_png[] = #endif { "" /* Pixbuf magic (0x47646b50) */ "GdkP" /* length: header (24) + pixel_data (262144) */ "¥0¥4¥0¥30" /* pixdata_type (0x1010002) */ "¥1¥1¥0¥2" /* rowstride (1024) */ "¥0¥0¥4¥0" /* width (256) */ "¥0¥0¥1¥0" /* height (256) */ "¥0¥0¥1¥0" /* pixel_data: */ "¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377" "¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377" (途中省略) "¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377" "¥377¥377¥377¥377¥377¥377¥377¥377¥377¥377"};

ifdef などの分岐があって複雑に見えますが static const guint8 sample_png[] = {...}; でバイト列を sample_png という変数に設定しているだけです。

注意
gdk-pixbuf-csource の変換結果は画像ファイルのバイト列そのままではなく Glib::Pixbuf として読み込むための専用形式になります。

バイト列を Gdk::Pixbuf に読み込む

gdk-pixbuf-csource で作成したファイル sample.png.h #include すると sample_png 変数を参照できます。この sample_png 変数を create_from_inline 関数に指定します。

sample.png.hを読み込む
#include "img/sample.png.h" Glib::RefPtr<Gdk::Pixbuf> pixbuf = Gdk::Pixbuf::create_from_inline(-1, sample_png);

create_from_inline 関数の第 1 引数にはバイト配列の長さを指定しますが -1 を指定することもできます。

Glib::RefPtr<Gdk::Pixbuf> が作成できたら あとは Gtk::Image のコンストラクタ引数で指定するか もしくは set 関数で指定するだけです。

コンストラクタでGlib::RefPtrを指定する
Gtk::Image image(pixbuf);

もしくは

set関数でGlib::RefPtrを指定する
Gtk::Image image; image.set(pixbuf);

非アニメーション画像を実行ファイル内のリソースとして保持して読み込む手順は以上です。

サンプルプログラム

PNG 画像を実行ファイル内リソースから読み込むサンプルプログラムのソースコードをダウンロードできます。

サンプルプログラムの実行結果

Gdk::PixbufAnimation の作り方

続いて アニメーション GIF を実行ファイル内にリソースとして保持する方法を説明します。

Gdk::Pixbuf には create_from_inline というバイト列を指定して読み込む関数がありましたが 残念なことに Gdk::PixbufAnimation には create_from_inline 関数がありません。Gdk::PixbufAnimation には create_from_file 関数しかありません

代わりに Gdk::PixbufLoader を使用してバイト列から Gdk::PixbufAnimation を作成します。

Gdk::PixbufLoader には write というバイト列を読み込む関数があります。この関数に指定するバイト列は Gdk::Pixbuf::create_from_inline とは形式が異なることに注意してください。create_from_inline 関数には gdk-pixbuf-csource コマンドで作成したピクセルデータのバイト配列を指定しましたが Gdk::PixbufLoader write 関数には画像ファイルのバイト列そのものを指定する必要があります。

Gdk::PixbufAnimation 用のバイト配列の作り方

  • Gdk::PixbufAnimation をバイト列から作成するために Gdk::PixbufLoader を使う
  • Gdk::PixbufLoader gdk-pixbuf-csource で作成したバイト列を受け取れない

ということで Gdk::PixbufLoader で処理できるバイト列 画像ファイルのバイナリーそのもの を作成します。

バイナリーファイルを C 言語のソースコードとして取り込むのに xxd コマンドが使えます。

Windows xxd を使う
xxd コマンドは MSYS2 vim に含まれています。MSYS2 MinGW を使っている場合には vim パッケージをインストールすることで xxd コマンドが使えるようになります。
$ pacman -S vim

たとえば sample.gif というアニメーション GIF ファイルを変換する場合は以下のようにします。

xxd -i sample.gif > sample.gif.h

xxd コマンドに -i オプションを指定することで C 言語ソースコードにインクルード可能な形式になります。出力されたファイル sample.gif.h の中身は以下のようになっています。

sample.gif.h
unsigned char sample_gif[] = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x00, 0x01, 0x00, 0x01, 0xa5, 0x1f, 0x00, 0xff, 0xff, 0xff, 0xf7, 0xf7, 0xf7, 0xef, 0xef, 0xef, 0xe6, 0xe6, 0xe6, 0xde, 0xde, 0xde, 0xd6, 0xd6, 0xd6, 0xce, 0xce, 0xce, 0xc5, 0xc5, (途中省略) 0x83, 0x7a, 0xe6, 0x09, 0xa7, 0x86, 0x25, 0xed, 0x89, 0xe6, 0xa0, 0x86, 0x34, 0x39, 0x8b, 0x87, 0x03, 0xe8, 0x68, 0xe7, 0x7f, 0xf7, 0x75, 0x79, 0xa6, 0x3c, 0x72, 0x4e, 0x3a, 0x06, 0x4a, 0x96, 0x66, 0x58, 0x4d, 0x10, 0x00, 0x3b }; unsigned int sample_gif_len = 8942;

シンプルで分かりやすい内容です。ファイルのバイト列がそのまま sample_gif という変数に設定されています。また sample_gif_len という変数にバイト列の長さも設定してくれています。

ヒント
xxd はファイルの内容を C 言語のソースコードに取り込み可能なバイト列に変換するだけなので 画像以外のリソースを実行ファイルに埋め込むのにも使えます。

バイト列を Gdk::PixbufLoader で読み込む

xxd で作成したファイル sample.gif.h #include すると sample_gif 変数と sample_gif_len 変数を参照できます。この sample_gif 変数と sample_gif_len 変数を Gdk::PixbufLoader write 関数に指定します。

sample.png.hを読み込む
#include "img/sample.gif.h" Glib::RefPtr<Gdk::PixbufLoader> loader = Gdk::PixbufLoader::create(); loader->write(sample_gif, sample_gif_len); loader->close(); Glib::RefPtr<Gdk::PixbufAnimation> pixbuf = loader->get_animation();

Gdk::PixbufLoader get_animation 関数を呼び出すと Glib::RefPtr<Gdk::PixbufAnimation> を取得することができます。

ヒント
Gdk::PixbufLoader には get_animation 関数だけでなく get_pixbuf 関数もあります。この関数を使えば非アニメーション画像を読み込んで Glib::RefPtr<Gdk::Pixbuf> を取得することができます。非アニメーション画像の読み込みにも xxd + Gdk::PixbufLoader を使用して共通化するのがいいかもしれませんね。

Glib::RefPtr<Gdk::PixbufAnimation> が取得できたら あとは Gtk::Image のコンストラクタ引数で指定するか もしくは set 関数で指定するだけです。

コンストラクタでGlib::RefPtrを指定する
Gtk::Image image(pixbuf);

もしくは

set関数でGlib::RefPtrを指定する
Gtk::Image image; image.set(pixbuf);

アニメーション GIF を実行ファイルに埋め込む手順の説明は以上です。

サンプルプログラム

アニメーション GIF を実行ファイル内リソースから読み込むサンプルプログラムのソースコードをダウンロードできます。

サンプルプログラムの実行結果
この記事を共有しませんか?