GDI



DC

デバイスコンテキスト

描画したいウィンドウからGetDCすることでDCを取得する。
DCには描画対象ウィンドウに対して
どんな線のサイズで描画するか
どんな色で描画するか
といった情報を持つ。

我々はDCの持つ描画のための情報を適時切り替えることで対象ウィンドウに描画することができる。また、ウィンドウに限らずプリンタとかにも出力できる。


GetDCにウィンドウを渡すと基本的にはディスプレイのコンテキストが返ってくるが、複数ディスプレイというか、ウィンドウがディスプレイ跨いでたら何が返ってくるの?


GetDC 関数にウィンドウハンドルを渡すと、そのウィンドウのクライアント領域に対応するデバイスコンテキストが返ってきます。このデバイスコンテキストは、ウィンドウが表示されているディスプレイに関わらず、そのウィンドウに描画するためのコンテキストです。

複数ディスプレイ環境での挙動

複数ディスプレイ環境において、ウィンドウが複数のディスプレイにまたがっている場合、GetDC で取得したデバイスコンテキストはどのように動作するかについて説明します。

  1. ウィンドウが単一のディスプレイに存在する場合

    • GetDC で取得されるデバイスコンテキストは、そのディスプレイに対して描画操作を行います。

  2. ウィンドウが複数のディスプレイにまたがっている場合

    • GetDC で取得されるデバイスコンテキストは、ウィンドウ全体に対する描画コンテキストです。描画操作は、ウィンドウが存在するすべてのディスプレイに適用されます。

    • ウィンドウがどのディスプレイにまたがっているかを気にすることなく、ウィンドウ内のクライアント領域に対して描画することができます。


OSとしては複数ディスプレイの場合仮想的な領域を確保して、実際のデータ描画の分配は勝手にやってくれるから、コンテキスト的には仮想的な領域に対応する情報が返ってくる感じ?


その通りです。複数ディスプレイ環境では、OSは仮想的なデスクトップ領域を確保し、その領域内でウィンドウの位置とサイズを管理します。デバイスコンテキスト(DC)を取得する際には、仮想デスクトップ領域に基づいて描画操作を行うことになります。実際の描画データの分配はOSが担当し、適切なディスプレイに描画内容を分配します。


デバイスコンテキストって一般に分類されてるの?
厳密なもの?大雑把なもの?新しいメカつくられること考えたら完全な分類は無理だよね?


デバイスコンテキスト(DC)は、一般的にはいくつかの主要なカテゴリに分類されますが、その分類は大まかなものであり、すべての特定の用途を網羅するわけではありません。新しいデバイスや描画技術が登場するにつれて、その分類も拡張される可能性があります。

デバイスコンテキストの分類

デバイスコンテキストは、主に以下のように分類されます。

  1. ディスプレイデバイスコンテキスト

    • ウィンドウデバイスコンテキスト

      • ウィンドウのクライアント領域に対する描画に使用されます。

      • 例: `GetDC`, `GetWindowDC`

    • デスクトップデバイスコンテキスト

      • デスクトップ全体に対する描画に使用されます。

      • 例: `GetDC(NULL)`

  2. メモリデバイスコンテキスト

    • オフスクリーン描画やビットマップ操作に使用されます。

    • 例: `CreateCompatibleDC`

  3. プリンターデバイスコンテキスト

    • 印刷操作に使用されます。

    • 例: `CreateDC`, `StartDoc`

  4. その他のデバイスコンテキスト

    • 他の特定のデバイスに対する描画に使用されます。例えば、特定のグラフィックスカードやカスタムデバイス向けのデバイスコンテキスト。

    • 例: 特定のハードウェアやデバイスに依存するカスタムデバイスコンテキスト

大まかな分類の理由

デバイスコンテキストの分類は大まかなものである理由はいくつかあります。

  1. 技術の進化

    • 新しい描画技術やデバイスが登場するたびに、新しい種類のデバイスコンテキストが必要になることがあります。

    • 例: 仮想現実(VR)デバイスや拡張現実(AR)デバイス向けのデバイスコンテキスト。

  2. 用途の多様性

    • 同じデバイスコンテキストでも、異なる用途に応じて異なる使い方がされることがあります。

    • 例: メモリデバイスコンテキストは、ビットマップ操作やオフスクリーン描画など、さまざまな用途に使用されます。

  3. 互換性と拡張性

    • Windows APIは後方互換性を重視しており、新しいデバイスコンテキストの種類を追加する際には既存のAPIとの互換性を維持する必要があります。

    • そのため、分類は大まかに保たれ、新しいデバイスや技術に対しても柔軟に対応できるようになっています。

