CreatePenとCreateBrushでGDIオブジェクトを作成する
WindowsのGDI(Graphics Device Interface)で線や図形を描画するには、描画先のデバイスコンテキスト(HDC)だけでなく、線の属性を表すペン(HPEN)や塗りつぶし属性を表すブラシ(HBRUSH)が必要です。CreatePenとブラシ生成APIを使うと、色、太さ、線種、塗りつぶしパターンをプログラムから指定できます。
GDIオブジェクトは作成して選択するだけでなく、使用後に元のオブジェクトへ戻し、不要になった時点で解放することが重要です。ここでは、CreatePen、CreateSolidBrush、CreateBrushIndirectを中心に、基本的な使い方と管理上の注意点を整理します。
ペンとブラシの役割を理解する
ペンは輪郭線の描画に使われます。CreatePenの基本形は次のとおりです。
HPEN hPen = CreatePen(PS_SOLID, 2, RGB(0, 0, 255));
第1引数は線種、第2引数は幅、第3引数は色です。線種にはPS_SOLID、PS_DASH、PS_DOTなどがあり、破線や点線も指定できます。ただし、線幅が1より大きい場合は、線種によって期待した表示にならない場合があります。
ブラシは閉じた図形の内部を塗るためのGDIオブジェクトです。単色ならCreateSolidBrush、ハッチングならCreateHatchBrush、詳細な属性をまとめて指定するならCreateBrushIndirectを使います。Windows APIには用途別のブラシ生成関数が用意されているため、一般的なコードでは「CreateBrush」という単一の関数ではなく、目的に合う関数を選択します。
デバイスコンテキストへ選択する
作成したペンとブラシは、SelectObjectでHDCへ選択してから描画に使用します。ウィンドウの再描画なら、通常はWM_PAINT内のBeginPaintとEndPaintの間で処理します。
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HPEN hPen = CreatePen(PS_SOLID, 3, RGB(30, 90, 180));
HBRUSH hBrush = CreateSolidBrush(RGB(220, 240, 255));
HGDIOBJ oldPen = SelectObject(hdc, hPen);
HGDIOBJ oldBrush = SelectObject(hdc, hBrush);
Rectangle(hdc, 40, 30, 240, 130);
SelectObject(hdc, oldBrush);
SelectObject(hdc, oldPen);
DeleteObject(hBrush);
DeleteObject(hPen);
EndPaint(hwnd, &ps);
}
break;
Rectangleでは、ペンが外周線、ブラシが内部の塗りつぶしに使用されます。選択前のオブジェクトを保存しておき、描画後に元へ戻すのが基本です。選択されたままのGDIオブジェクトを削除すると、失敗したり、描画状態が不定になったりするためです。
CreateBrushIndirectの属性指定
複数のブラシ属性を構造体で扱う場合は、LOGBRUSHを利用します。たとえば、ハッチブラシは次のように作成できます。
LOGBRUSH info = {};
info.lbStyle = BS_HATCHED;
info.lbHatch = HS_DIAGCROSS;
info.lbColor = RGB(120, 120, 120);
HBRUSH hBrush = CreateBrushIndirect(&info);
lbStyleにはBS_SOLID、BS_NULL、BS_HATCHEDなどを指定できます。BS_NULLは内部を塗らないブラシで、図形の背景を透過的に扱いたい場合に便利です。ただし、背景の消去や再描画の仕組みまで自動的に整うわけではないため、ちらつき対策は別途必要です。
GUIアプリケーションでは、GDIハンドルを複数のスレッドから無秩序に共有しないことも大切です。描画処理と状態管理を分ける場合は、共有データの保護方法を設計し、必要に応じてクリティカルセクションの初期化などの同期機構を検討します。単にペンやブラシをグローバル変数に置くだけでは、安全なスレッド設計にはなりません。
リソース管理で確認する項目
GDIオブジェクトはカーネルやプロセスが管理する有限のリソースです。毎回CreatePenやCreateSolidBrushを呼び出して破棄しないコードは、長時間動作するアプリケーションでGDIリークを起こします。Create系関数の戻り値がNULLでないかも確認してください。
管理時に意識したい点は次のとおりです。
SelectObjectの前に作成結果がNULLでないか確認する- 選択前のペンやブラシを保存して描画後に戻す
- 選択中のGDIオブジェクトを
DeleteObjectしない - ウィンドウ破棄時や不要になった時点でハンドルを解放する
- 共有する場合は、生成と破棄の所有者を明確にする
同じ色や線幅を何度も使う場合は、ウィンドウ作成時にオブジェクトを生成し、破棄時にまとめて解放する設計も可能です。一方、描画ごとに異なる色を使う場合は、使用範囲を狭くして、処理の直後に元へ戻して削除すると管理しやすくなります。
APIの使い分けと描画結果
ペンとブラシは似た「描画属性」に見えますが、担当範囲が異なります。線を引くLineToや図形の輪郭にはペン、EllipseやPolygonの内部にはブラシが使われます。文字の背景モードや背景色は、SetBkModeやSetBkColorなど別の状態で制御します。
| 目的 | 主なAPI | 戻り値 | 主な設定 |
|---|---|---|---|
| 実線や破線を作る | CreatePen |
HPEN |
線種、幅、色 |
| 単色で塗る | CreateSolidBrush |
HBRUSH |
塗りつぶし色 |
| ハッチングで塗る | CreateHatchBrush |
HBRUSH |
パターン、色 |
| 構造体で指定する | CreateBrushIndirect |
HBRUSH |
スタイル、色、ハッチ |
| 既存オブジェクトを選択する | SelectObject |
HGDIOBJ |
HDCの描画状態 |
| オブジェクトを解放する | DeleteObject |
BOOL |
ペン、ブラシなど |
描画の座標は論理単位で扱われることがあり、マッピングモードやDPI設定によって見た目が変わります。高DPI環境で線幅や図形サイズを一定の物理サイズに近づけたい場合は、DPIに応じて幅や座標を計算してください。GDIの基礎仕様を確認する際は、Windows APIを分類して参照できるAPIリファレンスも役立ちます。
CreatePenとブラシ生成APIを使う際に覚えておくべき点は、属性の指定、HDCへの選択、元オブジェクトの復元、そしてDeleteObjectによる解放です。描画コードをこの順序で組み立てれば、線と塗りつぶしを安定して制御できます。GDIオブジェクトは作成したら、使う場所と所有者、解放するタイミングまで設計することが重要です。