LECTURE 6 グラフィックスコンテキスト

○ はじめに

グラフィックスの描画において、グラフィックスコンテキスト( GC )は非常に重要です。GC とは、グラフィックスを描画するための情報(描画属性)を格納しておくためのものです( Windows GDI でのペンとブラシと同じで、これを指定しないと描画できません)。GC も X サーバのリソースとして管理されています。

Xlib を用いて描画する時は、まず X サーバ内のリソースとして GC を確保し、描画に必要な描画属性を GC に設定します。そして描画する時に、使いたい GC のリソース ID を指定して描画します。同じ描画属性を複数回使用するような場合は、その属性が設定されている GC のリソース ID のみを X サーバに指定すれば良いのでネットワーク伝送の効率が良いという利点があります。

ここでは

という順で説明していきます。

○ GC の生成と解放

GC の実体は XGCValues 構造体(<X11/Xlib.h>)として定義されています。

/* XGCValues 構造体 */

typedef struct
{
  int           function;           /* 表示関数 */
  unsigned long plane_mask;         /* プレーンマスク */
  unsigned long foreground;         /* フォアグラウンドカラー */
  unsigned long background;         /* バックグラウンドカラー */
  int           line_width;         /* 線幅 */
  int           line_style;         /* 線の形状 */
  int           cap_style;          /* 線の端点形状 */
  int           join_style;         /* 線の結合形状 */
  int           fill_style;         /* 塗りつぶしスタイル */
  int           fill_rule;          /* 塗りつぶし規則 */
  int           arc_mode;           /* 円弧の塗りつぶしモード */
  Pixmap        tile;               /* タイル用のピクスマップ */
  Pixmap        stipple;            /* スティップル用ビットマップ */
  int           ts_x_origin;        /* スティップルやタイルの原点 */
  int           ts_y_origin; 
  Font          font;               /* フォント */
  int           subwindow_mode;     /* 子ウィンドウへの描画モード */
  Bool          graphics_exposures; /* GraphicsExposeイベント用イベントマスク */
  int           clip_x_origin;      /* クリッピングの原点 */
  int           clip_y_origin; 
  Pixmap        clip_mask;          /* クリップマスク */
  int           dash_offset;        /* 破線の開始オフセット */
  char          dashes;             /* 破線のパターン */
} XGCValues;

(*)Bool型は <X11/Xlib.h> で次のように定義されています。
#define Bool   int
#define True   1
#define False  0

GC を生成するには XCreateGC()を使います。

GC XCreateGC( Display* display, Drawable d, unsigned long valuemask, XGCValues* values );

valuemask : GC のメンバのうち、設定、コピー、変更されるもの、
            あるいは値が返されるものをビットマスクで指定。

values    : valuemaskで指定したメンバの値を指定

この関数は valuemask、values で指定した属性値を持つ GC を新しく一つ生成します。valuemask に0、values に NULL を指定するとデフォルトの属性値を持つ GC が生成されます。各メンバに対応するデフォルトの属性値およびマスクは下表のようになっています。

メンバ デフォルト値 マスク
function GXcopy GCFunction
plane_mask 全プレーン(~0) GCPlaneMask
foreground 0 GCForeground
background 1 GCBackground
line_width 0 GCLineWidth
line_style LineSolid GCLineStyle
cap_style CapButt GCCapStyle
join_style JoinMiter GCJoinStyle
fill_style FillSolid GCFillStyle
fill_rule EvenOddRule GCFillRule
arc_mode ArcPieSlice GCArcMode
tile フォアグラウンドのピクセル値を持つピクスマップ GCTile
stipple 全ビットが1のビットマップ GCStipple
ts_x_origin 0 GCTileStipXOrigin
ts_y_origin 0 GCTileStipYOrigin
font fixed(サーバ依存) GCFont
subwindow_mode ClipByChildren GCSubwindowMode
graphics_exposures True GCGraphicsExposures
clip_x_origin 0 GCClipXOrigin
clip_y_origin 0 GCClipYOrigin
clip_mask None GCClipMask
dash_offset 0 GCDashOffset
dashes 4(リスト[4, 4]) GCDashList

