LECTURE 17 DIB ( Device-Independent Bitmap ) の表示

○ はじめに

DIB ( Device-Independent Bitmap )とは、X Window System における Bitmap のことではなく、カラー情報を自分で持っている Win32API 対応のカラービットマップを表します。世の中で一般的に言われているビットマップとはこの形式を指しています。ここでは、この DIB ファイルを読み込んで画面に表示する DIB Viewer を作成します。

○ DIB ファイルの形式

サンプルプログラムの前に、DIB ファイルの形式 ( フォーマット )について必要な部分についてのみ簡単に説明します。

DIB ファイルは以下の様に構成されています。なお、各構造体は Windows 上のヘッダで定義されているものであることに注意して下さい。

  • BITMAPFILEHEADER 構造体 ( ファイルヘッダ )
  • BITMAPINFOHEADER 構造体 ( 情報ヘッダ )
  • RGBQUAD 構造体 ( 色情報 )
  • ピクセルデータ

BITMAPFILEHEADER 構造体は 14 bytes、BITMAPINFOHEADER 構造体は 40 bytes であり、計 54 bytes が DIB ファイルのヘッダとなります。この先頭 54 bytes の中では以下の情報を使用します。

オフセット(byte) サイズ(byte) 識別名(構造体のメンバ名) データの内容
0 2 bfType "BM" という文字列であれば、ビットマップファイル
10 4 bfOffBits ファイルの先頭からピクセルデータ領域の先頭までのオフセット。 下にある biBitCount が 16 以上の場合は常に 54。
18 4 biWidth 画像の幅(ピクセル単位)
22 4 biHeight 画像の高さ(ピクセル単位)
28 2 biBitCount 1 ピクセルあたりのカラービット数
30 4 biCompression ピクセルデータの圧縮の種類。0 ( BI_RGB )、1 ( BI_RLE8 )、2 ( BI_RLE4 )、 3 ( BI_BITFIELDS )のいずれかの値となる。BI_RGB の場合は無圧縮、BI_RLE4は 4 ビットカラー用のランレングス圧縮、BI_RLE8 は 8 ビットカラー用のランレングス 圧縮となる。BI_BITFIELDS の場合は、圧縮はされていないがカラーマスクが 設定されている。BI_BITFIELDS は 16 ビットカラーあるいは 32 ビットカラーの場合にのみ有効。
46 4 biClrUsed biBitCount < 16 であれば、biClrUsed は色数となる。biClrUsed が 0 であれば、 biBitCount に対応した最大色数が使われていることになる。biBitCount >= 16 の場合は Windows で参照される特殊な意味を持つが、特に気にしなくてよい。

54 bytes のヘッダが終った後は、色数に応じて RGBQUAD 構造体が続きます。RGBQUAD 構造体の内容は次の通りです。

オフセット(byte)サイズ(byte)内容
01青の輝度 ( 0 〜 255 )
11緑の輝度 ( 0 〜 255 )
21赤の輝度 ( 0 〜 255 )
31予備

ヘッダ情報の biBitCount が 8 以下の場合は、RGBQUAD 構造体が色数個並びます。16 以上の場合は、後述のピクセルデータ自身が色情報を表しているため、RGBQUAD 構造体領域は使用されません( 0 個 )。

RGBQUAD 構造体が並んだ後はピクセルデータが続きます。一般的(と思われる)な順序とは逆に、画像の下行から順に 1 行ずつのピクセルデータが並べられています。また、各行のピクセルデータのサイズが 4 バイト境界に満たない場合は、足りない部分が埋められています。

このピクセルデータは、ヘッダ情報の biBitCount が 8 以下の場合は biCompression が BI_RGB であれば各ピクセルのデータに対応する色番号 ( 前述の RGBQUAD 構造体領域に並べられた色の何番目の色かを表す )が 1 バイトずつ並べられています。biCompression が BI_RLE4 もしくは BI_RLE8 の場合は、カラービット数に応じたランレングス圧縮がかけられています。ランレングス圧縮についてはここではふれません。

