GDI (Graphics Device Interface)

デバイスコンテキスト (Device context : DC)

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

    LPTSTR str;

    switch (message)
    {
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);

        str = L"SAMPLE";
        TextOut(hdc, 0, 0, str, lstrlen(str));

        EndPaint(hWnd, &ps);
        break;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

デバイスコンテキストの取得

WM_PAINTメッセージを処理するときだけ、BeginPaint()でデバイスコンテキストのハンドルを取得できます。それ以外ではGetDC()を用います。 BeginPaint 関数 | MSDN windows - Difference between GetDC() and BeginPaint() - Stack Overflow | MSDN

HDC BeginPaint(
  HWND hwnd,             //  ウィンドウのハンドル
  LPPAINTSTRUCT lpPaint  //  描画情報を持つ構造体へのポインタ
  );
BeginPaint 関数 | MSDN
BOOL EndPaint(
  HWND hWnd,                  // ウィンドウのハンドル
  CONST PAINTSTRUCT *lpPaint  // 描画データ
  );
EndPaint 関数 | MSDN
HDC GetDC(
  HWND hWnd   // ウィンドウのハンドル
  );
GetDC 関数 | MSDN GetDC function (Windows) | MSDN (英語)

hWndにNULLを指定すると、スクリーン全体のデバイスコンテキストが返されます。

int ReleaseDC(
  HWND hWnd,  // ウィンドウのハンドル
  HDC hDC     // デバイスコンテキストのハンドル
  );
ReleaseDC 関数 | MSDN

メモリデバイスコンテキストの作成と使用

HDC CreateCompatibleDC(
  HDC hdc   // デバイスコンテキストのハンドル
  );
CreateCompatibleDC 関数 | MSDN

指定のデバイスコンテキストと互換性のあるデバイスコンテキストを作成できるため、これをメモリデバイスコンテキストとして利用できます。

HBITMAP CreateCompatibleBitmap(
  HDC hdc,        // デバイスコンテキストのハンドル
  int nWidth,     // ピクセル単位のビットマップの幅
  int nHeight     // ピクセル単位のビットマップの高さ
  );
CreateCompatibleBitmap 関数 | MSDN

指定のデバイスコンテキストと互換性のあるビットマップを作成できるため、このビットマップを描画領域として利用できます

BOOL PatBlt(
  HDC hdc,      // デバイスコンテキストのハンドル
  int nXLeft,   // 長方形の左上隅の x 座標
  int nYLeft,   // 長方形の左上隅の y 座標
  int nWidth,   // 長方形の幅
  int nHeight,  // 長方形の高さ
  DWORD dwRop   // ラスタオペレーションコード
  );
PatBlt 関数 | MSDN

指定のブラシで指定サイズの長方形を描画できるため、背景の消去に利用できます。

BOOL BitBlt(
  HDC hdcDest, // コピー先デバイスコンテキストのハンドル
  int nXDest,  // コピー先長方形の左上隅の x 座標
  int nYDest,  // コピー先長方形の左上隅の y 座標
  int nWidth,  // コピー先長方形の幅
  int nHeight, // コピー先長方形の高さ
  HDC hdcSrc,  // コピー元デバイスコンテキストのハンドル
  int nXSrc,   // コピー元長方形の左上隅の x 座標
  int nYSrc,   // コピー元長方形の左上隅の y 座標
  DWORD dwRop  // ラスタオペレーションコード
  );
BitBlt 関数 | MSDN

デバイスコンテキスト間でコピーできるため、メモリデバイスコンテキストからのコピーに利用できます。

