WisdomSoft - for your serial experiences.

6.9 ダブルバッファリング

GDI+ ではコントロールを再描画するとき、背景色で塗りつぶしてから描画するため、短い期間で頻繁に再描画すると画面がちらつきます。この現象は、再描画をメモリイメージに対して行い、完成した画像で画面を上書きするダブルバッファリングと呼ばれる手法で回避できます。

6.9.1 画面のちらつき防止

短い間隔で連続して Invalidate() メソッドを呼び出し Control オブジェクトを再描画すると、再描画を行った画面上のコントロールがちらつくことを確認できます。「6.4 画像の実行時生成 コード3」を実行して画面に絵を描いてみてください。マウスイベントが発生するたびに Invalidate() を呼び出すため、画面がちらつきます。ゲームのようなマルチメディアアプリケーションは、画面を何度も更新するためちらつきを何とかしなければ使えません。

このちらつきは、コントロールが背景を塗りつぶし、その後に Graphics オブジェクトからコントロールに描画処理を行っているために発生しています。コントロールの背景を塗りつぶしているのは Windows です。これを停止させてちらつきを防止するには、Control クラスの設定を SetStyle() メソッドから変更する必要があります。

Control クラス SetStyle() メソッド
protected void SetStyle(ControlStyles flag, bool value)

このメソッドは、コントロールの動作を指定するスタイルと呼ばれる設定を変更します。flag では設定するスタイルを System.Windows.Forms.ControlStyles 列挙体で指定します。value には、指定したスタイルを有効にする場合は true を、無効にするには false を指定します。

System.Windows.Forms.ControlStyles 列挙体
[Flags]
[Serializable]
public enum ControlStyles

ControlStyles 列挙体は、FlagsAttribute 属性を保有しているためメンバを組み合わせることができます。複数のスタイルを同時に有効化したり、無効化することが可能です。

画面のちらつきを防止するには 3 つの設定を有効にしなければなりません。まず、システムではなくアプリケーションでコントロールを描画することを表す UserPaint メンバを適用しなければなりません。次に、AllPaintingInWmPaint メンバを適用して背景の塗りつぶしを行わないように設定します。AllPaintingInWmPaint は UserPaint が true になっていなければ適用されません。

最後に、Graphics による描画処理の結果をすぐに画面に表示するのではなく、すべての描画処理が終了した時点で結果のみを画面に描画するように DoubleBuffer スタイルを有効化します。これはダブルバッファリングと呼ばれる描画によるちらつきを抑える描画手法です。

ダブルバッファリングでは、描画処理を直接グラフィックス出力デバイスに送るのではなく、一度メモリデバイス(メモリ上のイメージ)に描画します。そして、すべての描画が終了した時点でメモリデバイスのイメージをグラフィックス出力デバイスに転送するという手法です。

図2 ダブルバッファリング
図2 ダブルバッファリング

ダブルバッファリングを使用しない場合、描画処理の過程が目に見えてしまう可能性があります。Graphics オブジェクトの draw~() や fill~() を使った描画処理が1度や2度の呼び出し程度であれば気にならないかもしれませんが、複雑な画面を構築するには様々な処理が必要になります。しかし、ダブルバッファリングを使えば、複雑な描画過程はすべてメモリのイメージに対して行われるためユーザーに直接見られることはなくなります。

コード1
using System.Drawing;
using System.Windows.Forms;

public class Test : Form
{
	private Image image;
	private readonly Size es = new Size(10, 10);
	protected override void OnPaint(PaintEventArgs e)
	{
		base.OnPaint (e);
		e.Graphics.DrawImage(image, 0, 0);
	}

	protected override void OnMouseMove(MouseEventArgs e)
	{
		base.OnMouseMove (e);
		if (e.Button == MouseButtons.Left) 
		{
			Graphics g = Graphics.FromImage(image);
			Brush brush = new SolidBrush(Color.Black);
			g.FillEllipse(
				brush, e.X - (es.Width / 2), e.Y - (es.Height / 2),
				es.Width, es.Height
			);
			Invalidate();
		}
	}

	public Test() 
	{
		SetStyle(
			ControlStyles.DoubleBuffer |
			ControlStyles.UserPaint |
			ControlStyles.AllPaintingInWmPaint, true
		);

		image = new Bitmap(400, 300);
		Graphics g = Graphics.FromImage(image);
		Brush brush = new SolidBrush(Color.White);
		g.FillRectangle(brush, 0, 0, image.Width, image.Height);
	}

	static void Main() 
	{
		Application.Run(new Test());
	}
}

コード1は「6.4 画像の実行時生成 コード3」にダブルバッファリングを採用したプログラムです。「6.4 画像の実行時生成 コード3」はウィンドウ上でマウスを左ボタンを押したままカーソルを移動させると、カーソル上に黒い線を引く単純なお絵かきプログラムの例ですが、イベントが発生するたびに Invalidate() メソッドを呼び出しているので画面がちらつきました。

コード1では、アプリケーションがダブルバッファリングを採用するように SetStyle() メソッドから UserPaint メンバ、AllPaintingInWmPaint メンバ、DoubleBuffer メンバの組み合わせを適用しています。これによって、フォームは再描画時にシステムによって背景色でクリアされることはなくなり、ダブルバッファリングによって描画過程はすべてメモリ上のオフスクリーンに対して行われるようになります。コード1を実行すれば、今度は線を描いたときに画面がちらつかないことを確認できます。