biBitCount が 16 以上の場合は、各ピクセル毎に青、緑、赤の順に輝度が並べられています。biCompression が BI_RGB である場合、16 bpp ( biBitCount = 16 )の場合は青、緑、赤それぞれ 5 ビットで表され(残り 1 ビットは意味無し)、24 / 32 bpp の場合は 8 ビットで各色が表現されます(32 bpp の場合の残り 1 バイトは意味無し)。

biCompression が BI_BITFIELDS の場合、独自のカラーマスクが設定されています。カラーマスクの大きさは 4 バイトであり、biBitCount が 8 以下の場合に RGBQUAD 構造体が並べられる位置に 3 つ並べられています( それぞれ青、緑、赤色用)。このカラーマスクとピクセルデータのマスクをとって、実際の色値を取得することになります。

○ サンプルプログラム

256 色以下の DIB ファイルを読み込んで画面に表示する DIB Viewer のサンプルプログラム xdib.c です。画像ファイルの読み込みや書き込みを行う場合は、そのために作成されているライブラリを使うのが一般的だと思いますが、ここでは DIB を読み込む部分から作成します。

はじめに のページから、全てのサンプルプログラムを固めた tar ボールをダウンロードすることもできます。

冒頭にあるように、DIB ファイルは 256 色以下の色数のものを対象とします。ピクセルデータ部は無圧縮であることを前提とします( biCompression が BI_RGB )。また、スクリーンの深さは 16、32 bpp であることを前提とします。それ以外の深さのスクリーンで実行した場合や、256 色より色数の多い、あるいは biCompression が BI_RGB でない DIB ファイルを読み込んだ場合にはそこで終了します。

(24 bpp の場合は正常に動作しない場合がありました。陸さん、ありがとうございました。2004/11/28)

コンパイルを終えたら、DIB ファイル名を引数に与えて実行してみて下さい。DIB ファイルが画面に表示されると思います。ちなみに下図は知り合いの車をデジカメで写した DIB ファイルを表示させた画面をキャプチャしたものです。

017dib.jpg

