文字列の描画 - Rustで作るWindowsアプリ
目次
文字列を描画する
Windows APIで作成したウィンドウ上に文字列を描画するには以下の関数を使います。
- GetDC
- TextOutW
- ReleaseDC
GetDC
関数でデバイス コンテキストのハンドルを取得し、TextOutW
関数でそのハンドルに対して文字列を描画します。そして文字列の描画が終わったら後処理としてハンドルをReleaseDC
関数でリリースします。
デバイス コンテキストとは?
デバイス コンテキスト(DC)は簡単に言うとグラフィックに関連する構造のことを言います。線画のペンや塗りつぶしとそのためのブラシ、画面の一部をコピーするためのビットマップ、色のパレットなどそういった操作を行うにはこのデバイス コンテキストを通じて行います。
Windowsにはデバイスの独立をサポートするためのライブラリが存在し、その1つがGdi.dll
です。これはグラフィックス デバイス インターフェース(GDI)と呼ばれます。アプリはドライバーが読み込まれたらグラフィック関連の準備をするようにGDIに通知し、これらの処理はDCによって維持されます。
DCは簡単に言えば構造体です。つまりグラフィック関連の処理ではこのDCという構造体にデータが保存され参照されるということですね。DCの内容は隠ぺいされており、普通はDCの情報にアクセスするには用意されている関数などを使います。
useする関数
use windows::{ core::*, Win32::Foundation::*, Win32::Graphics::Gdi::*, Win32::System::LibraryLoader::*, Win32::UI::WindowsAndMessaging::*, };
上記のようにコードを編集して関連の関数や構造体をuse
してください。
WM_PAINT処理の修正
WM_PAINT => { // 画面がちらつくテキストの描画 let hdc: HDC = GetDC(window); TextOutW(hdc, 10, 10, w!("こんにちは。").as_wide()); ReleaseDC(window, hdc); LRESULT(0) }
ウィンドウプロシージャ関数内のWM_PAINT
の処理を上記のように変更します。
この修正を行ったあとにプログラムを実行すると以下のような画面になります。
実行してみるとわかりますが、このコードだと文字列がちらつきます。これの解決にはダブルバッファリングを使うのが一般的です。これの方法は後述します。
GetDC関数
Function windows::Win32::Graphics::Gdi::GetDC pub unsafe fn GetDC<P0>( hwnd: P0 // ウィンドウのハンドル ) -> HDC where P0: IntoParam<HWND>,
この関数は第1引数で指定されたウィンドウのハンドルのクライアント領域または画面全体のデバイス コンテキストのハンドルを得ます。このハンドルは他のGDI関連の関数で使われます。デバイス コンテキストは構造体ですがその内部仕様は不透明になっていて操作するには関数を通じて行います。
引数がNone
の場合、この関数は画面全体のDCを得ます。
返り値は成功した場合はDCのハンドルになります。
ちなみにGetDC
関数にNone
を渡すとウィンドウからはみ出した画面全体のコンテキストになります。この状態でテキストを書き込むとディスプレイ画面の好きな位置に文字列を表示できます。なかなか面白いですよね。
HDC
Struct windows::Win32::Graphics::Gdi::HDC #[repr(transparent)] pub struct HDC(pub isize);
HDC
はisize
のラッパーのタプル構造体です。
HDC
にはis_invalid()
メソッドが実装されています。このメソッドはHDC
の整数をチェックして状態が不正かどうかbool
で返します。不正の場合は返り値はtrue
になります。整数が-1
または0
だった場合、そのデバイス コンテキストは不正な状態になります。
TextOutW関数
Function windows::Win32::Graphics::Gdi::TextOutW pub unsafe fn TextOutW<P0>( hdc: P0, // デバイス コンテキストへのハンドル x: i32, // 文字列のx座標 y: i32, // 文字列のy座標 lpstring: &[u16] // 文字列 ) -> BOOL where P0: IntoParam<HDC>,
TextOutW
関数はデバイス コンテキストの指定した座標に文字列を書き込みます。返り値は成功した場合は0以外、失敗した場合は0になります。
w!()
マクロが返すPCWSTR
を&[u16]
に変換したい場合はw!("こんにちは").as_wide();
のようにas_wide()
メソッドを使います。
C言語のAPIではTextOutW
関数は文字列の長さも指定する必要がありますが、windows-rs
のTextOutW
関数は内部で暗黙的にAPIにlpstring
の長さを渡してくれてます。
文字列を変数にしておく
文字列を一度変数として保存しておきたい場合は
WM_PAINT => { let hdc: HDC = GetDC(window); let s: PCWSTR = w!("こんにちは"); TextOutW(hdc, 10, 10, s.as_wide()); ReleaseDC(window, hdc); LRESULT(0) }
というようなコードを書くことで可能です。PCWSTR
を使う場合は
use windows::{ core::{PCWSTR}, ... }
という感じでuse
することも可能です。
ReleaseDC関数
Function windows::Win32::Graphics::Gdi::ReleaseDC pub unsafe fn ReleaseDC<P0, P1>( hwnd: P0, // DCが解放されるウィンドウのハンドル hdc: P1 // DCのハンドル ) -> i32 where P0: IntoParam<HWND>, P1: IntoParam<HDC>,
この関数はほかのアプリケーションが使用できるようにデバイス コンテキスト(DC)を開放します。DCを使い終わったらこの関数で解放するようにしておきましょう。
返り値はDCが解放された場合は1
で、解放されなかった場合は0
です。