必要なくなった GC を解放するためには XFreeGC() を使います。

XFreeGC( Display* dpy, GC gc );


○ GC のコピーと属性の変更

ある GC の内容を別の GC にコピーする場合は XCopyGC() を使います。

XCopyGC( Display* display, GC src, unsigned long valuemask, GC dest );

src,      : コピー元 GC
valuemask : コピーする属性を指定するマスク
dest      : コピー先 GC 

この関数は src から dest に属性をコピーします。コピーしたい属性に対応するマスクを valuemask に設定しておきます。また、src と dest はあらかじめ作成しておかなければなりません(さらにはルートウィンドウと深さが同じでなければいけません)。

既に GC に設定されている属性値を変更する場合には XChangeGC() を使います。

XChangeGC( Display* display, GC gc, unsigned long valuemask, XGCValues* values );

valuemask : 変更する属性を指定するマスク
values    : 変更する属性の値を指定

valuemask で指定した GC の属性値が values の値に設定され、それ以外は変更されません。

○ GC に設定される描画属性

XGCValues 構造体のメンバを GC の描画属性として設定できます。各属性は、既に説明したように GC の生成時に設定できますが、各属性設定用の Xlib 関数を用いて設定することも可能です。ここでは GC の描画属性のうち

  1. 表示関数(function)
  2. 色(foreground, background)
  3. 線の属性
  4. 破線の属性
  5. 塗りつぶし/タイル/スティップル
  6. 塗りつぶしの規則/モード
  7. フォント(font)
  8. 子ウィンドウへの描画モード(subwindow_mode)
  9. GraphicsExpose/NoExpose イベント生成設定(graphics_exposures)

について説明します。ここで説明していないメンバのうち、clip_ で始まるメンバに関してはクリッピングという重要な概念があります。クリッピングは、描画量を抑えたり、透過色を持つ画像を扱う際に必須の内容で、LECTURE 7 Expose イベント にてあらためて説明します。その他について興味がある方は、Xlib のマニュアルなどのより詳細な文献を参照してください。

● 表示関数(function)

描画は、描画対象( dst )およびそこに描画するもの( src )の双方の対応するピクセル同士に論理演算を施すことで描画を行います。この描画時に施される論理演算として、X ウィンドウシステムでは 16 種類の演算が定義されています(下表参照)。デフォルトの表示関数は GXcopy です。

表示関数 論理演算 演算結果(src=0011、dst=0101)
GXClear 0 0000
GCand src AND dst 0001
GXandReverse src AND (NOT dst) 0010
GXcopy src 0011
GXandInverted (NOT src) AND dst 0100
GXnoop dst 0101
GXxor src XOR dst 0110
GXor src OR dst 0111
GXnor (NOT src) AND (NOT dst)1000
GXequiv (NOT src) XOR dst 1001
GXinvert NOT dst 1010
GXorReverse src OR (NOT dst) 1011
GXcopyInvertedNOT src 1100
GXorInverted (NOT src) OR dst 1101
GXnand (NOT src) OR (NOT dst) 1110
GXset 1 1111

排他的論理和( GXxor )はラバーバンドを描画するときに良く使用されます。表示関数に GXxor を設定した GC を用いて一度描画し、その上から同じ GC を用いてもう一度同じものを描画すると前に描画したものが消去されて元通りの画像が得られます。

表示関数の設定は、XCreateGC 時に function メンバを設定した XGCValues 構造体を用いる他に、 XSetFunction()を使って設定することが出来ます。

XSetFunction( Display* display, GC gc, int function );

function : 表示関数( GXcopy、GXxor、...)を指定する。

● 色(foreground, background)

描画時に使用するフォアグラウンドカラーおよびバックグラウンドカラーをピクセル値で指定します。XCreateGC 時に foreground/background メンバを設定した XGCValues 構造体を用いる他に、XSetForeground()、XSetBackground() を使って設定することが出来ます。

XSetForeground( Display* display, GC gc, unsigned long foreground );

foreground : フォアグラウンドカラーのピクセル値

XSetBackground( Display* display, GC gc, unsigned long background );

background : バックグラウンドカラーのピクセル値

