第5章 色を織りなす


8bitモードでは16,777,216色の色のなかから同時に256色の色を表示する ことができます。窓に色々な色を織り込むと、それだけで楽しい アプリケーションになるでしょう。この章では色の操作の仕方について 解説します。


カラーマップ

人間が認識するすべての色は赤緑青(RGB)の3色の混ぜ具合を調整する ことで表現できます。コンピュータでの色もこのRGBの明るさを調整する ことで表現されています。ひとつの色のRGBの明るさの度合(輝度)を 記録する変数のことをカラーセルと呼びます。 Xserverでは256個のカラーセルを同時に管理することができます。 そのようなカラーセルの集まったものをカラーマップと呼びます。 カラーマップの各カラーセルには0から255まで順番にpixel値と呼ばれる 番号が記録されています。普通このpixel値を用いてカラーマップの中の 色セルを指定します。 カラーマップに関する操作をするにはまず最初にDefaultColormap() 関数(マクロ)によってこのカラーマップのIDを入手しておかなければ なりません。

Prototype

Colormap DefaultColormap( Display* display, int screen_num );

引数のscreen_numは0にしておきます。返り値はカラーマップのIDです。

Example

Colormap cmap;

cmap = DefaultColormap( dis, 0 );


色のpixel値を知る

カラーマップの先頭の数十セルには、基本的な色がでたらめの順序で 入っています。残りのセルは未使用セルで何の色も入っていません。
自分の欲しい色がどのセルに入っているかを探すには XAllocNamedColor()関数を使うのが簡単です。

Prototype

Bool XAllocNamedColor( Display* display, Colormap cmap, char* color_name, XColor* near_color, XColor* true_color );

引数のcmapには、先に DefaultColormap()関数によって得られた カラーマップのIDを渡します。color_nameには色の名前を表す文字列を 渡します。
色の名前は xco というアプリケーションにより分かります。また /usr/(local/)X11R6/lib/X11/rgb.txt には色のRGB値と名前が定義 されています。これに定義されていない名前は使えません。

指定した名前の色がカラーマップのセルにあると、そのセルに関する 情報がnear_colorで示すXColor型の構造体に格納されます。
指定した名前の色がカラーマップのセルにないと、カラーマップの未使用セル にその色の情報が登録されて、その情報がnear_colorに格納されます。
何らかの理由で色の情報を新たな未使用セルに登録できない場合には、 その色と似た色のセルの情報がnear_colorに格納されます。 この時 true_color に指定した色の正確な情報が格納されます。

結局、この関数は指定した色、もしくはそれに似た色の 情報が得られたら True を返し、失敗したら False を返します。

XColor構造体は X11/Xlib.hで次のように定義されています。

typedef struct {
  unsigned long pixel;
  unsigned short red, green, blue;
  char flags;
  char pad;
} XColor;

pixelにはpixel値、red,green,blueにはRGBの輝度が0から65535までの 範囲の値で格納されています。RGBの本当の階調は256段階なのですが これにはどういうわけかそれを256倍した値が格納されています。

以上を踏まえて、GCに色を指定する例を示します。

Example

Colormap cmap;
XColor near_color, true_color;

cmap = DefaultColormap( dis, 0 );
XAllocNamedColor( dis, cmap, "LightSkyBlue", &near_color, &true_color );

XSetForeground( dis, gc, near_color.pixel  );


共有セルと私有セル

カラーマップはXserverによって管理され、画面にあるすべての窓が このひとつのカラーマップを参照します。 それゆえカラーマップに最初から登録されている先頭の数十色のセルは 勝手に変更できないようなっています。

XClientが新しく色をカラーマップの未使用セルに登録する際に、 そのセルに対して2種類の性質のうちどちらかを選択することができます。 その性質とは、そのセルの色を後で変更できるか固定であるかです。

色が固定のセルのことを共有セルと呼び、変更可能なセルの ことを私有セルと呼びます。色を新しく登録する際には目的に応じて これらを選択します。カラーマップに最初から登録されている 先頭の数十色のセルは共有セルです。


新しい色を共有セルとして登録する

先のXAllocNamedColor()関数は指定の色がカラーマップの共有セルにない 場合には、未使用セルに共有セルとして登録します。どちらにせよ得られる セルは共有セルです。

名前で色を指定するのではなく、そのRGB値で指定するには XAllocColor()関数を使います。この関数には事前にXColor構造体変数に RGB値を記して渡します。

