X11: XRender (1) - フォーマット

Render 拡張機能
Render 拡張機能を使うと、Pixmap のイメージにアルファ値を含めたり、アルファ合成を行うことができます。
また、アルファ値付きのカーソル画像を作成したり、テキストのグリフ描画を行うこともできます。

この機能だけでは、ウィンドウの内容を半透明にしたりすることはできません。
コンポジット型ウィンドウマネージャを使うと、Composite 拡張機能によって、ウィンドウの合成や影の描画などを行うことができますが、クライアント側が X ウィンドウのアルファ値を任意の値にするようなことはできません。
あくまで、ウィンドウマネージャによる画面効果としての合成になります。

<X11/extensions/Xrender.h> のインクルードと、-lXrender のリンクが必要です。

関数についての詳細は、Render 拡張機能 をご覧ください。
バージョン確認
Status XRenderQueryVersion(Display *dpy, int *major_versionp, int *minor_versionp);

まずは、Render 拡張機能がサーバーでサポートされているか、また、使用できるバージョンを確認します。
現在の最新バージョンは、0.11 です。

戻り値が True の場合、拡張機能がサポートされています。

また、この関数では、ライブラリ内部で XRenderQueryFormats() が行われることによって、互換性のある Pict フォーマットがリストアップされて、内部でキャッシュされます。
XRenderPictFormat
Render では、Drawable のイメージ (Window, Pixmap) 内に、アルファ値が含まれているものとして扱うことができます。
また、別の 8bit などのイメージを、アルファ値のイメージとして指定することも出来ます。

ただし、コア X では、イメージ内にアルファ値があるかどうかを判別できないので、イメージ内のピクセル値からどのように R,G,B,A 値を取得するかについては、別途情報が必要になります。

X サーバーで扱えるすべてのフォーマットはあらかじめ登録されており、XID によって関連付けられています。
クライントは、そのフォーマットの中から、必要なフォーマットを選択し、Render で処理を行いたい Drawable に対して、そのフォーマットを関連付ける必要があります。

Render で扱えるフォーマットは、XRenderPictFormat 構造体で取得できます。
XRenderPictFormat 構造体
typedef struct {
    PictFormat id;
    int type;
    int depth;
    XRenderDirectFormat direct;
    Colormap colormap;
} XRenderPictFormat;

typedef struct {
    short red;
    short redMask;
    short green;
    short greenMask;
    short blue;
    short blueMask;
    short alpha;
    short alphaMask;
} XRenderDirectFormat;

■ XRenderPictFormat
idフォーマットの XID
typePictTypeIndexed (0) : インデックスカラー。アルファチャンネルはない。
PictTypeDirect (1) : RGB or RGBA
depthピクセル値のビット数
directPictTypeDirect の場合、各 RGBA の情報
colormapPictTypeIndexed の場合、カラーマップ。
PictTypeDirect の場合はなし。

■ XRenderDirectFormat
red,green,blue,alphaピクセル値内での、それぞれのビット位置。
例: red = 16, green = 8, blue = 0, alpha = 24
redMask
greenMask
blueMask
alphaMask
それぞれのマスク値。
(ピクセル値内のマスク値ではなく、各値の最大値と等しくなる)

例: red = 0xff, green = 0xff, blue = 0xff, alpha = 0

alphaMask = 0 の場合、アルファ値は常に最大値 (不透明) となる。
{red,green,blue}Mask のいずれかが 0 の場合、R,G,B は常に 0 となる (アルファ値のみのイメージの場合)。
フォーマットを取得する関数
X サーバーに登録されているフォーマットを取得するには、以下の関数を使います。

//ビジュアルから取得

XRenderPictFormat *XRenderFindVisualFormat(Display *dpy, _Xconst Visual *visual);

//テンプレートに一致する最初のフォーマットを取得

XRenderPictFormat *XRenderFindFormat(Display *dpy, unsigned long mask,
    _Xconst XRenderPictFormat *templ, int count);

#define PictFormatID (1 << 0)
#define PictFormatType (1 << 1)
#define PictFormatDepth (1 << 2)
#define PictFormatRed (1 << 3)
#define PictFormatRedMask (1 << 4)
#define PictFormatGreen (1 << 5)
#define PictFormatGreenMask (1 << 6)
#define PictFormatBlue (1 << 7)
#define PictFormatBlueMask (1 << 8)
#define PictFormatAlpha (1 << 9)
#define PictFormatAlphaMask (1 << 10)
#define PictFormatColormap (1 << 11)

//標準フォーマットを取得

XRenderPictFormat *XRenderFindStandardFormat(Display *dpy, int format);

#define PictStandardARGB32 0
#define PictStandardRGB24 1
#define PictStandardA8 2
#define PictStandardA4 3
#define PictStandardA1 4

各フォーマットの情報は、XRenderQueryFormats() によって、ライブラリ内部でキャッシュされています。
この関数は、XRenderQueryVersion() を行った時、自動で実行されています。

上記の関数を使った場合、ライブラリ内部で確保されている XRenderPictFormat のポインタが返るので、戻り値は解放しないでください。

XRenderFindFormat 関数
templ で指定された値に一致する、一つのフォーマットを返します。
templ で使用する値は、mask で指定します。
count は、一致したフォーマットを、先頭から指定個数スキップします。

戻り値で、一致するすべてのフォーマットを配列で返してくれればわかりやすいのですが、この場合、一致したすべてのフォーマットの count 位置のフォーマットを、ライブラリ内部でキャッシュされているポインタで返します。
解放処理が全く必要のない方法になっています。