--- xdib.c ---

    1	#include <X11/Xlib.h>
    2	#include <X11/Xutil.h>
    3	#include <stdio.h>
    4	#include <stdlib.h>
    5	#include <string.h>
    6	
    7	#define WIN_X   100  /* ウィンドウ表示位置(X) */
    8	#define WIN_Y   100  /* ウィンドウ表示位置(Y) */
    9	
   10	
   11	/* DIB ファイルの画像情報を格納する構造体 */
   12	typedef struct _DIBImage_t
   13	{
   14	    int            width;           /* 画像の幅 */
   15	    int            height;          /* 画像の高さ */
   16	    int            n_color;         /* 画像の色数 */
   17	    unsigned char  palette[256][3]; /* パレット情報 */
   18	    unsigned char* data;            /* ピクセルデータ */
   19	}
   20	DIBImage;
   21	
   22	
   23	static Display* dpy;        /* ディスプレイ */
   24	static Window   root, win;  /* ルートウィンドウおよび表示ウィンドウ */
   25	static int      screen;     /* スクリーン */
   26	static int      depth;      /* スクリーンの深さ */
   27	static Pixmap   pixmap;     /* ピクスマップ */
   28	static GC       gc;         /* グラフィックスコンテキスト */
   29	static XColor*  colors;     /* DIB ファイルのパレット情報を格納する領域 */
   30	
   31	
   32	static int  xdib_readFile( char* fname, DIBImage* dib_image );
   33	static void xdib_setup( DIBImage* dib_image );
   34	
   35	
   36	int 
   37	main( int argc, char** argv )
   38	{
   39		XEvent   evt;
   40		DIBImage dib_image;
   41	
   42	
   43		/* 指定された DIB ファイルを読み込んで画像情報を保存する */
   44		if ( xdib_readFile( argv[1], &dib_image ) == 0 ) {
   45			printf( "Failed to read specified DIB file : %s\n", argv[1] );
   46			return 1;
   47		}
   48	
   49		dpy = XOpenDisplay( "" );
   50	
   51		root   = DefaultRootWindow( dpy );
   52		screen = DefaultScreen( dpy );
   53		depth  = DefaultDepth( dpy, screen );
   54	
   55		/* depth が 16、32 以外の場合はここで終了する */
   56		if ( depth != 16 && depth != 32 ) {
   57			XCloseDisplay( dpy );
   58			printf( "Screen depth is not 16 nor 32 bpp.\n" );
   59			return 1;
   60		}
   61	
   62	
   63		win = XCreateSimpleWindow( dpy, root, WIN_X, WIN_Y, 
   64					   dib_image.width,
   65					   dib_image.height,
   66					   0,
   67					   BlackPixel( dpy, screen ), 
   68					   BlackPixel( dpy, screen ) );
   69	
   70		gc = XCreateGC( dpy, win, 0, NULL );
   71	
   72		/* 読み込んだ DIB ファイル情報を表示できるように設定する */
   73		xdib_setup( &dib_image );
   74	
   75		XSelectInput( dpy, win, ExposureMask | KeyPressMask );
   76		XMapWindow( dpy, win );
   77	
   78		while( 1 ) {
   79			XNextEvent( dpy, &evt );
   80	
   81			switch( evt.type ) { 
   82				case Expose:
   83					if ( evt.xexpose.count == 0 ) {
   84						/* ピクスマップをウィンドウにコピーする */
   85						XCopyArea( dpy, pixmap, win, gc, 
   86							   0, 0,
   87							   dib_image.width,
   88							   dib_image.height,
   89							   0, 0 );
   90						XFlush( dpy );
   91					}
   92					break;
   93	
   94				case KeyPress:
   95					free( colors );
   96					XFreePixmap( dpy, pixmap );
   97					XDestroyWindow( dpy, win );
   98					XCloseDisplay( dpy );
   99					return 0;
  100			}
  101		}
  102	}
  103	
  104	
  105	/* 
  106	 * 指定した DIB ファイルを読み込み、画像情報を dib_image 変数
  107	 * に格納する。ピクセルデータを保存するための dib_image.data 
  108	 * 領域は関数内で確保する。そのため本関数を利用する際には、
  109	 * dib_image.data が未確保である dib_image 変数へのポインタを
  110	 * 引数に指定すること。
  111	 * 読み込み処理に失敗した場合は 0 を、成功した場合はそれ以外の値を返す。
  112	 */
  113	static int 
  114	xdib_readFile( char* fname, DIBImage* dib_image )
  115	{
  116		unsigned char buf[4];    
  117		FILE* fp;
  118		int   i;
  119		int   dib_depth;
  120		int   offset;
  121		int   bw;
  122		int   index;
  123	
  124	
  125		if ( ( fp = fopen( fname, "r" ) ) == NULL ) {
  126			printf( "Failed to open %s.\n", fname );
  127			return 0;
  128		}
  129	
  130	
  131		/* 先頭 2 バイトが "BM" であれば、BMP ファイル */
  132		fread( buf, 2, 1, fp );
  133	
  134		if ( strncmp( ( char* )buf, "BM", 2 ) != 0 ) {
  135			printf( "Specified file is not DIB file.\n" );
  136			return 0;
  137		}
  138	
  139	
  140		/* ファイルの先頭から 28 バイトの位置に移動 */
  141		fseek( fp, 28, SEEK_SET );
  142	
  143		/* 1 ピクセルあたりのビット数を取得 */
  144		fread( buf, 2, 1, fp );
  145		dib_depth = buf[0] + ( buf[1] << 8 );
  146	
  147		/* 256 色以下のファイルが対象 */
  148		if ( dib_depth > 8 ) {
  149			printf( "Specified file is more than 8 bpp ( %d bpp ).\n", dib_depth );
  150			return 0;
  151		}
  152	
  153	
  154		/* ファイルの先頭から 30 バイトの位置に移動 */
  155		fseek( fp, 30, SEEK_SET );
  156		fread( buf, 4, 1, fp );
  157	
  158		/* 読み込んだ値が 0 でなければ、biCompression が BI_RGB でない
  159		   (ピクセルデータが圧縮されている)ので、ここで終了する。*/
  160	
  161		if ( buf[0] != 0 ) {
  162			printf( "Specified DIB file is compressed\n" );
  163	      		return 0;
  164		}
  165	
  166	
  167		/* ファイルの先頭から 46 バイトの位置に移動 */
  168		fseek( fp, 46, SEEK_SET );
  169		fread( buf, 4, 1, fp );
  170	
  171		/* 読み込んだ値が 0 でなければ、depth によって色数が決まる。
  172		   256 色以下のファイルであるため、最初の 1 バイトのみを考えれば良い */
  173		if ( buf[0] == 0 ) {
  174			dib_image->n_color = 1 << dib_depth;
  175		}
  176		else {
  177			dib_image->n_color = buf[0];
  178		}
  179	
  180	
  181		/* ファイルの先頭から 10 バイトの位置に移動 */
  182		fseek( fp, 10, SEEK_SET );
  183	
  184		/* ファイルの先頭からピクセルデータ領域の先頭までのオフセットを取得 */
  185		fread( buf, 2, 1, fp );
  186		offset = buf[0] + ( buf[1] << 8 );
  187	
  188	
  189	       	/* ファイルの先頭から 18 バイトの位置に移動 */
  190		fseek( fp, 18, SEEK_SET );
  191	
  192		/* 画像の幅を取得 */
  193		fread( buf, 4, 1, fp );
  194	
  195		dib_image->width  = buf[0] + ( buf[1] << 8 ) + 
  196			( buf[2] << 16 ) + ( buf[3] << 24 );
  197	
  198		/* 画像の高さを取得 */
  199		fread( buf, 4, 1, fp );
  200	
  201		dib_image->height = buf[0] + ( buf[1] << 8 ) + 
  202			( buf[2] << 16 ) + ( buf[3] << 24 );
  203	
  204	    
  205		/* ファイルの先頭から 54 バイトの位置に移動 */
  206		fseek( fp, 54, SEEK_SET );
  207	
  208		/* パレット情報を取得 */
  209		for ( i = 0; i < dib_image->n_color; i ++ ) {
  210			fread( buf, 4, 1, fp );
  211			dib_image->palette[i][0] = buf[2];
  212			dib_image->palette[i][1] = buf[1];
  213			dib_image->palette[i][2] = buf[0];
  214		}
  215	
  216	
  217		/* 画像の 1 行分のバイト数は、画像の幅に 1 番近い 4 の倍数に繰り上げられる */
  218		bw = ( dib_image->width + 3 ) & ~3;
  219	
  220	
  221		/* ピクセルデータ領域の確保 */
  222		dib_image->data = 
  223			( unsigned char* )malloc( dib_image->width * dib_image->height );
  224	
  225		for ( i = 0; i < dib_image->height; i ++ ) {
  226			/* ピクセルデータ領域の先頭から 1 行ずつ読み込む */
  227			fseek( fp, offset + bw * i, SEEK_SET );
  228	
  229			/* 
  230			 * DIB 内のピクセルデータは下行から順に詰められている。
  231			 * dib_image->data には上行から順に詰めるため、対応する
  232			 * インデックスを計算している。
  233			 */
  234			index = dib_image->width * ( dib_image->height - i - 1 );
  235	
  236			/* 1 行読み込み */
  237			fread( &( dib_image->data[ index ] ), dib_image->width, 1, fp );
  238		}
  239	
  240		fclose( fp );
  241		return 1;
  242	}
  243	
  244	
  245	/*
  246	 * 読み込んだ DIB ファイル情報を使って、実際にウィンドウ
  247	 * に表示できるように設定を行う
  248	 */
  249	static void 
  250	xdib_setup( DIBImage* dib_image )
  251	{
  252		/* 
  253		 * 読み込んだ DIB ファイルの画像情報をクライアント側で
  254		 * 保持するための XImage 構造体
  255		 */
  256		XImage*  ximage;
  257	
  258		/* クライアント側から X サーバに転送するピクセルデータを格納する領域 */
  259		unsigned char* img_data;  
  260	
  261		int size;
  262		int i;
  263	
  264	
  265		/* DIBImage 型変数に格納されたカラーパレット情報を、XColor 構造体の
  266		   配列形式に変換する */
  267		colors = ( XColor* )malloc( sizeof( XColor ) * dib_image->n_color );
  268	
  269		for ( i = 0; i < dib_image->n_color; i ++ ) {
  270			colors[i].red   = dib_image->palette[i][0] * 257;
  271			colors[i].green = dib_image->palette[i][1] * 257;
  272			colors[i].blue  = dib_image->palette[i][2] * 257;
  273			colors[i].flags = DoRed | DoGreen | DoBlue;
  274	
  275			XAllocColor( dpy, DefaultColormap( dpy, screen ), &colors[i] );
  276		}
  277	
  278	
  279		/* X サーバ側で画像を保持するためのピクスマップを作成する */
  280		pixmap = XCreatePixmap( dpy, win, 
  281					dib_image->width, 
  282					dib_image->height, 
  283					depth );
  284	
  285	
  286		/* スクリーンの深さに合わせた、転送用ピクセルデータを作成する */
  287		size     = dib_image->width * dib_image->height;
  288		img_data = ( unsigned char* )malloc( size * depth / 8 );
  289	
  290		memset( img_data, 0, size * depth / 8 );
  291	
  292	
  293		/* 
  294		 * DIBImage 内のピクセルデータは、パレット情報配列のどの要素が
  295		 * ピクセルに対応する色であるかを表している。 転送用のピクセル
  296		 * データは、 スクリーンの深さと同じビット数で表された各色の
  297		 * ピクセル値でなければならないので、ここで変換を行う。
  298		 */
  299		for ( i = 0; i < size; i ++ ) {
  300			memcpy( img_data + i * ( depth / 8 ) , 
  301				&( colors[dib_image->data[i]].pixel ), depth / 8 );
  302		}
  303	
  304	
  305		/* クライアント側の画像情報保持領域である XImage 構造体変数を作成する */
  306		ximage = XCreateImage( dpy, DefaultVisual( dpy, screen ),
  307				       depth, ZPixmap, 0, 
  308				       ( char* )img_data,
  309				       dib_image->width, 
  310				       dib_image->height,
  311				       /* 16 bpp の場合は 16、24 / 32 bpp の場合は 32 にする */
  312				       ( depth == 16 ) ? 16 : 32, 
  313				       0 );
  314	
  315		/* ピクセルデータをピクスマップに転送する */
  316		XPutImage( dpy, pixmap, gc, ximage, 
  317			   0, 0, 
  318			   0, 0, 
  319			   dib_image->width, 
  320			   dib_image->height );
  321	
  322		XFlush( dpy );
  323		XDestroyImage( ximage );
  324		free( dib_image->data );
  325	}
  326	
  327	
  328	/* End of xdib.c */