新しいデバイスや技術の登場に対する対応

新しいデバイスや技術が登場すると、それに対応するために新しいAPIやデバイスコンテキストが追加されることがあります。しかし、基本的な概念やカテゴリは大きく変わらず、新しい種類のデバイスコンテキストが既存のフレームワークに統合されることが一般的です。

例えば、Direct2DやDirect3Dなどの高レベルのグラフィックスAPIは、従来のGDI(Graphics Device Interface)に代わるものとして登場しましたが、これらもデバイスコンテキストに類似した概念を使用しています。

まとめ

デバイスコンテキストの分類は大まかなものであり、新しいデバイスや技術の登場により拡張される可能性があります。しかし、基本的な概念やカテゴリは大きく変わらず、柔軟に対応できるような設計がされています。このため、厳密な分類よりも実用的な分類が重視されており、新しい技術にも対応しやすくなっています。

GetDC

GetDC 関数は、指定されたウィンドウまたはデスクトップ全体のデバイスコンテキスト(DC)のハンドルを取得します。

パラメータ:
hWnd
: デバイスコンテキストを取得するウィンドウのハンドル。NULL を指定すると、デスクトップウィンドウのデバイスコンテキストが返されます。

戻り値:
成功した場合、指定されたウィンドウのデバイスコンテキストのハンドルを返します。
失敗した場合、NULL を返します。

        hdc = GetDC(hwnd);  
        ReleaseDC(hwnd, hdc); 
HDC hdc = GetDC(hwnd);
if (hdc != NULL) {
    // 描画操作を行う
    ReleaseDC(hwnd, hdc);
}

仮にディスプレイAが解像度1920x1080で、ディスプレイBが解像度1280x1024の場合、仮想デスクトップ領域は横幅3200ピクセル(1920 + 1280)になります。ウィンドウがディスプレイAとディスプレイBにまたがっている場合でも、以下のコードはそのウィンドウのクライアント領域全体に対して描画操作を行います。