そのため、複数の一致するフォーマットを取得したい場合は、戻り値が NULL になるまで、count 引数を 0,1,2... というように、加算していって呼び出します。
count = 1 の場合、先頭の1つ目はスキップされて、2番目に一致したフォーマットが返ります。
標準フォーマット
Render を実装する X サーバーは、以下のフォーマットを常にサポートする必要があります。

  • red,green,blue,alpha = 8 bit (RGBA 32bit)
  • red,green,blue = 8 bit, alpha = 0 (RGB 24bit)
  • r,g,b = 0, alpha = 1,4,8 bit

ただ、実際に X.Org のサーバーで調べてみると、alpha = 4 bit には対応していませんでした。
プログラム
デフォルトのビジュアル、標準フォーマット、すべての Direct タイプの、各 XRenderDirectFormat 構造体の値を表示します。
また、ピクセル値中の各 RGBA ビットを表示しています。

$ cc -o run e04-render.c -lX11 -lXrender

<e04-render.c>
#include <stdio.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xrender.h>

static void _put_bits(XRenderDirectFormat *p)
{
    char m[34];
    int i,n;

    memset(m, '0', 32);

    for(i = 31 - p->red, n = 1; n < p->redMask + 1; i--, n <<= 1)
        m[i] = 'R';

    for(i = 31 - p->green, n = 1; n < p->greenMask + 1; i--, n <<= 1)
        m[i] = 'G';

    for(i = 31 - p->blue, n = 1; n < p->blueMask + 1; i--, n <<= 1)
        m[i] = 'B';

    for(i = 31 - p->alpha, n = 1; n < p->alphaMask + 1; i--, n <<= 1)
        m[i] = 'A';

    m[32] = 0;
    printf("%s\n", m);
}

static void _put_pictformat(XRenderPictFormat *pf)
{
    printf("id(0x%lx) type(%s) depth(%d)\n",
        pf->id, (pf->type == PictTypeIndexed)? "Indexed": "Direct",
        pf->depth);

    if(pf->type == PictTypeDirect)
    {
        printf("r(%d) g(%d) b(%d) a(%d)"
            " rmask(0x%x) gmask(0x%x) bmask(0x%x) amask(0x%x)\n",
            pf->direct.red, pf->direct.green, pf->direct.blue, pf->direct.alpha,
            pf->direct.redMask, pf->direct.greenMask, pf->direct.blueMask,
            pf->direct.alphaMask);

        _put_bits(&pf->direct);
    }

    printf("\n");
}

int main(int argc,char **argv)
{
    Display *disp;
    XRenderPictFormat *pf,temp;
    int major,minor,i;
    
    disp = XOpenDisplay(NULL);
    if(!disp) return 1;

    //バージョン

    if(!XRenderQueryVersion(disp, &major, &minor))
        printf("unsupported XRender\n");
    else
        printf("XRender ver %d.%d\n", major, minor);

    //standard

    for(i = 0; i <= 4; i++)
    {
        printf("-- standard %d --\n", i);
    
        pf = XRenderFindStandardFormat(disp, i);
        if(pf) _put_pictformat(pf);
    }

    //default visual

    printf("-- default visual --\n");

    i = DefaultScreen(disp);

    pf = XRenderFindVisualFormat(disp, DefaultVisual(disp, i));
    if(pf) _put_pictformat(pf);

    //Direct

    temp.type = PictTypeDirect;

    for(i = 0; 1; i++)
    {
        pf = XRenderFindFormat(disp, PictFormatType, &temp, i);
        if(!pf) break;

        printf("-- Direct %d --\n", i);
        _put_pictformat(pf);
    }

    XCloseDisplay(disp);

    return 0;
}
結果
-- standard 0 --
id(0x25) type(Direct) depth(32)
r(16) g(8) b(0) a(24) rmask(0xff) gmask(0xff) bmask(0xff) amask(0xff)
AAAAAAAARRRRRRRRGGGGGGGGBBBBBBBB

-- standard 1 --
id(0x29) type(Direct) depth(24)
r(16) g(8) b(0) a(0) rmask(0xff) gmask(0xff) bmask(0xff) amask(0x0)
00000000RRRRRRRRGGGGGGGGBBBBBBBB

-- standard 2 --
id(0x24) type(Direct) depth(8)
r(0) g(0) b(0) a(0) rmask(0x0) gmask(0x0) bmask(0x0) amask(0xff)
000000000000000000000000AAAAAAAA

-- standard 3 --
-- standard 4 --
id(0x23) type(Direct) depth(1)
r(0) g(0) b(0) a(0) rmask(0x0) gmask(0x0) bmask(0x0) amask(0x1)
0000000000000000000000000000000A

-- default visual --
id(0x29) type(Direct) depth(24)
r(16) g(8) b(0) a(0) rmask(0xff) gmask(0xff) bmask(0xff) amask(0x0)
00000000RRRRRRRRGGGGGGGGBBBBBBBB
...

デフォルトスクリーンのデフォルトビジュアルでは、アルファ値に対応していないことがわかります。

他にも、以下のようなビット状態がサポートされています (一部抜粋)。

BBBBBBBBGGGGGGGGRRRRRRRRAAAAAAAA (BGRA)
BBBBBBBBGGGGGGGGRRRRRRRR00000000 (BGR)
00000000BBBBBBBBGGGGGGGGRRRRRRRR (BGR)
00000000000000000000RRRRGGGGBBBB (15bit, rgb=4bit)
00000000000000000RRRRRGGGGGBBBBB (15bit, rgb=5bit)
0000000000000000ARRRRRGGGGGBBBBB (16bit, rgb=5bit, a=1bit)
0000000000000000AAAARRRRGGGGBBBB (16bit, rgb=4bit, a=4bit)
AARRRRRRRRRRGGGGGGGGGGBBBBBBBBBB (rgb=10bit, a=2bit)