○ サンプルプログラムの説明

では、サンプルプログラムについて説明していきます。

  • 11 〜 20 行目

    105 行目からの DIB 読み込み関数 xdib_readFile() によって読み込んだ DIB ファイル情報 を格納するための構造体 DIBImage を定義しています。palette は、RGB の輝度 ( 0 〜 255 ) を最大 256 色分格納するための領域です。data には、画像の各ピクセルを palette に格納 されている何番目の色で表示するかを表す番号が、画像の上行から順に 1 バイトずつ格納され ます。

  • 27 行目

    ここで宣言されている pixmap には、ウィンドウに表示する画像がロードされます。main() を見る際には、この pixmap に画像がロードされているからそれをウィンドウにコピーして画 像を表示しているんだなと思って下さい。pixmap に画像がロードされる過程については main() の説明の後で説明します。

  • 36 〜 102 行目 ( main() )

    xdib.c の main 関数です。各処理について以下で説明していきます。

    • 43 〜 47 行目

      xdib_readFile() を実行して、引数で指定された DIB ファイルを読み込みます。読み込みに失 敗した場合はここで終了します。

    • 55 〜 60 行目

      直前にディスプレイマクロで取得したスクリーンの深さが 16、32 bpp 以外の場合は、ここで終了 しています。

    • 63 〜 68 行目

      画像表示用のウィンドウを作成しています。既に読み込み済みの DIB ファイル情報を使ってウ ィンドウの幅と高さを指定している点に注意してください。

    • 72 〜 73 行目

      xdib_setup() を実行して、読み込んだ DIB ファイル情報を作成したウィンドウに表示できる ようにするための処理を行っています。処理内容をおおまかに言うと、読み込んだ DIB ファイルのピクセルデータと XImage 構造体変数を関係付け、その XImage 構造体変数を使って最終的に ウィンドウに表示するために使用する pixmap に画像をロードしています。詳細は後述します。

    • 82 〜 92 行目

      Expose イベントに対するイベント処理部です。ここでは、xdib_setup() によって画像が ロードされた pixmap の内容を XCopyArea() を使ってそのままウィンドウにコピーしています。

  • 105 〜 242 行目 ( xdib_readFile() )

    DIB ファイルを読み込んで、DIBImage 型変数に情報を格納する関数です。この関数内では、 DIB ファイルの形式 において取り上げた必要な情報を読み込んで引数 で指定された DIBImage 型変数領域に内容を格納しているだけです。特に注意すべき点について、 以降で説明します。

    • 167 〜 178 行目

      DIB ファイルの形式 で取り上げた biClrUsed が 0 の場合は、色数を 2 の biOffBits 乗として計算し、biClrUsed が 0 である場合にはそれを色数としています。

    • 205 〜 214 行目

      カラーパレットデータを読み込んでいます。DIB ファイル内のパレットデータでは、輝度が青、 緑、赤の順に並んでいるのに対して DIBImage 構造体の palette メンバには赤、緑、青の順に 詰め直している点に注意して下さい。

    • 217 〜 218 行目

      DIB ファイル内のピクセルデータ 1 行分のバイト数を計算しています。DIB ファイル内では、 1 行のバイト数を 4 バイト境界に合わせるためにパディングされているため、画像幅に 1 番近い 4 の倍数として計算しています。

          /* 画像の 1 行分のバイト数は、画像の幅に 1 番近い 4 の倍数に繰り上げられる */
             bw = ( dib_image->width + 3 ) & ~3;
      
      

      上のコードで ~3 ( 2進数で 11111100 )と AND 演算を行っていますが、これは 11111100 と の AND を取ることで下位 2 ビットを落しています。dib_image->width + 3 によって、画像 の幅に 1 番近い 4 の倍数もしくはそれより 1 〜 3 大きい数値が得られるので、 その数値の下位 2 ビットを落す( 0 〜 3 の数値を切捨てる ) ことによって 1 番近い 4 の倍数を計算しています。

    • 221 〜 238 行目

      ピクセルデータを読み込んでいます。DIB ファイル内ではピクセルデータは下行から順に詰められているのに対して、 DIBImage 構造体の data メンバには上行から順に詰めるため、読み込んだデータを格納する インデックスをあらかじめ計算してから読み込みを行っています。

  • 245 〜 325 行目 ( xdib_setup() )

    DIBImage 型変数に読み込んだ画像情報を pixmap にロードしてウィンドウにコピーできるようにするための 処理を行っています。

    読み込んだピクセルデータとパレットデータを使って、ウィンドウに対して XDrawPoint() で全ての点を描画しても表示できるのですが、それではネットワーク遅延による影響が大きくなってしまいます。 それを避けるために、まずクライアント側にピクセルデータを格納できる XImage 構造体を作成して、そこに読み込んだピクセルデータを格納します。その後で X サーバ側の Pixmap にピクセルデータを一気に転送します。

    なお、Pixmap ではなく表示用ウィンドウに直接転送しても構いませんが、その場合、表示用 ウィンドウが別のウィンドウに隠れたりして描画内容が失われた場合に再度クライアント側から ピクセルデータを転送しなおさなければなりません。Pixmap に転送しておけば、サーバ側での転送だけで済みます。

    具体的な処理内容については、以降で説明していきます。

    • 265 〜 276 行目

      DIBImage 型変数に格納されたカラーパレットデータを、XColor 型の形式に変換しています。 各原色の輝度を 257 倍しているのは、DIBImage のパレットデータの輝度が 0 〜 255 であるのに対して、 X のカラーマップでは 0 〜 65535 の範囲で指定するためです。

      XColor 形式に変換した後は、XAllocColor() で対応するピクセル値を取得しています。

    • 286 〜 302 行目

      DIBImage 型変数に格納されたピクセルデータを Pixmap に転送するためには、後程 XImage 構造体を作成する必要がありますが、その際に必要となるピクセルデータ格納領域を作成しています。 スクリーンの深さが 16、32 bpp の場合はそれぞれ 1 ピクセルを 2、4 バイトで格納するため、 サンプルの様なコードの書き方をしています。24 bpp の場合は、3 バイトで表す場合や 4 バイトで格納する場合があるため、 状況に応じて書き方を変える必要があります。 これには利用中の X サーバでサポートしている Pixmap フォーマットを XListPixmapFormats 等で調べる必要がありますが、ここでは 16、32 bpp に環境を限定して話を簡単にしています。

      陸さん、情報ありがとうございました!(2004/11/28)

      DIBImage 型変数に格納されたピクセルデータは、パレットデータの何番目の色で表示するかを表しています。 パレットデータの各色を、その順番通りのピクセル値で確保できれば良いのですが、16 bpp 以上の深さのスクリーンの場合はカラーマップの構造上不可能です ( 1 つのピクセル値を何ビットずつかに分けて、 それぞれが赤、緑、青に対応するピクセル値となるから。なお 8 bpp 以下の場合は可能ですが、その場合最大 256 色を確保するためには、専用のカラーマップを作成して XStoreColor() で任意のピクセル値に色を設定する必要があります)。

      よって、XAllocColor() で得た各色に対応するピクセル値を使ってピクセルデータを変換しています。 変換後のピクセルデータは img_data に格納されますが、各ピクセルのピクセル値を格納している点に注意して下さい。

    • 305 〜 313 行目

      XCreateImage() を使って、クライアント側にピクセルデータを格納するための領域である XImage 構造体を作成しています。

          XImage* XCreateImage( Display*     dpy,
                                Visual*      visual,
                                unsigned int depth,
                                int          format,
                                int          offset,
                                char*        data,
                                unsigned int width,
                                unsigned int height,
                                int          bitmap_pad,
                                int          bytes_per_line
                              );
           
          depth          : 画像の深さ
          format         : XYBitmap / XYPixmap / ZPixmap のいずれか
          offset         : 画像データの各走査線の先頭で無視するピクセル数
          data           : 画像データ領域へのポインタ
          width          : 画像の幅
          height         : 画像の高さ
          bitmap_pad     : 画像データのある走査線の先頭から次の走査線の先頭までのビット数
                           が 8、16、32 のいずれの数で割り切れるかを指定する
          bytes_per_line : 画像データの 1 走査線のバイト数
      
      

      第 4 引数の format に指定する XYBitmap / XYPixmap / ZPixmap についてですが、これは第 6 引数の data の形式を指定する値です。XYBitmap の場合、data の値はビットマップデータ ( 0 もしくは 1 ) が走査線順に並んでいると解釈されます。XYPixmap の場合は、ビットマップのデータが プレーンごとに並びます。ZPixmap を指定した場合、ピクセル値が走査線順に並んでいると解釈されます。 xdib.c ではピクセル値によってピクセルデータを指定することにしたため、ZPixmap を指定しています。

      第 10 引数の bytes_per_line は、画像データの 1 走査線を表すために data に格納しているバイト数を指定します。ここを 0 に設定した場合、他のパラメータに従って Xlib が自動的に計算してくれます。

    • 313 〜 318 行目

      XImage 型変数に格納されたピクセルデータを、XPutImage() を使って X サーバ側のピクスマップに転送しています。 これにより、ピクセルデータを再度サーバ側に転送する必要が無くなります。 ここでピクスマップの代わりに表示用ウィンドウに転送すれば、直接表示することができます。

          XPutImage( Display*     dpy, 
                     Drawable     d,
                     GC           gc,
                     XImage*      image,
                     int          src_x, src_y,
                     int          dest_x, dest_y,
                     unsigned int width, height
                   );
      
          src_x,  src_y   : 転送元イメージの転送領域左上角の座標
          dest_x, dest_y  : 転送先イメージの転送領域左上角の座標
          width,  height  : 転送領域の幅と高さ
      
      

    • 322 行目

      XFlush() を実行してリクエストバッファをフラッシュさせ、ピクスマップへの転送要求が実際 にサーバに送られるまで待っています。

    • 323 〜 324 行目

      xdib_setup() 内で確保したリソースのうち、不要になったものを解放しています。確保した XImage 構造体は、XDestroyImage() を使って解放します。

          XDestroyImage( XImage* ximage );
      
      

      XCreateImage()、XGetImage()、XGetSubImage() で作った XImage 型変数領域を解放する場合、 XImage 型変数領域そのものと data で指定されるデータ領域の両方が解放されます。この時データ領域が static 領域に確保されている場合はメモリを解放できないため、data メンバに NULL を設定する必要があります。

