LECTURE 20 Imlib を使った画像表示

○ はじめに

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 についての説明は終わりです。次回は、読み込んだ画像データをちょっと加工してみたいと思います。