サンプルコード

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;

    static HDC memoryHDC;
    static HBITMAP memoryHBMP;

    int screenWidth = GetSystemMetrics(SM_CXSCREEN);
    int screenHeight = GetSystemMetrics(SM_CYSCREEN);

    switch (message)
    {
    case WM_CREATE:
        hdc = GetDC(hWnd);

        // メモリデバイスコンテキストを作成する
        memoryHDC = CreateCompatibleDC(hdc);

        memoryHBMP = CreateCompatibleBitmap(hdc, screenWidth, screenHeight);
        SelectObject(memoryHDC, memoryHBMP);

        SetTimer(hWnd, 1, 10, (TIMERPROC)NULL);
        break;

    case WM_TIMER:
        // 背景を消去する
        SelectObject(memoryHDC, GetStockObject(WHITE_BRUSH));
        PatBlt(memoryHDC, 0, 0, screenWidth, screenHeight, PATCOPY);

        // メモリデバイスコンテキストに対して描画する
        SetPixel(memoryHDC, 0, 0, RGB(0, 0, 0));

        InvalidateRect(hWnd, NULL, FALSE);
        break;

    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);

        // メモリデバイスコンテキストから、実際のデバイスコンテキストへコピーする
        BitBlt(hdc, 0, 0, screenWidth, screenHeight, memoryHDC, 0, 0, SRCCOPY);
        EndPaint(hWnd, &ps);
        break;
    }
}

参考

参考書

表示の更新

BOOL InvalidateRect(
  HWND hWnd,           // ウィンドウのハンドル
  CONST RECT *lpRect,  // 長方形の座標
  BOOL bErase          // 消去するかどうかの状態
  );
InvalidateRect 関数 | MSDN

bEraseをTRUEとすると、BeginPaint()呼び出し時に自動で背景が消去されます。そのとき描画の完了までに時間がかかると、描画されていない時間が発生し、結果として表示がちらつく原因となります。

文字列の表示

BOOL TextOut(
  HDC hdc,           // デバイスコンテキストのハンドル
  int nXStart,       // 開始位置(基準点)の x 座標
  int nYStart,       // 開始位置(基準点)の y 座標
  LPCTSTR lpString,  // 文字列
  int cbString       // 文字数
  );
TextOut 関数 | MSDN

フォント

定義済みのオブジェクトを用いるならば、ストックオブジェクトを呼び出すことで簡単にフォントを変更できます。

SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));

色の指定

typedef DWORD COLORREF;
typedef DWORD* LPCOLORREF;
COLORREF (Windows) | MSDN

色は0x00bbggrrの形式で指定します。これは最上位をつねに00として、続けて青、緑、赤の順に16進で指定します。これを生成するためのマクロもあり、それは次の形式です。

COLORREF RGB(
  BYTE byRed,
  BYTE byGreen,
  BYTE byBlue
  );
RGB macro (Windows) | MSDN

描画方法の指定

定義済みのオブジェクトが用意されており、基本的なものならばそれでまかなえます。

HGDIOBJ GetStockObject(
  int fnObject   // ストックオブジェクトのタイプ
  );
GetStockObject 関数 | MSDN
  • BRUSH
    • WHITE_BRUSH
    • LTGRAY_BRUSH
    • GRAY_BRUSH
    • DKGRAY_BRUSH
    • BLACK_BRUSH
    • NULL_BRUSH
    • HOLLOW_BRUSH
  • PEN
    • WHITE_PEN
    • BLACK_PEN
    • NULL_PEN
  • FONT
    • OEM_FIXED_FONT
    • ANSI_FIXED_FONT
    • ANSI_VAR_FONT
    • SYSTEM_FIXED_FONT
    • SYSTEM_FONT
    • DEVICE_DEFAULT_FONT
  • PALETTE
    • DEFAULT_PALETTE
HGDIOBJ SelectObject(
  HDC hdc,          // デバイスコンテキストのハンドル
  HGDIOBJ hgdiobj   // オブジェクトのハンドル
  );
SelectObject 関数 | MSDN
HGDIOBJ oldObj = SelectObject(hdc, GetStockObject(BLACK_PEN));

// ペンを使用した描画処理
// ...

// 元のオブジェクトへ戻す
SelectObject(hdc, oldObj);

ペン

目的のペンが定義済みオブジェクトにあるならば、それを用います。

SelectObject(hdc, GetStockObject(BLACK_PEN));

