LECTURE 3 黒、白以外の色を使う

○ はじめに

LECTURE 1 の xwin.c でウィンドウを表示した際に、ボーダの色とウィンドウの背景色にそれぞれ黒と白のピクセル値を設定しました。このように、色を設定する時にはピクセル値を使います(ピクセル値については LECTURE 2 で説明しました)。黒と白のピクセル値については、ディスプレイマクロ

black = BlackPixel( dpy, screen );
white = WhitePixel( dpy, screen );

を使ってピクセル値を取得しました。では、他の色を使いたい場合にはどのようにしてピクセル値を取得するのでしょうか? ここでは、その方法、および色関連のいくつかの関数について説明していきます。

○ サンプルプログラム(色名を指定してピクセル値を取得)

ウィンドウの背景色を緑色にして表示するサンプルプログラム xcolor.c です。xwin.c を基本として、必要な修正を加えています。はじめに のページから、全てのサンプルプログラムを固めた tar ボールをダウンロードすることもできます。

xwin.c の時と同様にコンパイルして実行すると、緑色を背景色としてウィンドウが表示されます。何かキーを押すとプログラムが終了します。

003color.jpg

--- xcolor.c ---

    1	#include <X11/Xlib.h>
    2	
    3	#define WIN_W  400  /* ウィンドウの幅   */
    4	#define WIN_H  300  /* ウィンドウの高さ */
    5	#define WIN_X  100  /* ウィンドウ表示位置(X) */
    6	#define WIN_Y  100  /* ウィンドウ表示位置(Y) */
    7	#define BORDER 2    /* ボーダの幅      */
    8	
    9	
   10	int 
   11	main( void )
   12	{
   13		Display*       dpy;          /* ディスプレイ */
   14		Window         root;         /* ルートウィンドウ */
   15		Window         win;          /* 表示するウィンドウ */
   16		int            screen;       /* スクリーン */
   17		unsigned long  black, white; /* 黒と白のピクセル値 */
   18		XEvent         evt;          /* イベント構造体変数 */
   19	
   20		Colormap       cmap;         /* カラーマップ */
   21		XColor         color, exact; 
   22		unsigned long  green;        /* 緑のピクセル値 */
   23	
   24	
   25		/* Xサーバと接続する */
   26		dpy = XOpenDisplay( "" );
   27	
   28	
   29		/* ディスプレイ変数の取得 */
   30		root   = DefaultRootWindow( dpy );
   31		screen = DefaultScreen( dpy );
   32	
   33	
   34		/* XAllocNamedColor() のためにカラーマップを取得 */
   35		cmap   = DefaultColormap( dpy, screen ); 
   36	
   37	
   38		/* 白、黒のピクセル値を取得 */
   39		white  = WhitePixel( dpy, screen );
   40		black  = BlackPixel( dpy, screen );
   41	
   42	
   43		/* 緑色のピクセル値を取得 */
   44		XAllocNamedColor( dpy, cmap, "green", &color, &exact );
   45		green = color.pixel;
   46	
   47	
   48		/* ウィンドウを作成する。この時点ではまだ画面に表示されない。*/
   49		win = XCreateSimpleWindow( dpy, root,
   50			   WIN_X, WIN_Y, WIN_W, WIN_H, BORDER, black, green );
   51	
   52	
   53		/* Xサーバから通知してもらうイベントを指定
   54		   ここではキー押下イベントを選択している */
   55		XSelectInput( dpy, win, KeyPressMask );
   56	
   57	
   58		/* ウィンドウを画面に表示する(マップする)。 */
   59		XMapWindow( dpy, win );
   60	
   61	
   62		/* Xサーバからイベントを読みこんで随時処理していくイベントループ */
   63		while( 1 ) {
   64			XNextEvent( dpy, &evt );
   65	
   66			switch ( evt.type ) {
   67				case KeyPress:
   68					/* リソースの解放 */
   69					XDestroyWindow( dpy, win );
   70					XCloseDisplay( dpy );
   71					return 0;
   72			}
   73		}
   74	}
   75	
   76	/* End of xcolor.c */

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