以上で xdib.c の説明は終りです。以降では、ここまでで紹介していない XImage 構造体データ操作用のいくつかの関数について説明していきます。

○ウィンドウもしくはピクスマップの画像データを取得する

ウィンドウもしくはピクスマップの画像データをクライアント側に取得するには、XGetImage() を使います。

XImage* XGetImage( Display*      dpy, 
                   Drawable      d, 
                   int           x, y, 
                   unsigned int  width, height,
                   unsigned long plane_mask,
                   int           format
                 );

d             : 画像データを取得したい Drawable を指定
x, y          : 画像データを取得したい領域の左上角の座標
width, height : 画像データを取得したい領域の幅と高さ
plane_mask    : 画像データを取得したいプレーンを指定
format        : XYBitmap / XYPixmap / ZPixmap のいずれか

plane_mask には画像データを取得したいプレーンをマスク指定します。Xlib.h に AllPlanes という定数が定義されていて、これを指定すると全てのプレーンを指定することができます。

○ピクセルデータを操作する

XImage 型変数に対してピクセルデータの操作をする関数を以下にまとめて紹介します。

unsigned long XGetPixel( XImage* ximage, int x, int y );
XPutPixel( XImage* ximage, int x, int y, unsigned long pixel );
XAddPixel( XImage* ximage, long value );

XGetPixel() は、x、y で指定した位置のピクセルデータのピクセル値を取得します。XPutPixel() は、指定した位置のピクセルデータに指定したピクセル値を設定します。XAddPixel() は、全てのピクセル値に指定した値を加算します。