幅や色などを変更したい場合は、CreatePen()でペンを作成します。

HPEN CreatePen(
  int fnPenStyle,    // ペンのスタイル
  int nWidth,        // ペンの幅
  COLORREF crColor   // ペンの色
  );
CreatePen 関数 | MSDN
HPEN pen;
pen = CreatePen(PS_SOLID, 1, RGB(0,0xff,0));
SelectObject(hdc, pen);
// 描画処理
DeleteObject(pen);

pen = CreatePen(PS_SOLID, 1, RGB(0,0,0xff));
SelectObject(hdc, pen);
// 描画処理
DeleteObject(pen);

ブラシ

図形の描画

COLORREF SetPixel(
  HDC hdc,           // デバイスコンテキストのハンドル
  int X,             // ピクセルの x 座標
  int Y,             // ピクセルの y 座標
  COLORREF crColor   // ピクセルの色
 );
SetPixel 関数 | MSDN
SetPixel(hdc, 10, 20, RGB(0, 0, 0));

直線

BOOL MoveToEx(
  HDC hdc,          // デバイスコンテキストのハンドル
  int X,            // 新しい現在の位置の x 座標
  int Y,            // 新しい現在の位置の y 座標
  LPPOINT lpPoint   // それまでの現在の位置
  );
MoveToEx 関数 | MSDN
BOOL LineTo(
  HDC hdc,    // デバイスコンテキストのハンドル
  int nXEnd,  // 終点の x 座標
  int nYEnd   // 終点の y 座標
  );
LineTo 関数 | MSDN

まずMoveToEx()で現在の位置を指定し、LineTo()の位置まで直線を描画します。LineTo()は現在の位置をその終点で更新するため、続けて呼び出すことで連続した直線となります。

MoveToEx(hdc, 10, 20, NULL);
LineTo(hdc, 100, 20);
LineTo(hdc, 50, 50);

直線の描画はPolyline()でも行えます。こちらは現在の位置を用いず、すべての頂点を引数で与えます。LineTo()の例と同じ処理は、Polyline()では次のようになります。

POINT p[3];
p[0].x = 10;
p[0].y = 20;
p[1].x = 100;
p[1].y = 20;
p[2].x = 50;
p[2].y = 50;
Polyline(hdc, p, 3);
BOOL Polyline(
  HDC hdc,            // デバイスコンテキストのハンドル
  CONST POINT *lppt,  // 端点からなる配列
  int cPoints         // 配列内の点の数
  );
Polyline 関数 | MSDN

楕円

BOOL Ellipse(
  HDC hdc,        // デバイスコンテキストのハンドル
  int nLeftRect,  // 長方形の左上隅の x 座標
  int nTopRect,   // 長方形の左上隅の y 座標
  int nRightRect, // 長方形の右下隅の x 座標
  int nBottomRect // 長方形の右下隅の y 座標
  );
Ellipse 関数 | MSDN

円を描く関数はなく、この関数でx方向とy方向の大きさを同一とすることで、円とします。

参考

ウィンドウのサイズの取得

BOOL GetWindowRect(
  HWND hWnd,      // ウィンドウのハンドル
  LPRECT lpRect   // ウィンドウの座標値
  );
GetWindowRect 関数 | MSDN

クライアントエリア

BOOL GetClientRect(
  HWND hWnd,      // ウィンドウのハンドル
  LPRECT lpRect   // クライアント座標
  );
GetClientRect 関数 | MSDN

lpRectへはRECT構造体のポインタを渡します。

RECT clientRect;
GetClientRect(hWnd, &clientRect);

このRECT構造体は次のように定義されています。

typedef struct _RECT {
  LONG left;
  LONG top;
  LONG right;
  LONG bottom;
} RECT, *PRECT;
RECT structure (Windows) | MSDN
typedef struct tagRECT
{
  LONG left;
  LONG top;
  LONG right;
  LONG bottom;
} RECT, *PRECT, NEAR *NPRECT, FAR *LPRECT;
windef.h

参考

参考書

Microsoft Learnから検索