Prototype

Bool XAllocColor( Display* display, Colormap cmap, XColor* color );

カラーマップの共有セルに指定のRGB値を持つセルがあれば そのセルのpixel値が渡されたXColor構造体変数のpixelに格納されます。
指定のRGB値を持つセルがなければ、未使用セルにこの色が登録されて そのセルのpixel値が渡されたXColor構造体変数のpixelに格納されます。

この関数は指定した色のpixel値が得られればTrueを返し失敗したら Falseを返します。

Example

XColor col;
col.green = 256*75;
col.red   = 256*4;
col.blue  = 256*19;
XAllocColor( dis, cmap, &col );

XSetForeground( dis, gc, col.pixel );

XAllocNamedColor()関数やXAllocColor()関数によって共有セルを 得ることの利点は、もし指定の色がすでにカラーマップにあるならば 新たにセルを使わずに既存のセルを使うのでセルの節約になることです。 セルは全部で256個しかなく、それをすべての窓が使うのですから 節約できるものは節約するべきなのです。


新しい色を私有セルとして登録する

私有セルに色を登録するには、その前にカラーマップの未使用セルを 私有セルとして明言しなければなりません。それからRGB値を書き込みます。 私有セルとして明言するには XAllocColorCells()関数を使います。

XAllocColorCells()関数はかなり込み入っているので、ここではその 基本的な使い方を解説します。応用的な使い方はその後で解説します。
次のようにすることで4色分の私有セルが得られます。

Example

Colormap cmap;
unsigned long sub_mask[1], pixel_base[4];

cmap = DefaultColormap( dis, 0 );
XAllocColorCells( dis, cmap, True, sub_mask, 0, pixel_base, 4 );

XAllocColorCells()関数の第7引数の整数値は、新たに私有セルとして 明言するセルの数を指定します。第6引数の配列にその私有セルの pixel値が格納されます。第3引数のBool値は、ここで私有セルとして 明言するセルのpixel値が連続する必要があるかないかを指定します。 Trueで連続します。Falseで連続できなければ分散します。
その他の引数についてはまだ気にしないでください。

こうして配列 pixel_base[] に4色分の私有セルのpixel値が 格納されます。

次に得られた私有セルに色を登録します。色の名前で登録する 方法と、色のRGB値で登録する方法の2通りがあります。

Prototype

void XStoreNamedColor( Display* display, Colormap cmap, char* color_name, unsigned long pixel, int flags );

この関数は pixelで指定する私有セルに、color_nameで指定する名前の 色を登録します。flagsには通常 DoGreen|DoRed|DoBlue と指定します。

Prototype

void XStoreColor( Display* display, Colormap cmap, XColor* color );
void XStoreColors( Display* display, Colormap cmap, XColor* colors, int num );

XStoreColor()関数は指定のXColor構造体変数に記されているRGB値を pixel値の私有セルに登録します。
XStoreColors()関数は配列としてXColor構造体変数を渡すことで 複数の色についてXStoreColor()関数と同じ作業をします。 色の数をnumで指定します。
どのXColor構造体変数にもそのflagsメンバにDoGreen|DoRed|DoBlue と 指定しておかなければなりません。

Example

XColor cols[4];

for( int i=0 ; i<4 ; i++ ){
    cols[i].pixel = pixel_base[i];
    cols[i].red   = 256*i;
    cols[i].green = 256*i;
    cols[i].blue  = 256*i;
    cols[i].flags = DoGreen|DoRed|DoBlue;
}
XStoreColors( dis, cmap, cols, 4 );

XSetForeground( dis, gc, cols[0].pixel  );
/* または */
XSetForeground( dis, gc, pixel_base[0] );

私有セルを得ることの利点は、第一に色の内容を後から変更することが できることです。つまり、この色で何か図形を描いたあとで、この 色の内容を変更すると、先に描いた図形の色が瞬時に更新されるのです。
第二の利点は複数のセルを連続的に得ることによりあとで解説するように より高度な描画操作が可能になることです。


私有セルのより高度な確保の仕方

私有セルを確保する関数 XAllocColorCells() のより高度な使い方を 解説します。

先の例では確保された私有セルのpixel値が配列 pixel_base[]に 格納されていました。XAllocColorCells()関数は、pixel_base[]の個々の pixel値に対して、さらに従属的なpixel値をもつ私有セルを確保する ことができます。その私有セルのpixel値は配列 sub_mask[]に 格納されているmask値とpixel_base[]のpixel値との論理和で表されます。

