Picture
Render 拡張機能では、イメージの合成を行うために、Picture 型の XID が必要になります。
Picture には、「イメージを参照する Drawable」「イメージのフォーマット」「合成時に使われる属性 (XRenderPictureAttributes 構造体)」などが関連付けられています。
Render のイメージとして扱えるのは、Drawable (Window か Pixmap) だけなので、XImage は使えません。
Picture には、「イメージを参照する Drawable」「イメージのフォーマット」「合成時に使われる属性 (XRenderPictureAttributes 構造体)」などが関連付けられています。
Render のイメージとして扱えるのは、Drawable (Window か Pixmap) だけなので、XImage は使えません。
関数
Picture の作成
//Picture の作成 Picture XRenderCreatePicture(Display *dpy, Drawable drawable, _Xconst XRenderPictFormat *format, unsigned long valuemask, _Xconst XRenderPictureAttributes *attributes); //Picture の解放 void XRenderFreePicture(Display *dpy, Picture picture);
新しい Picture を作成し、そこに「イメージとなる Drawable」「drawable イメージのフォーマット」「Picture の属性」を関連付けた上で、drawable 引数のドローアブルに Picture を関連付けます。
drawable が破棄された場合は、Picture も自動的に破棄されます。
※drawable の深さと、フォーマットの深さは同じである必要があります。
※drawable がウィンドウの場合、RGB のマスクが、フォーマットのマスクと一致する必要があります。
属性については、Render 拡張機能 をご覧ください。
特に指定する属性がない場合は、valuemask = 0, attributes = NULL にできます。
合成
void XRenderComposite(Display *dpy, int op, Picture src, Picture mask, Picture dst, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, unsigned int width, unsigned int height);
src の Picture に関連付けられている Drawable のイメージを、dst の Picture に関連付けられている Drawable に合成します。
合成方法は op で指定します (通常のアルファ合成なら、PictOpOver)。
mask はアルファマスクのイメージとして使われ、合成前にソースに対してアルファ値が適用されます。
mask = None の場合、アルファ値は 1.0 として扱われます。
src_x などの各位置は、それぞれの Drawable の原点位置が基準となります。
※src, mask, dst のそれぞれのフォーマットが異なる場合、いずれかのフォーマットで、精度を保ったまま変換が可能な場合は、そのフォーマットに変換されます。
合成の注意点
ソースの画像を、イメージ内のアルファ値 (または別のイメージにあるアルファ値) を使って、合成先に合成したい場合、通常は以下のような計算式で合成されます。
ただし、XRender の場合は、この計算式に当てはまる合成オペランドがありません。
一番近いのは PictOpOver で、「src * 1.0 + dst * (1.0 - srcA)」の計算になります。
この場合、src にアルファ値が掛けられていないので、PictOpOver の合成だけでは、通常のアルファ合成は行えません。
ただし、PictOpOver の合成前に、ソースにアルファ値を掛ければ、結果として通常のアルファ合成と同じ計算になるので、その方法で合成することになります。
src * Alpha + dst * (1.0 - Alpha)
ただし、XRender の場合は、この計算式に当てはまる合成オペランドがありません。
一番近いのは PictOpOver で、「src * 1.0 + dst * (1.0 - srcA)」の計算になります。
この場合、src にアルファ値が掛けられていないので、PictOpOver の合成だけでは、通常のアルファ合成は行えません。
ただし、PictOpOver の合成前に、ソースにアルファ値を掛ければ、結果として通常のアルファ合成と同じ計算になるので、その方法で合成することになります。
アルファマスクを使う
XRenderComposite 関数などでの合成時、mask 引数に、アルファ値が含まれた Picture を指定することで、そのアルファ値をソースに適用 (乗算) することができます。
ソースが RGBA イメージで、合成にソースのアルファ値を使用したい場合は、src と mask に同じ Picture を指定して PictOpOver 合成を行えば、通常のアルファ合成が行えます。
ソースが RGBA イメージで、合成にソースのアルファ値を使用したい場合は、src と mask に同じ Picture を指定して PictOpOver 合成を行えば、通常のアルファ合成が行えます。
アルファマスクを指定しなかった場合
ソースが RGBA イメージで、合成時にアルファマスクを指定しなかった場合は、意図したアルファ合成になりません。
例えば、ソースが赤色&アルファ値付きで、合成先が青色だった場合、アルファマスクなしで PictOpOver で合成すると、以下のようになります。
ソースの色はそのままで、合成先にソースのアルファ値が掛けられるので、ソースで透明な部分は #FF00FF (紫色) になります。
本来のアルファ合成であれば青色になるはずですが、ソースの Red にアルファ値が掛けられていないので、このような結果になります。
例えば、ソースが赤色&アルファ値付きで、合成先が青色だった場合、アルファマスクなしで PictOpOver で合成すると、以下のようになります。
[PictOpOver] C = src * 1.0 + dst * (1.0 - srcA) [src:A = 0] dstR = srcR(255) * 1.0 + dstR(0) * (1.0 - 0) = 255 dstG = srcG(0) * 1.0 + dstG(0) * (1.0 - 0) = 0 dstB = srcB(0) * 1.0 + dstB(255) * (1.0 - 0) = 255 [src:A = 0.5] dstR = 255 dstG = 0 dstB = srcB(0) * 1.0 + dstB(255) * (1.0 - 0.5) = 128 [src:A = 1.0] dstR = 255 dstG = 0 dstB = srcB(0) * 1.0 + dstB(255) * (1.0 - 1.0) = 0
ソースの色はそのままで、合成先にソースのアルファ値が掛けられるので、ソースで透明な部分は #FF00FF (紫色) になります。
本来のアルファ合成であれば青色になるはずですが、ソースの Red にアルファ値が掛けられていないので、このような結果になります。
Pixmap
Render の合成ソース・合成先として使えるイメージは Drawable だけなので、XImage をイメージとして使うことはできません。
ただし、XImage でイメージを用意して、それを XPutImage() で Drawable に転送することはできます。
ウィンドウではないイメージを作りたい場合は、Pixmap を作成する必要があります。
ただし、XImage でイメージを用意して、それを XPutImage() で Drawable に転送することはできます。
ウィンドウではないイメージを作りたい場合は、Pixmap を作成する必要があります。
Pixmap の作成
Pixmap は、以下の関数で作成できます。
depth は、スクリーンでサポートされている深さであれば、指定したビット数のイメージにすることができます。
Pixmap のイメージは X サーバーが保持しているので、直接バッファを操作することはできません。
Pixmap に描画するには、通常の X グラフィック関数を使います。
バッファを直接操作可能な Pixmap を作成したい場合は、MIT-SHM 拡張機能を使って、共有メモリを使用した Pixmap を作ることができます。
Pixmap XCreatePixmap(Display *display, Drawable d, unsigned int width, unsigned int height, unsigned int depth); //解放 void XFreePixmap(Display *display, Pixmap pixmap);
depth は、スクリーンでサポートされている深さであれば、指定したビット数のイメージにすることができます。
Pixmap のイメージは X サーバーが保持しているので、直接バッファを操作することはできません。
Pixmap に描画するには、通常の X グラフィック関数を使います。
バッファを直接操作可能な Pixmap を作成したい場合は、MIT-SHM 拡張機能を使って、共有メモリを使用した Pixmap を作ることができます。
共有メモリ Pixmap
共有メモリ Pixmap は、XYPixmap または ZPixmap のフォーマットの、いずれか1つのみに対応しています。
X.Org 実装の場合は、ZPixmap になります (1px ごとにピクセル値が並ぶ)。
MIT-SHTM のページを参考にして、XShmSegmentInfo 構造体に値をセットした後、XShmCreatePixmap() で作成できます。
解放する時の手順もほぼ同じですが、戻り値の Pixmap は XFreePixmap() で解放します。
共有メモリ Pixmap は、スクリーンがサポートしている深さには依存せず、スクリーンにも依存しません。
ZPixmap フォーマットの場合、Y1行のサイズは、BitmapPad(display) マクロで取得できるビット数の単位にしなければならないので、例えばこの値が 32 だった場合は、4 byte 単位にする必要があります。
depth = 8 の Pixmap を作成する場合、幅が 201 px であれば、(201 + 3) & ~3 で、Y1行は 204 byte になります。
X.Org 実装の場合は、ZPixmap になります (1px ごとにピクセル値が並ぶ)。
MIT-SHTM のページを参考にして、XShmSegmentInfo 構造体に値をセットした後、XShmCreatePixmap() で作成できます。
解放する時の手順もほぼ同じですが、戻り値の Pixmap は XFreePixmap() で解放します。
Pixmap XShmCreatePixmap(Display *display, Drawable d, char *data, XShmSegmentInfo *shminfo, unsigned int width, unsigned int height, unsigned int depth);
共有メモリ Pixmap は、スクリーンがサポートしている深さには依存せず、スクリーンにも依存しません。
バッファサイズ
XImage の場合は、bytes_per_line に Y1行ごとのバイト数の値がセットされていたので、それを元にバッファサイズを決めることができましたが、Pixmap の場合は、自分でバッファサイズを計算する必要があります。ZPixmap フォーマットの場合、Y1行のサイズは、BitmapPad(display) マクロで取得できるビット数の単位にしなければならないので、例えばこの値が 32 だった場合は、4 byte 単位にする必要があります。
depth = 8 の Pixmap を作成する場合、幅が 201 px であれば、(201 + 3) & ~3 で、Y1行は 204 byte になります。
プログラム
背景色が青のウィンドウに、赤色で、アルファ値がグラデーションしているイメージを合成します。
閉じるボタンで終了します。
<e05-picture.c>
閉じるボタンで終了します。
$ cc -o run e05-picture.c util.c -lX11 -lXrender
<e05-picture.c>
#include <stdint.h> #include <stdio.h> #include <X11/Xlib.h> #include <sys/ipc.h> #include <sys/shm.h> #include <X11/extensions/XShm.h> #include <X11/extensions/Xrender.h> #include "util.h" XShmSegmentInfo g_seginfo; #define WIDTH 200 #define HEIGHT 200 static Pixmap _create_image(Display *disp) { Pixmap pixmap; uint32_t *pd,a; int ix,iy; //depth が 32 以下なら、Y1列のバイト数を BitmapPad(display) のビット単位にすること g_seginfo.shmid = shmget(IPC_PRIVATE, WIDTH * 4 * HEIGHT, IPC_CREAT | 0777); g_seginfo.shmaddr = shmat(g_seginfo.shmid, 0, 0); g_seginfo.readOnly = False; XShmAttach(disp, &g_seginfo); pixmap = XShmCreatePixmap(disp, g_disp.root, g_seginfo.shmaddr, &g_seginfo, WIDTH, HEIGHT, 32); //赤 + A グラデーション pd = (uint32_t *)g_seginfo.shmaddr; for(iy = 0; iy < HEIGHT; iy++) { for(ix = 0; ix < WIDTH; ix++) { a = ix * 255 / (WIDTH - 1); *(pd++) = 0xff0000 | (a << 24); } } return pixmap; } int main(int argc,char **argv) { Display *disp; Window win; XEvent ev; Pixmap pixmap; Picture picwin,picimg; XRenderPictFormat *pfwin,*pfimg; int major,minor; disp = XOpenDisplay(NULL); if(!disp) return 1; set_display(disp); XShmQueryExtension(disp); //Render if(!XRenderQueryVersion(disp, &major, &minor)) printf("unsupported XRender\n"); else printf("XRender ver %d.%d\n", major, minor); // win = create_test_window2(disp, WIDTH, HEIGHT, rgb_to_pixel(0x0000ff), ExposureMask); //ウィンドウの Picture pfwin = XRenderFindVisualFormat(disp, DefaultVisualOfScreen(g_disp.screen)); picwin = XRenderCreatePicture(disp, win, pfwin, 0, NULL); //ソース pixmap = _create_image(disp); pfimg = XRenderFindStandardFormat(disp, PictStandardARGB32); picimg = XRenderCreatePicture(disp, pixmap, pfimg, 0, NULL); //イベント XMapWindow(disp, win); while(1) { XNextEvent(disp, &ev); if(event_quit(&ev)) break; switch(ev.type) { case Expose: XRenderComposite(disp, PictOpOver, picimg, picimg, picwin, 0,0, 0,0, 0,0, WIDTH, HEIGHT); break; } } // XShmDetach(disp, &g_seginfo); shmdt(g_seginfo.shmaddr); shmctl(g_seginfo.shmid, IPC_RMID, 0); XFreePixmap(disp, pixmap); XCloseDisplay(disp); return 0; }
解説
ウィンドウは、デフォルトのビジュアルから取得した PictFormat を使って、Picture を作成します。
また、合成元のソースとして、depth = 32 の共有メモリ Pixmap を作成します。
色は赤、アルファ値は X 方向に 0〜255 のグラデーションになるようにセットします。
Expose イベントが来て、再描画のタイミングが来た時、ウィンドウの背景色によって、ウィンドウの内容は青色になっているので、その上にソースイメージを合成します。
青色の上に、赤のグラデーションが合成される形になります。
また、合成元のソースとして、depth = 32 の共有メモリ Pixmap を作成します。
色は赤、アルファ値は X 方向に 0〜255 のグラデーションになるようにセットします。
Expose イベントが来て、再描画のタイミングが来た時、ウィンドウの背景色によって、ウィンドウの内容は青色になっているので、その上にソースイメージを合成します。
青色の上に、赤のグラデーションが合成される形になります。
他の機能
他にも、Picture から Cursor (カーソル形状の XID) を作成したり、アニメーションカーソルを作成する機能があります。
また、アンチエイリアス付きのテキストを描画するために、各グリフのイメージを X サーバーに送って、サーバー側でグリフイメージを合成させることもできます。
なお、Xft ライブラリは、fontconfig、FreeType、Render 拡張機能を使って、テキスト描画を行うためのライブラリです。
ただし、グリフイメージはすべて X サーバーが保持する形になるので、日本語のようなグリフ数が多いフォントを使う場合、あまり効率が良いとは言えません。
少々手間はかかりますが、クライアントが自分で fontconfig、FreeType を処理して、XImage などのイメージに色をセットした後、そのイメージをウィンドウに転送する方が、総合的に見て使いやすくなると思います。
また、アンチエイリアス付きのテキストを描画するために、各グリフのイメージを X サーバーに送って、サーバー側でグリフイメージを合成させることもできます。
なお、Xft ライブラリは、fontconfig、FreeType、Render 拡張機能を使って、テキスト描画を行うためのライブラリです。
ただし、グリフイメージはすべて X サーバーが保持する形になるので、日本語のようなグリフ数が多いフォントを使う場合、あまり効率が良いとは言えません。
少々手間はかかりますが、クライアントが自分で fontconfig、FreeType を処理して、XImage などのイメージに色をセットした後、そのイメージをウィンドウに転送する方が、総合的に見て使いやすくなると思います。