ダブルバッファ
ダブルバッファ拡張機能 (DBE) を使うと、ウィンドウのダブルバッファリングを実現できます。
主に、アニメーション・動画・ゲームなどで、ウィンドウの画面を、ちらつきなく表示したい時に使います。
(ダブルバッファリングを使ったとしても、モニタの更新タイミングなどによって、ちらつきが起こる可能性があるため、完全に防げるわけではありません)
ダブルバッファリングでは、現在画面に表示されている「フロントバッファ」と、画面に表示されない状態で次のイメージを描画するための「バックバッファ」の、2つのイメージを使います。
次に表示するイメージをバックバッファに描画しておき、それをフロントバッファと交換することで、ウィンドウ画面を (可能な限り) 一瞬で切り替えます。
主に、アニメーション・動画・ゲームなどで、ウィンドウの画面を、ちらつきなく表示したい時に使います。
(ダブルバッファリングを使ったとしても、モニタの更新タイミングなどによって、ちらつきが起こる可能性があるため、完全に防げるわけではありません)
ダブルバッファリングでは、現在画面に表示されている「フロントバッファ」と、画面に表示されない状態で次のイメージを描画するための「バックバッファ」の、2つのイメージを使います。
次に表示するイメージをバックバッファに描画しておき、それをフロントバッファと交換することで、ウィンドウ画面を (可能な限り) 一瞬で切り替えます。
DBE
<X11/extensions/Xdbe.h> のインクルードと、-lXext のリンクが必要です。
初期化などについての説明は省略します。
詳細は、Double Buffer 拡張機能 をご覧ください。
初期化などについての説明は省略します。
詳細は、Double Buffer 拡張機能 をご覧ください。
使い方
DBE では、1つの X ウィンドウに複数のバックバッファを作成して、それをウィンドウに関連付けることができます。
フロントバッファ= X ウィンドウの描画内容となります。
フロントバッファ= X ウィンドウの描画内容となります。
バックバッファの作成
指定ウィンドウにバックバッファを追加するには、以下の関数を使います。
XdbeBackBuffer は、Drawable 型 (つまり XID) です。
バックバッファに描画をしたい時は、X のグラフィック関数の描画先に、この XdbeBackBuffer の ID を指定します。
フロントバッファに描画したい場合は、通常通り Window の ID を指定することになります。
※バックバッファの実際のイメージデータは X サーバーが保持しているので、直接バッファを参照することはできません。
バックバッファはウィンドウに関連付けられているので、ウィンドウが破棄された時は、バックバッファも自動的に破棄されます。
(実際の交換時には、改めてアクションを指定できるため、この値はあくまで目安として扱われますが、出来る限り用途に合ったアクションを指定してください)
スワップした後、バックバッファ全体を改めて再描画する場合は、XdbeUndefined で構いません。
背景色でクリアしてから、バックバッファへの次の描画を行いたい場合は、XdbeBackground にします。
バックバッファの内容を、スワップ前の状態で維持して、更新する部分だけを描画したい場合は、XdbeUntouched にします。
フロントバッファ (画面のウィンドウ内容) をコピーして、更新する部分だけ描画したい場合は、XdbeCopied にします。
//バックバッファの作成 XdbeBackBuffer XdbeAllocateBackBufferName(Display *dpy, Window window, XdbeSwapAction swap_action); //バックバッファの削除 Status XdbeDeallocateBackBufferName(Display *dpy, XdbeBackBuffer buffer);
XdbeBackBuffer は、Drawable 型 (つまり XID) です。
バックバッファに描画をしたい時は、X のグラフィック関数の描画先に、この XdbeBackBuffer の ID を指定します。
フロントバッファに描画したい場合は、通常通り Window の ID を指定することになります。
※バックバッファの実際のイメージデータは X サーバーが保持しているので、直接バッファを参照することはできません。
バックバッファはウィンドウに関連付けられているので、ウィンドウが破棄された時は、バックバッファも自動的に破棄されます。
swap_action
XdbeAllocateBackBufferName() 時に指定する swap_action は、フロントバッファとバックバッファの交換時に、交換後のバックバッファの内容がどうなるかという、ヒントを指定します。(実際の交換時には、改めてアクションを指定できるため、この値はあくまで目安として扱われますが、出来る限り用途に合ったアクションを指定してください)
XdbeUndefined | バックバッファの内容は未定義 (交換後に内容がどうなっても構わない) |
---|---|
XdbeBackground | バックバッファは、ウィンドウの背景色で塗りつぶされる |
XdbeUntouched | バックバッファの内容は、交換によって変更されない (そのままの状態を維持) |
XdbeCopied | バックバッファは、フロントバッファのコピーとなる |
スワップした後、バックバッファ全体を改めて再描画する場合は、XdbeUndefined で構いません。
背景色でクリアしてから、バックバッファへの次の描画を行いたい場合は、XdbeBackground にします。
バックバッファの内容を、スワップ前の状態で維持して、更新する部分だけを描画したい場合は、XdbeUntouched にします。
フロントバッファ (画面のウィンドウ内容) をコピーして、更新する部分だけ描画したい場合は、XdbeCopied にします。
スワップ
フロントバッファとバックバッファを交換したい場合、以下の関数を使います。
複数のウィンドウをまとめてスワップできます。
スワップすると、画面が更新されます。
複数のウィンドウをまとめてスワップできます。
Status XdbeSwapBuffers(Display *dpy, XdbeSwapInfo *swap_info, int num_windows); typedef struct { Window swap_window; //ウィンドウ XdbeSwapAction swap_action; //アクション } XdbeSwapInfo;
スワップすると、画面が更新されます。
プログラム
起動時に、バックバッファに対応しているビジュアルのリストを表示します。
左クリックすると、バックバッファを使って、ウィンドウの内容を赤 <-> 緑に切り替えます。
閉じるボタンで終了します。
本来ならアニメーションを表示したいところですが、アイドル時のタイマー処理を実装しなければならないので、簡単なサンプルとしました。
<e03-dbe.c>
左クリックすると、バックバッファを使って、ウィンドウの内容を赤 <-> 緑に切り替えます。
閉じるボタンで終了します。
本来ならアニメーションを表示したいところですが、アイドル時のタイマー処理を実装しなければならないので、簡単なサンプルとしました。
$ cc -o run e03-dbe.c util.c -lX11 -lXext
<e03-dbe.c>
#include <stdio.h> #include <X11/Xlib.h> #include <X11/extensions/Xdbe.h> #include "util.h" static void _put_info(Display *disp) { XdbeScreenVisualInfo *info; XdbeVisualInfo *vi; int i,num = 1; info = XdbeGetVisualInfo(disp, &g_disp.root, &num); if(!info) return; vi = info->visinfo; for(i = 0; i < info->count; i++) { printf("[%d] visual(0x%lx) depth(%d) plevel(%d)\n", i, vi[i].visual, vi[i].depth, vi[i].perflevel); } XdbeFreeVisualInfo(info); } static void _draw(Display *disp,Drawable dst,int count) { int rgb; rgb = (count)? 0xff0000: 0x00ff00; XSetForeground(disp, g_disp.gc, rgb_to_pixel(rgb)); XFillRectangle(disp, dst, g_disp.gc, 0, 0, 200, 200); } int main(int argc,char **argv) { Display *disp; Window win,back; XEvent ev; XdbeSwapInfo swap; int major,minor,count = 0; disp = XOpenDisplay(NULL); if(!disp) return 1; set_display(disp); //DBE if(!XdbeQueryExtension(disp, &major, &minor)) printf("unsupported DBE\n"); else printf("DBE ver %d.%d\n", major, minor); _put_info(disp); // win = create_test_window2(disp, 200, 200, 0, ButtonPress); //バックバッファ back = XdbeAllocateBackBufferName(disp, win, XdbeUndefined); _draw(disp, back, count); swap.swap_window = win; swap.swap_action = XdbeUntouched; //イベント XMapWindow(disp, win); while(1) { XNextEvent(disp, &ev); if(event_quit(&ev)) break; if(ev.type == ButtonPress && ev.xbutton.button == Button1) { XdbeSwapBuffers(disp, &swap, 1); count ^= 1; _draw(disp, back, count); } } XCloseDisplay(disp); return 0; }