では、サンプルプログラムについて説明していきます。xwin.c と同じ箇所については、説明を省略します。

  • 20 〜 22 行目

    今回新しく追加になった変数です。いずれも、黒、白以外の色を指定して取得するために必要 な変数です。Colormap 型は、LECTURE 2 で説明したカラーマップを表す変数型、XColor 型は 色の RGB 値やピクセル値が格納される変数型です。unsigned long 型の green は、背景色を 指定するためのピクセル値を格納するための変数です。

  • 34 〜 35 行目

    次で説明する XAllocNamedColor() で使用するために、カラーマップの ID をディスプレイマ クロ DefaultColormap で取得しています。

  • 43 〜 45 行目

    ここが今回のメインとなる箇所で、"green" という色名で緑色を指定し、その色に対応するピ クセル値を取得して変数 green に格納しています。

    色名を指定して対応するピクセル値を取得するには、関数 XAllocNamedColor() を使います。

      Status XAllocNamedColor( Display* display, 
                               Colormap colormap,
                               char*    color_name, 
                               XColor*  screen_def_return,
                               XColor*  exact_def_return
                             );
    
      colormap          : 使用するカラーマップの ID
      color_name        : ピクセル値を知りたい色の名前
      screen_def_return : スクリーンに表示可能な、指定色名に最も近い色情報
      exact_def_return  : データベース上の正確な色情報
    
    

    color_name に指定できる名前は色のデータベース( /usr/lib/X11/rgb.txt )に登録されている 名前です( kterm などで showrgb コマンドを実行すると、登録されている名前を閲覧すること ができます)。スクリーンに表示可能であり、かつ指定した色名に最も近い色の RGB 値とピク セル値が XColor 構造体変数へのポインタ screen_def_return が示す領域に格納されます。

      typedef struct {
          unsigned long  pixel;            /* 色のピクセル値 */
          unsigned short red, green, blue; /* 色のRGB値 */
          char     flags;                  /* DoRed、DoGreen、DoBlue */
      } XColor;  
    
    

    red、green、blue には色の RGB 値が入り、pixel にはその RGB 値に対応するピクセル値が 格納されます。flags には red、green、blue のうちどれに値を設定したかが格納されます。 exact_def_return にはデータベースに登録されている正確な RGB 値が格納されます。 colormap はカラーマップ ID を指定しますが、通常はディスプレイマクロ DefaultColormap で取得したデフォルトのカラーマップを指定します。

    XAllocNamedColor() は、カラーマップ上にカラーセルを割り当てることが出来た場合(指定 した色が使用可能である)には 0 でない値を返し、そうでなければ 0 を返します ( Status 型は #define Status int として定義されています)。

  • 48 〜 50 行目

    XCreateSimpleWindow() でウィンドウを作成していますが、背景色の指定用の引数に先程取 得した緑色のピクセル値 ( green ) を指定しています。これによって、背景色が緑色である ウィンドウが作成されます。

以上で xcolor.c の解説は終わりです。xcolor.c では、色名を指定することによって希望の色のピクセル値を取得しましたが、この他にも RGB 値を指定することでピクセル値を取得することもできます。また、色名に対応する RGB 値を問い合わせた上でその RGB 値を指定してピクセル値を取得すると言う方法もあります。これらについて以降で説明します。

○ RGB 値を指定してピクセル値を取得

xcolor.c では、XAllocNamedColor() によって色名を指定してピクセル値を取得しました。それに対して、RGB 値を指定してそれに対応するピクセル値を取得することもできます。この場合には XAllocColor() を使います。

Status XAllocColor( Display* display, Colormap colormap, XColor* screen_in_out );

取得したい色の RGB 値を screen_in_out->red、screen_in_out->green、screen_in_out->blue のそれぞれに設定して実行します。RGB 値は 4 桁の 16 進数で指定します。例えば赤を指定したいときには

screen_in_out->red   = 0xffff;
screen_in_out->green = 0x0;
screen_in_out->blue  = 0x0;

とします。また、XAllocColor() の場合は XColor 構造体の flags メンバは使用されません。

なお、 4 桁未満の数を指定した場合はその値が設定されるわけではないので注意してください(例えば 0xa を指定した場合、0xffff の 10/15 番目の値、すなわち 0xaaaa を表すとマニュアルに書かれていますが、それ以外の様々なケースに対してどうなるのか僕自身は良く分かっていませんm(__)m)。

上記の打ち消し線部分の内容について、先日通りすがりさんから "それはつまり、RGB値は、少なくとも0x1000の数値を持っている必要があるということだろうか。" というご意見メールをいただきました。それで久しぶりに本ページを自分で読んでみたのですが、すみません、自分で書いている内容の意味がよく分かりません…

何のマニュアルだか本だかを読んだのか覚えていないのですが、今考える分には X の RGB 範囲 0 〜 0xffff(65535)を任意に指定して良さそうな気がします。実際 LECTURE 17 DIB ( Device-Independent Bitmap ) の表示 では、上記の様な制限は何も考えずに XAllocColor() を使っています…

ここでは "取得したい色の RGB 値を X の RGB 範囲 0 〜 0xffff (65535) の範囲で指定する" と訂正したいと思います。もしこの表現が間違っていて、正しい内容をご存知の方にこの文書を見ていただけた場合は、ぜひ著者宛にメールでお教えください。よろしくお願いします(2007/07/06)。

XAllocColor() は成功時には 0 でない値を返し、失敗時には 0 を返します。この動作は、使用しているビジュアルによって異なります。読み書き両用のビジュアルの場合、要求された正確な RGB 値のカラーセルへの割り当てに失敗した場合、0 を 返します。

これに対して読み出し専用のビジュアルの場合、指定した値に最も近い色のカラーセルのピクセル値と、実際に使用される RGB 値が返されます。

○ 色名に対応する RGB 値を問い合わせる

色名を指定して対応するピクセル値を取得する関数として XAllocNamedColor() について説明しました。この関数の欠点(というほどでも無いですが)として、実行結果が失敗に終わった場合に色名の認識に失敗したのか、RGB 値に対応するカラーセルの割り当てに失敗したのかが区別できません。

これを避けるために、色名を指定して対応する RGB 値を取得する関数 XParseColor() が用意されています。

Status XParseColor( Display* display, 
                    Colormap colormap, 
                    char*    spec, 
                    XColor*  exact_def_return
                  );

spec             : 色の名前を表す文字列。大文字/小文字を区別しない。
exact_def_return : 正確な RGB 値が返される

この関数は、指定したカラーマップに対応するスクリーンに関して、指定した色名に対応する RGB 値を exact_def_return に返します。それと同時に DoRed / DoGreen / DoBlue が exact_def_return->flags に設定されます。色の解決ができない場合は Status として 0 を返します。

XParseColor() を使って色の解決に成功した後に、得られた RGB 値を XAllocColor() に渡して対応するピクセル値を得るという手順を踏めば、ピクセル値取得に失敗した際に - 色名の解決 / カラーセルの割り当て - という風に問題を切り分けることができます。

○ ピクセル値に設定されている RGB 値を問い合わせる

ここまでは、色(名前もしくは RGB 値)を指定して対応するピクセル値を取得するための関数について説明してきましたが、もちろんその逆を行う関数も存在します。ピクセル値を指定して、そのピクセル値に現在設定されている RGB 値を問い合わせるために XQueryColor()、XQueryColors() という関数があります。

XQueryColor( Display* display, Colormap colormap, XColor* def_in_out );
def_in_out : 指定されているピクセル値に設定されている RGB 値が返される

XQueryColors( Display* display,
              Colormap colormap,
              XColor   defs_in_out[],
              int      ncolors
            );

defs_in_out : XQueryColor() の def_in_out の配列
ncolors     : defs_in_out の配列の要素数(色数)

XQueryColor() は、def_in_out が示す XColor 構造体変数に RGB 値を知りたいピクセル値を設定して実行すると、設定されている RGB 値が格納されます。それと同時に DoRed / DoGreen / DoBlue が def_in_out->flags に設定されます。また、XQueryColors() は XQueryColor() の処理を複数ピクセル値に対して行います。