例を示して解説します。次のように XAllocColorCells()関数を 使います。

Example

unsigned long sub_mask[3], pixel_base[2];
XAllocColorCells( dis, cmap, True, sub_mask, 3, pixel_base, 2 );

各配列に格納される値を2進数で表すと例えば次のようになります。

sub_mask[0] 00000001
sub_mask[1] 00000010
sub_mask[2] 00000100
pixel_base[0] 01001000
pixel_base[1] 01010000

注目すべきことは、sub_mask[]の下位3ビットのうち、それぞれ 別なひとつのビットが1になっていることと、pixel_base[]の下位3 ビットは0で、その上位のビットは適当な2種の数になっていることです。

この時、確保される私有セルのpixel値は、pixel_base[0]、pixel_base[1] の2つの他に、そのそれぞれのpixel値に3つのsub_mask[]の任意を論理和 したものをさらに論理和したpixel値のセルが確保されています。 つまり総計 2*2^3 = 16 色の私有セルが確保されるのです。 この例についてそのpixel値の具体値を示しておきます。

表現 pixel値(2進) pixel値(10進)
pixel_base[0] 01001000 72
pixel_base[0]|sub_mask[0] 01001001 73
pixel_base[0]|sub_mask[1] 01001010 74
pixel_base[0]|sub_mask[0]|sub_mask[1] 01001011 75
pixel_base[0]|sub_mask[2] 01001100 76
pixel_base[0]|sub_mask[0]|sub_mask[2] 01001101 77
pixel_base[0]|sub_mask[1]|sub_mask[2] 01001110 78
pixel_base[0]|sub_mask[0]|sub_mask[1]|sub_mask[2] 01001111 79
pixel_base[1] 01010000 80
pixel_base[1]|sub_mask[0] 01010001 81
pixel_base[1]|sub_mask[1] 01010010 82
pixel_base[1]|sub_mask[0]|sub_mask[1] 01010011 83
pixel_base[1]|sub_mask[2] 01010100 84
pixel_base[1]|sub_mask[0]|sub_mask[2] 01010101 85
pixel_base[1]|sub_mask[1]|sub_mask[2] 01010110 86
pixel_base[1]|sub_mask[0]|sub_mask[1]|sub_mask[2] 01010111 87

ここでXAllocColorCells()関数のPrototypeをまとめます。

Prototype

Bool XAllocColorCells( Display* display, Colormap cmap, Bool conting, unsigned long* sub_mask, int mask_num, unsigned long* pixel_base, int base_num );

contingは確保する私有セルのpixel値が連続する必要がある場合には Trueを指定し、必要がない場合にはFalseを指定します。
sub_maskはmask値を格納する配列です。mask_numはsub_mask配列の要素 数です。
pixel_baseは基本pixel値を格納する配列です。base_numはpixel_base配列の 要素数です。
この関数は指定された分の私有セルの確保に成功するとTrueを返し 失敗するとFalseを返します。

さて、先の例のように sub_mask[] と pixel_base[] を組み合わせて pixel値を指定することの利点を解説します。

このようにpixel値がビット演算によって得られるので、図形のOR描画 による色を指定することができるようになります。また各ビットを明示的に 指定できるので図形描画のより高度な操作が可能になります。

特殊かつ具体的な例を示すと、 pixel_base[0]とpixel_base[1]の2色で窓に模様を描いておきます。 その上に sub_mask[0], sub_mask[1], sub_mask[2] などで OR描画 すると、その結果の色は私有セルで確保されている色になります。 pixel_base[0]の模様の上に描く場合とpixel_base[1]の模様の上に 描く場合とでOR演算の結果が異なるので、私有セルの色を調整する ことにより、さまざま効果を演出できます。また、これらで いろいろ上塗りした窓のすべての点のpixel値の下位3ビットを0 にすると、それだけでpixel_base[0]とpixel_base[1]の2色の最初の 模様に戻ります。

この効果は工夫次第でさまざまな特殊描画に利用できます。


  • 第6章 絵を使いまわす
  • 目次
    Copyright(C) by Naoki Watanabe. Oct 21st, 1995.
    渡辺尚貴 naoki@cms.phys.s.u-tokyo.ac.jp