簡易グラフィック(3)

前回描いた内容に上書きする

今までのやりかたでは、最初に書いた絵の上に上書きして、少しずつ変化する グラフィックを実現できません。なぜなら、WM_PAINTが来た時点で、すでに 今まで書いた内容を忘れているからです。(WM_PAINTは、ウィンドウが隠されて 再度見えるようになった時、送られてくるのでしたね。で、BeginPaint()は、 まずウインドウを背景色で塗りつぶすのです。)

では、少しずつ変化する絵を描くにはどうしたらよいか。要するに、プログラム側 で、今までどんな絵を書いたか覚えておけば良いわけです。そのやりかたとして、 たとえば"どんな描画コマンドをどんな順序で発行したか覚えておく"というやりかた が一つあります。これはCAD系ソフトなどで使われています。メタファイルという のも、この実現手段として使われます。

しかし、もっと単純な方法があります。要は、絵そのものを覚えておけば良いわけ です。つまり、ビットマップとして記憶します。このためには、メモリデバイスコ ンテキストを使います。メモリDCとは、スクリーンDCもしくはプリンタDCとコンパ チブルなように、メモリ上に作られた仮想的なDCであり、ここに描画しても、当然 それだけでは画面にもどこにも出ません。適切なタイミングでスクリーンDC等に コピーする必要があります。

例として、乱数を使って線を引き続けるプログラムを作成します。要点だけ説明します。

まず、WM_INITDIALOGのタイミングで以下を実行します。

	hdc=GetDC(hDlg);/*スクリーンDCを取得*/
	hMemDC = CreateCompatibleDC(hdc); /* スクリーンDCとコンパチブルなメモリDCを作成 */
	hDDBitmap = CreateCompatibleBitmap(hdc,	XDISP, YDISP); /* DDBを作成 */
	hOldBitmap = (HBITMAP)SelectObject(hMemDC, hDDBitmap);/*メモリDCにDDBを選択*/
	ReleaseDC(hDlg, hdc);
DDBとは、デバイスにどっぷり依存したビットマップです。つまり色数は画面モード と共通であり、サイズはCreateCompatibleBitmap()の引数で指定した通りになります。

あとは、これを塗りつぶしたり

	BitBlt(hMemDC, 0, 0, XDISP, YDISP, NULL, 0, 0, WHITENESS);
線を引いたりするのは、好きなタイミングでできます。
	LineTo(hMemDC, rand()%XDISP, rand()%YDISP);
WM_PAINTが来たら、メモリDCの内容をスクリーンDCにコピーします。
	case WM_PAINT:
		hdc = BeginPaint(hDlg, &ps);
		BitBlt(hdc, 0, 0, XDISP, YDISP, hMemDC, 0, 0, SRCCOPY);
		EndPaint(hDlg, &ps);
メモリDCが必要なくなったら削除します。
	SelectObject(hMemDC, hOldBitmap);
	DeleteDC(hMemDC);
	DeleteObject(hDDBitmap);
これを忘れると、システムリソースがどんどん減っていきます。

「drawline」の全ソース

実行結果

ダイアログのサイズについて

ダイアログのサイズは、システムフォントのサイズに連動しています。 つまり、16ドットフォントの画面モードでデザインしたダイアログを 20ドットの画面モードで見ると、ドット数で見たサイズが違うということです。 ダイアログに、ボタンやエディットコントロールしか配置していない ときは、この方がむしろ便利です。(コントロールのサイズも連動するため) しかし、グラフィックをやるときは、むしろ困ります。
解決方法その1 ユーザに調節させる
ダイアログのフレームのスタイルを「サイズ変更可能」にしておけば、 ユーザーがマウスで調整してくれるでしょう。このためにはスタイル指定に WS_THICKFRAME を追加します。WS_MINIMIZEBOX や WS_MAXIMIZEBOX も追加したほうが 良いでしょう。
解決方法その2 実行時にサイズ変更する
MoveWindow() APIか SetWindowPos() APIを使って、ダイアログのサイズを変更することができます。 私は
#define chgsize(hwnd,x,y) SetWindowPos(hwnd,NULL,0,0,x,y,SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE)
のようなマクロ定義をして、これを使っています。

ただし、読み込んだドキュメントのサイズに自動的に合わせるような機能は、 つけないほうが良いと思います。たとえば次回BMPファイルのビュアーを作りますが、 読み込んだBMPのサイズにあわせてダイアログのサイズを自動で変更した場合、 BMPが1*1ドットのような極端に小さい場合、もしくは10000*10000ドットみたいに 極端に大きなファイルを読んだ場合どうなるでしょうか?

解決方法その3 実行時にサイズ取得する
GetClientRect() APIを使って、ダイアログのサイズを取得し、全ての座標を そのサイズとの比率で決定すれば良いというやり方です。ボタンやエディット コントロールも配置している場合は、こちらのほうが望ましいかもしれません。

その1とその3の組合せというのも考えられますね。 (ユーザーにサイズ変更させ、そのサイズを取得する)

解決方法その4 スクロールバーをつける
この方法を使うときは、その1やその3との併用ということになると思います。 結構大変です。(そのうち取り上げます。)

前のページ

次のページ

一つ上のページに戻る