HDC hdc = GetDC(hwnd);
if (hdc != NULL) {
    // ウィンドウのクライアント領域全体に対する描画操作
    RECT rect;
    SetRect(&rect, 10, 10, 3000, 100);  // 仮想デスクトップ領域に基づいた座標
    DrawText(hdc, TEXT("Hello, World!"), -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

    // デバイスコンテキストを解放
    ReleaseDC(hwnd, hdc);
}


GetWindowDC

概要: GetWindowDC 関数は、指定されたウィンドウの全体(クライアント領域および非クライアント領域)のデバイスコンテキストのハンドルを取得します。

シンタックス:

HDC GetWindowDC(
  HWND hWnd
);

パラメータ:
hWnd
: デバイスコンテキストを取得するウィンドウのハンドル。NULL を指定すると、デスクトップウィンドウのデバイスコンテキストが返されます。

戻り値:
成功した場合、指定されたウィンドウのデバイスコンテキストのハンドルを返します。失敗した場合、NULL を返します。

使用例:

HDC hdc = GetWindowDC(hwnd);
if (hdc != NULL) {
    // 描画操作を行う
    ReleaseDC(hwnd, hdc);
}

CreateDC

概要: CreateDC 関数は、指定されたデバイスドライバ名およびデバイス名に基づいてデバイスコンテキストを作成します。

シンタックス:

HDC CreateDC(
  LPCTSTR lpszDriver,
  LPCTSTR lpszDevice,
  LPCTSTR lpszOutput,
  CONST DEVMODE *lpInitData
);

パラメータ:

  • lpszDriver: デバイスドライバ名。通常は "WINSPOOL"(プリンタ用)や "DISPLAY"(ディスプレイ用)を指定します。

  • lpszDevice: デバイス名。プリンタ名やディスプレイ名を指定します。

  • lpszOutput: 出力ポート。通常は NULL を指定します。

  • lpInitData: 初期化データへのポインタ。通常は NULL を指定します。

戻り値:
成功した場合、デバイスコンテキストのハンドルを返します。
失敗した場合、NULL を返します。

使用例:

HDC hdcPrinter = CreateDC(TEXT("WINSPOOL"), TEXT("Printer Name"), NULL, NULL);
if (hdcPrinter != NULL) {
    // プリンタに対して描画操作を行う
    DeleteDC(hdcPrinter);
}


CreateCompatibleDC

概要: CreateCompatibleDC 関数は、指定されたデバイスコンテキストと互換性のあるメモリデバイスコンテキストを作成します。

シンタックス:

HDC CreateCompatibleDC(
  HDC hdc
);

パラメータ:

hdc:
互換性のあるデバイスコンテキストを指定します。NULL を指定すると、スクリーンデバイスコンテキストと互換性のあるデバイスコンテキストが作成されます。

戻り値:
成功した場合、メモリデバイスコンテキストのハンドルを返します。
失敗した場合、NULL を返します。

使用例:

HDC hdcMem = CreateCompatibleDC(NULL);
if (hdcMem != NULL) {
    // メモリDCに対して描画操作を行う
    DeleteDC(hdcMem);
}

StartDoc

概要: StartDoc 関数は、指定されたデバイスコンテキストで新しいドキュメントを開始することを通知します。主に印刷操作で使用されます。

シンタックス:

int StartDoc(
  HDC hdc,
  const DOCINFO *lpdi
);

パラメータ:
hdc
: プリンターデバイスコンテキストのハンドル。
lpdi: ドキュメント情報を含む DOCINFO 構造体へのポインタ。

戻り値:
成功した場合、0より大きい値を返します。
失敗した場合、0以下の値を返します。

使用例:

DOCINFO di = { sizeof(DOCINFO), TEXT("My Document") };
HDC hdcPrinter = CreateDC(TEXT("WINSPOOL"), TEXT("Printer Name"), NULL, NULL);
if (hdcPrinter != NULL) {
    if (StartDoc(hdcPrinter, &di) > 0) {
        // ドキュメントのページを開始
        StartPage(hdcPrinter);
        // プリンタに対して描画操作を行う
        EndPage(hdcPrinter);
        EndDoc(hdcPrinter);
    }
    DeleteDC(hdcPrinter);
}

これらの関数は、デバイスコンテキストを取得し、それに対して描画操作を行うために使用されます。GetDCGetWindowDC は主にディスプレイ(画面)に対する描画に使用され、CreateCompatibleDC はメモリデバイスコンテキストを作成し、オフスクリーン描画やビットマップ操作に使用されます。CreateDC はプリンタなどの特定のデバイスに対するデバイスコンテキストを作成し、StartDoc は印刷操作を開始するために使用されます。

BeginPaint

WndProcで
WM_PAINTを処理して
BeginPaintするのが基本。

LRESULT APIENTRY WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    PAINTSTRUCT ps; 
    HDC hdc; 
 
    switch (message) 
    { 
        case WM_PAINT: 
            hdc = BeginPaint(hwnd, &ps); 
            TextOut(hdc, 0, 0, "Hello, Windows!", 15); 
            EndPaint(hwnd, &ps); 
            return 0L; 

        // Process other messages.   
    } 
} 
 


GetCurrentObject

HGDIOBJ GetCurrentObject(
  [in] HDC  hdc,
  [in] UINT type
);

DCで現在選択中(使用中)のオブジェクトのハンドルを取得する。
オブジェクトとは

OBJ_BITMAP
OBJ_BRUSH
OBJ_COLORSPACE
OBJ_FONT
OBJ_PAL
OBJ_PEN

GetObject

