|
○ はじめに
LECTURE 17 DIB ( Device-Independent Bitmap ) の表示 では、自作コードを用いて DIB ファイル(Windows で使われているビットマップファイル)の読み込み/表示を行いました。ここでは Imlib (freshmeat.net の紹介ページ) というライブラリを使って画像を表示してみたいと思います。
Imlib は現在 GNOME プロジェクトで管理されている画像読み込みライブラリで、内部から libpng 等のライブラリを使って画像の読み書きを可能としています。xlib から使うライブラリとして現在主流なのかほとんど使われていないのかよく分からないのですが、割と以前から知っているので使ってみることにします。
○ サンプルプログラム
Imlib を使って指定した画像を表示するサンプルプログラム x_imlib.c です。はじめに のページから全てのサンプルプログラムを固めた tar ボールをダウンロードすることもできます。
コンパイル時に Imlib のライブラリをリンクする必要がありますので、これまでのサンプルプログラムのコンパイルオプションに加えて -lImlib オプションを指定してください。
例: gcc -I/usr/X11R6/include -L/usr/X11R6/lib -o x_imlib x_imlib.c -lX11 -lImlib ※先日 X.Org Foundation からリリースされた X11R7.1 の環境では やはり X11R7 なんでしょうか?周りに環境が無いもので^^; <=== Fedora6 等では、/usr/X11... というディレクトリ自体が無いんですね…
コンパイルを終えたら、画像ファイル名を引数に与えて実行してみてください。画像が表示されます。その状態で左右のカーソルキーを押すと、画像が左右反転表示されます。また、上下のカーソルキーを押すと、画像が上下反転表示されます。Esc キーを押すと終了します。
--- 元画像 ---
--- 左右反転 ---
--- 上下反転 ---
--- x_imlib.c ---
1 #include <X11/Xlib.h> 2 #include <X11/keysym.h> 3 #include <X11/Xutil.h> /* shape.h の前に必要 */ 4 #include <X11/extensions/shape.h> /* Shape Extension */ 5 #include <Imlib.h> 6 #include <stdio.h> 7 8 9 static Display* display; 10 static Window window; 11 static ImlibData* imlib_data; 12 static ImlibImage* imlib_image; 13 static Pixmap pixmap; 14 static Pixmap mask; 15 static int width; 16 static int height; 17 18 19 static void render( void ); 20 static void update_image( void ); 21 static void flip_horizontal( void ); 22 static void flip_vertical( void ); 23 24 25 int 26 main( int argc, char** argv ) 27 { 28 int screen; 29 30 if ( argc <= 1 ) { 31 fprintf( stderr, "画像ファイルを指定してください。\n" ); 32 exit(1); 33 } 34 35 display = XOpenDisplay( NULL ); 36 screen = DefaultScreen( display ); 37 38 if ( ( imlib_data = Imlib_init( display ) ) == NULL ) { 39 fprintf( stderr, "Imlib の初期化に失敗しました。\n" ); 40 XCloseDisplay( display ); 41 exit(1); 42 } 43 44 if ( ( imlib_image = Imlib_load_image( imlib_data, argv[1] ) ) == NULL ) { 45 fprintf( stderr, "画像の読み込みに失敗しました。\n" ); 46 XCloseDisplay( display ); 47 exit(1); 48 } 49 50 width = imlib_image->rgb_width; 51 height = imlib_image->rgb_height; 52 53 window = XCreateSimpleWindow( 54 display, 55 DefaultRootWindow( display ), 56 width, 57 height, 58 width, 59 height, 60 0, 61 BlackPixel( display, screen ), 62 BlackPixel( display, screen ) 63 ); 64 65 XSelectInput( display, window, StructureNotifyMask | KeyPressMask ); 66 XMapWindow( display, window ); 67 render(); 68 update_image(); 69 70 while ( 1 ) { 71 int esc_pressed = 0; 72 XEvent event; 73 KeySym key_sym; 74 75 XNextEvent( display, &event ); 76 77 switch ( event.type ) { 78 case KeyPress: 79 XLookupString( &( event.xkey ), 80 NULL, 0, &key_sym, NULL ); 81 82 switch ( key_sym ) { 83 case XK_Left: 84 case XK_Right: 85 flip_horizontal(); 86 break; 87 case XK_Up: 88 case XK_Down: 89 flip_vertical(); 90 break; 91 case XK_Escape: 92 esc_pressed = 1; 93 break; 94 } 95 96 break; 97 98 case ConfigureNotify: 99 width = event.xconfigure.width; 100 height = event.xconfigure.height; 101 Imlib_free_pixmap( imlib_data, pixmap ); 102 render(); 103 update_image(); 104 break; 105 } 106 107 if ( esc_pressed ) { 108 break; 109 } 110 } 111 112 113 Imlib_free_pixmap( imlib_data, pixmap ); 114 Imlib_kill_image( imlib_data, imlib_image ); 115 XDestroyWindow( display, window ); 116 XCloseDisplay( display ); 117 118 return 0; 119 } 120 121 122 static void 123 render( void ) 124 { 125 Imlib_render( imlib_data, imlib_image, width, height ); 126 pixmap = Imlib_move_image( imlib_data, imlib_image ); 127 mask = Imlib_move_mask( imlib_data, imlib_image ); 128 } 129 130 131 static void 132 update_image( void ) 133 { 134 XSetWindowBackgroundPixmap( display, window, pixmap ); 135 136 if ( mask ) { 137 XShapeCombineMask( display, window, 138 ShapeBounding, 0, 0, mask, ShapeSet ); 139 } 140 141 XClearWindow( display, window ); 142 XSync( display, False ); 143 } 144 145 146 static void 147 flip_horizontal( void ) 148 { 149 Imlib_free_pixmap( imlib_data, pixmap ); 150 Imlib_flip_image_horizontal( imlib_data, imlib_image ); 151 render(); 152 update_image(); 153 } 154 155 156 static void 157 flip_vertical( void ) 158 { 159 Imlib_free_pixmap( imlib_data, pixmap ); 160 Imlib_flip_image_vertical( imlib_data, imlib_image ); 161 render(); 162 update_image(); 163 } 164 165 166 /* End of x_imlib.c */
○ サンプルプログラムの説明
では、サンプルプログラムについて説明していきます。
- 5 行目
Imlib を使うために必要な Imlib.h をインクルードしています。
- 11 〜 16 行目
ImlibData 型は Imlib とやりとりするための共通データを格納する構造体で、ImlibImage は 1 画像に対応するデータを格納する構造体です。pixmap と mask は読み込んだ画像データおよび対応するマスクを格納するための変数で、width / height は画像の幅と高さを表します。
- 25 〜 119 行目 ( main() )
x_imlib.c の main 関数です。各処理について以下で説明していきます。
- 38 〜 42 行目
Imlib を初期化しています。Imlib の初期化は Imlib_init() を使って行います。
ImlibData* Imlib_init( Display* disp );
この関数を実行すると Imlib が初期化され、ImlibData 型のポインタが返ります。このポインタは他の Imlib 関数にて引数として指定することになります。
- 44 〜 48 行目
指定された画像ファイルを Imlib を使って読み込みます。画像の読み込みには Imlib_load_image() を使用します。
ImlibImage* Imlib_load_image( ImlibData* id, char* file ); file : 読み込み画像ファイルパス
Imlib は、libpng や libjpeg 等のライブラリや、ImageMagick 等のツールを使い様々な画像フォーマットの読み込みを可能としています(もちろんそれらのライブラリが環境にインストールされていなければなりませんが)。全ての必要な外部ライブラリがインストールされている環境では、JPEG/GIF/PPM/PGM/XPM/PNG/TIFF/EIM といった画像を読み込むことが出来る様です(自分では全ては試していません)。
- 67 〜 68 行目
Imlib_load_image() で読み込んだ画像は、X で表示できる Pixmap の形式にはなっていません。render() は Imlib が読み込んだ画像データを Pixmap に変換し、ファイルの先頭で宣言した pixmap / mask 変数に ID を格納する自作関数です。また update_image() は、ウィンドウに画像を表示するための自作関数です。いずれも後程説明します。
- 83 〜 86 行目
左右のカーソルキーが押された時の処理です。実際の処理は 146 〜 153 行目の flip_horizontal() で行っています。flip_horizontal() では、Imlib_free_pixmap() で現在の表示用 Pixmap の解放処理を行い、Imlib_flip_image_horizontal() で画像の左右反転処理を行っています。
void Imlib_flip_image_horizontal( ImlibData* id, ImlibImage* image );
Imlib_flip_image_horizontal() は、ImlibImage 内部の画像データの反転処理を行うのみですので、再度 render() / update_image() にて Pixmap の生成 / 画像表示を行っています。Imlib_free_pixmap() については後程説明します。
- 87 〜 90 行目
上下のカーソルキーが押された時の処理です。実際の処理は 156 〜 163 行目の flip_vertical() で行っています。flip_vertical() では、Imlib_free_pixmap() で現在の表示用 Pixmap の解放処理を行い、Imlib_flip_image_vertical() で画像の上下反転処理を行っています。
void Imlib_flip_image_vertical( ImlibData* id, ImlibImage* image );
Imlib_flip_image_vertical() は、ImlibImage 内部の画像データの反転処理を行うのみですので、再度 render() / update_image() にて Pixmap の生成 / 画像表示を行っています。Imlib_free_pixmap() については後程説明します。
- 98 〜 104 行目
ConfigureNotify は、ウィンドウの大きさが変更された場合に送られてくるイベントです。新しいウィンドウの大きさを保存し、その大きさで表示用 Pixmap を作り直しで再表示しています。
Imlib は内部でキャッシュ機構を実装しているため、Pixmap が不要になった場合は普通に XFreePixmap() を使うのではなく、Imlib_free_pixmap() を使う必要があります。
void Imlib_free_pixmap( ImlibData* id, Pixmap pixmap ); pixmap : 解放する Pixmap ID
この関数は、Pixmap の参照カウンタ(Imlib が内部で管理)を減らし、参照カウンタが 0 になった時点でキャッシュに移動します。その後、キャッシュが一杯になった場合に古い Pixmap から解放されます(すぐに解放するわけではない様です。)。Imlib で作成した Pixmap を解放する際にはこの関数を使ってください。また、Imlib で作成していない Pixmap でも普通に解放できますので、この関数で統一しても構わないと思います。また、関連するマスク用 Pixmap も自動的に解放しますので、マスクに対して個別に解放する必要はありません。
- 113 〜 114 行目
Imlib_free_pixmap() によって Pixmap を解放した後、Imlib_kill_image() を使って ImlibImage 構造体を解放しています。
void Imlib_kill_image( ImlibData* id, ImlibImage* image ); void Imlib_destroy_image( ImlibData* id, ImlibImage* image );
Imlib_kill_image() を実行すると、画像データ等をキャッシュに保存することなく即座に解放します。なお、Imlib_destroy_image() の方は、参照カウンタを減らしてカウンタが 0 になった時点でキャッシュに移動します。その後、キャッシュが一杯になった際に解放されます。
- 122 〜 128 行目 ( render() )
Imlib_render() を使い、ImlibImage に格納されている画像データから指定幅と高さの Pixmap(および対応するマスク)を生成しています。
int Imlib_render( ImlibData* id, ImlibImage* image, int width, int height ); width : 生成する Pixmap の幅 height : 生成する Pixmap の高さ
Pixmap 生成に成功した場合は 1 を、失敗した場合は 0 を返します。その後、実際に Pixmap とマスクを取り出すために Imlib_move_image() および Imlib_move_mask() を実行しています。
Pixmap Imlib_move_image( ImlibData* id, ImlibImage* image ); Pixmap Imlib_move_mask( ImlibData* id, ImlibImage* image );
Imlib_move_image() / Imlib_move_mask() を実行すると Pixmap / マスクを返します。Pixmap が生成されていない場合は 0 または NULL を返します。これらを実行すると ImlibImage に設定されている Pixmap ID が初期化されるため、同じ内容の Pixmap を取得するには再度 Imlib_render() からやり直す必要があります。取得した Pixmap に変更を加えるのでオリジナルの Pixmap を再度取得したい場合は、次の Imlib_copy_image() / Imlib_copy_mask() を使ってください。
Pixmap Imlib_copy_image( ImlibData* id, ImlibImage* image ); Pixmap Imlib_copy_mask( ImlibData* id, ImlibImage* image );
Imlib_copy_image() / Imlib_copy_mask() は、保持している Pixmap のコピーを作成して返します。オリジナルが保存されるため、取得した Pixmap に変更を加えた後でオリジナルを再取得したい場合は、もう一度 Imlib_copy_image() / Imlib_copy_mask() を実行すれば良いです。ただしコピー処理を行うため、Imlib_move_image() / Imlib_move_mask() よりもパフォーマンスが落ちます。
- 131 〜 143 行目 ( update_image( void ) )
Pixmap をウィンドウの背景 Pixmap として設定し、更に XShapeCombineMask() を使ってマスクを設定しています。これにより、透過表示すべき画像もきちんと表示されます。
以上で x_imlib.c についての説明は終わりです。次回は、読み込んだ画像データをちょっと加工してみたいと思います。