● 線の属性

  1. 線幅 ( line_width )

    線の幅をピクセル数で指定します。

  2. 線の形状 ( line_style )

    線の形状(実線/破線)を指定します。次のうちいずれかを指定します。

    定数名 説明
    LineSolid GC のフォアグラウンドカラーで実線を描画(デフォルト)
    LineOnOffDash GC のフォアグラウンドカラーで破線を描画(破線がオフの部分は描かれない)
    LineDoubleDash GC のフォアグラウンドカラーで破線を描画 (破線がオフの部分は GC のバックグラウンドカラー

  3. 線の端点形状 ( cap_style )

    線の端点の形状を指定します。次のうちいずれかを指定します。

    定数名 説明
    CapNotLast 線幅が0および1の場合には終端のピクセルを描画しない以外は CapButt と同じ
    CapButt 終端が平らで突起がない。線端は垂直
    CapRound 終端の中央を中心にした線幅の直径の半円
    CapProjecting 線幅の半分だけ終端をはみ出す

  4. 線の結合形状 ( join_style )

    2つの線の結合形状を指定します。次のうちいずれかを指定します。

    定数名 説明
    JoinMiter2本の直線の外側のエッジが交わるまで伸ばす(デフォルト)
    JoinRound接合点を中心にした線幅の直径の円弧
    JoinBevel終端は CapButt で、できた三角形の隙間を埋める

以上の描画属性を GC に設定するには、XCreateGC において line_width/line_style/cap_style/join_style メンバを設定した XGCValues 構造体を用いる他に、XSetLineAttributes()を使って設定することが出来ます。

XSetLineAttributes( Display*     display, 
                    GC           gc, 
                    unsigned int line_width, 
                    int          line_style, 
                    int          cap_style,  
                    int          join_style 
                  );

line_width : 線幅 
line_style : 線のスタイル
cap_style  : 線の端点形状
join_style : 線の結合形状

● 破線の属性

破線の属性は、そのパターンと開始オフセットで指定します。

  1. 破線のパターン(dashes)

    破線とギャップの長さをピクセル単位で指定

  2. 破線の開始オフセット(dash_offset)

    破線パターンの開始点までのオフセットをピクセル数で指定

以上の属性は、XCreateGC 時に dashes/dash_offset メンバを設定した XGCValues 構造体を用いる他に、XSetDashes() を使って設定することが出来ます。

XSetDashes( Display* display, GC gc, int dash_offset, char dash_list[], int n );

dash_offset : 破線の開始オフセット
n           : dash_list の長さ
dash_list[] : 破線のグラフィックスパターン

dash_list には、破線とギャップの長さをピクセル値で表した上で、破線とギャップのパターンを列挙したものを設定します。たとえば 3 ピクセルの線、2 ピクセルのギャップ、4 ピクセルの線、1 ピクセルのギャップ、以降はそのパターンの繰り返しという破線を設定するなら

char dash_list[] = { 3, 2, 4, 1 };

と設定します。dash_offset にはこのパターンの中の何ピクセル目から線を引き始めるかを設定します。

● 塗りつぶし/タイル/スティップル

塗りつぶしの方法には、FillSolid、FillTiled、FillStippled、そして FillOpaqueStippled の 4 種類があります。それぞれの内容は下表の通りです。

定数名 説明
FillSolid GC のフォアグラウンドカラーで塗りつぶす(デフォルト)
FillTiled GC の tile で指定したピクスマップを敷きつめる(タイリング)
FillStippled GC の stipple で指定したビットマップでマスクした領域を GC のフォアグラウンドカラーで塗りつぶす(スティップル)
FillOpaqueStippled GC の stipple で指定したビットマップのビットが設定されている部分は GC のフォアグラウンドカラーで、ビットが設定されていない部分は GC のバックグラウンドカラーで塗りつぶす

上の塗りつぶしの方法を設定するには、XCreateGC 時に fill_style メンバを設定した XGCValues 構造体を用いる他に、XSetFillStyle() を使って設定することが出来ます。

XSetFillStyle( Display* display, GC gc, int fill_style );

fill_style : 塗りつぶしのスタイル

タイリングに使用するピクスマップ、およびスティップルに使用するビットマップはそれぞれ、XCreateGC 時に tile/stipple メンバを設定した XGCValues 構造体を用いる他に、XSetTile() および XSetStipple() を使って設定することが出来ます。

XSetTile( Display* display, GC gc, Pixmap tile );

tile : タイリング用ピクスマップID

XSetStipple( Display* display, GC gc, Pixmap stipple );

stipple : スティップル用ビットマップのID


● 塗りつぶしの規則/モード

  1. 塗りつぶしの規則( fill_rule )

    ポリゴンを塗りつぶす場合の、塗りつぶし規則を指定します。次の2つが指定できます。

    定数名 説明
    WindingRule 重なっている領域はすべて塗りつぶす
    EvenOddRule 奇数回重なっている領域は塗りつぶさない

    塗りつぶしの規則の指定には、XCreateGC 時に fill_rule メンバを設定した XGCValues 構造体を用いる他に、XSetFillRule() を使って設定することが出来ます。

       XSetFillRule( Display* display, GC gc, int fill_rule );
    
       fill_rule : WindingRule か EvenOddRuleを指定
    
    

  2. 円弧の塗りつぶしモード( arc_mode )

    円弧を塗りつぶす場合の塗りつぶしモードを指定します。次の2つが指定可能です。

    定数名 説明
    ArcPieSlice 円弧の端点と円弧の中心を結んで閉じた領域を塗りつぶす
    ArcChord 円弧の端点同士を結んで閉じた領域を塗りつぶす

    円弧の塗りつぶしモードの設定には、XCreateGC 時に arc_mode メンバを設定した XGCValues 構造体を用いる他に、XSetArcMode() を使って設定することが出来ます。

       XSetArcMode( Display* dpy, GC gc, int arc_mode );
    
       arc_mode : ArcPieSlice か ArcChordを指定
    
    

● フォント(font)

文字列を描画する際に用いるフォントを指定します。フォントの設定は、XCreateGC 時に font メンバを設定した XGCValues 構造体を用いる他に、XSetFont() を使って設定することが出来ます。

XSetFont( Display* display, GC gc, Font font );

font : フォントID

ところで当たり前のことですが、フォントを設定する前にフォントをロードしなければなりません。フォントをロードするには XLoadQueryFont() を使います。

XFontStruct* XLoadQueryFont( Display* display, char* fontname );

fontname : フォント名 ( "-*-fixed-medium-r-normal--16-*-*-*" の様な形式 )

指定したフォントが見付からなかった場合、XLoadQueryFont() は NULL を返します。フォントが見付かった場合は、XFontStruct 構造体へのポインタが返って来ますが、XSetFont() に指定するフォント ID には XFontStruct のメンバである fid を指定します。

ところでこれらは半角英数字などの 1 バイト文字描画用の関数であり、日本語のようなマルチバイト文字の描画については以降の LECTURE で別途説明します。

● 子ウィンドウへの描画モード(subwindow_mode)

親ウィンドウ上の子ウィンドウ領域に対しても描画を行うかどうかを指定します。

定数名 説明
ClipByChildren 子ウィンドウ領域には描画しない(デフォルト)
IncludeInferiors 子ウィンドウ領域に描画する

子ウィンドウへの描画モードの設定には、XCreateGC 時に subwindow_mode メンバを設定した XGCValues 構造体を用いる他に、XSetSubwindowMode() を使って設定することが出来ます。

XSetSubwindowMode( Display* display, GC gc, int subwindow_mode );

subwindow_mode : ClipByChildren かIncludeInferiors を指定

● GraphicsExpose/NoExpose イベント生成設定(graphics_exposures)

GC に対して明示的に指定しない限り、XCopyPlane/XCopyArea 実行時に GraphicsExpose/NoExpose イベントがクライアントに対して送られて来ます ( XSelectInput によるマスクの有無関係なしに )。これらのイベントを送らないでもらう様にするには、XCreateGC 時に graphis-exposures メンバを False に設定した XGCValues 構造体を用いて生成します ( 今の所、それ以外の方法が分かりません )。