ハンドルからオブジェクトの詳細情報を取得する。
例えばGetCurrentObjectでハンドルを取得し、そのハンドルをGetObjectに放り込むことによってオブジェクトの詳細を得る。

    HDC hdc;                     // display DC handle  
    HBRUSH hbrushNew, hbrushOld; // brush handles  
    HBRUSH hbrush;               // brush handle  
    LOGBRUSH lb;                 // logical-brush structure  
 
    // Retrieve a handle identifying the current brush.  
 
    hbrush = GetCurrentObject(hdc, OBJ_BRUSH); 
 
    // Retrieve a LOGBRUSH structure that contains the  
    // current brush attributes.  
 
    GetObject(hbrush, sizeof(LOGBRUSH), &lb); 
 
    // If the current brush is not a solid-black brush,  
    // replace it with the solid-black stock brush.  
 
    if ((lb.lbStyle != BS_SOLID) 
           || (lb.lbColor != 0x000000)) 
    { 
        hbrushNew = GetStockObject(BLACK_BRUSH); 
        hbrushOld = SelectObject(hdc, hbrushNew); 
    } 
 
    // Perform painting operations with the white brush.  
 
 
    // After completing the last painting operation with the new  
    // brush, the application should select the original brush back  
    // into the device context and delete the new brush.  
    // In this example, hbrushNew contains a handle to a stock object.  
    // It is not necessary (but it is not harmful) to call  
    // DeleteObject on a stock object. If hbrushNew contained a handle  
    // to a brush created by a function such as CreateBrushIndirect,  
    // it would be necessary to call DeleteObject.  
 
    SelectObject(hdc, hbrushOld); 
    DeleteObject(hbrushNew);

また、Bitmapのロードなりセーブなりでもでてくる。



描画


BOOL fDraw = FALSE; 
POINT ptPrevious; 
 
  . 
  . 
  . 
 
case WM_LBUTTONDOWN: 
    fDraw = TRUE; 
    ptPrevious.x = LOWORD(lParam); 
    ptPrevious.y = HIWORD(lParam); 
    return 0L; 
 
case WM_LBUTTONUP: 
    if (fDraw) 
    { 
        hdc = GetDC(hwnd); 
        MoveToEx(hdc, ptPrevious.x, ptPrevious.y, NULL); 
        LineTo(hdc, LOWORD(lParam), HIWORD(lParam)); 
        ReleaseDC(hwnd, hdc); 
    } 
    fDraw = FALSE; 
    return 0L; 
 
case WM_MOUSEMOVE: 
    if (fDraw) 
    { 
        hdc = GetDC(hwnd); 
        MoveToEx(hdc, ptPrevious.x, ptPrevious.y, NULL); 
        LineTo(hdc, ptPrevious.x = LOWORD(lParam), 
          ptPrevious.y = HIWORD(lParam)); 
        ReleaseDC(hwnd, hdc); 
    } 
    return 0L;


void Marker(LONG x, LONG y, HWND hwnd) 
{ 
    HDC hdc; 
 
    hdc = GetDC(hwnd); 
        MoveToEx(hdc, (int) x - 10, (int) y, (LPPOINT) NULL); 
        LineTo(hdc, (int) x + 10, (int) y); 
        MoveToEx(hdc, (int) x, (int) y - 10, (LPPOINT) NULL); 
        LineTo(hdc, (int) x, (int) y + 10); 

    ReleaseDC(hwnd, hdc); 
}


// Line- and arc-drawing variables  
 
static BOOL bCollectPoints; 
static POINT ptMouseDown[32]; 
static int index; 
POINTS ptTmp; 
RECT rc; 
 
    case WM_LBUTTONDOWN:  
        if (bCollectPoints && index < 32)
        { 
            // Create the region from the client area.  
 
            GetClientRect(hwnd, &rc); 
            hrgn = CreateRectRgn(rc.left, rc.top, 
                rc.right, rc.bottom); 
 
            ptTmp = MAKEPOINTS((POINTS FAR *) lParam); 
            ptMouseDown[index].x = (LONG) ptTmp.x; 
            ptMouseDown[index].y = (LONG) ptTmp.y; 
 
            // Test for a hit in the client rectangle.  
 
            if (PtInRegion(hrgn, ptMouseDown[index].x, 
                    ptMouseDown[index].y)) 
            { 
                // If a hit occurs, record the mouse coords.  
 
                Marker(ptMouseDown[index].x, ptMouseDown[index].y, 
                    hwnd); 
                index++; 
            } 
        } 
        break;



MoveToEx


LineTo


この記事が気に入ったらサポートをしてみませんか?