cairoをWindowsで直接使う #4

Win32APIを直接利用してcairoで描画する領域を用意してみる。
cairoにはWin32サーフェスが用意されており、
描画対象のデバイスコンテクストのハンドルを渡せばcairoサーフェスとして機能する。
Win32サーフェスを使うときはcairo-win32.hもインクルードする。

#include <windows.h>
#include <tchar.h>
#include <cairo.h>
#include <cairo-win32.h>

#define UNUSED_ARGUMENT(x) (void)(x)
#define WIDTH 860
#define HEIGHT 520

static TCHAR window_class_name[] = _T("cairoHelloTest");
static TCHAR window_title[] = _T("Hello");

LRESULT CALLBACK window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void draw_hello(cairo_surface_t *surf);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc;
    HWND hwnd;
    MSG msg;
    UNUSED_ARGUMENT(hPrevInstance);
    UNUSED_ARGUMENT(lpCmdLine);

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = window_proc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = window_class_name;
    wc.hIconSm = LoadIcon(hInstance, IDI_APPLICATION);
    if (! RegisterClassEx(&wc)) {
        MessageBox(NULL, _T("failed to RegisterClassEx"), _T("error"), MB_OK);
        return 0;
    }
    hwnd = CreateWindow(window_class_name, window_title, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, WIDTH, HEIGHT, NULL, NULL, hInstance, NULL);
    if (! hwnd) {
        MessageBox(NULL, _T("failed to CreateWindow"), _T("error"), MB_OK);
        return 0;
    }
    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}

LRESULT CALLBACK window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;
    HDC hdc;
    static HDC hmdc;
    static HBITMAP hbmp;
    cairo_surface_t *surf;

    switch (uMsg) {
    case WM_CREATE:
        hdc = GetDC(hwnd);
        hmdc = CreateCompatibleDC(hdc);
        hbmp = CreateCompatibleBitmap(hdc, WIDTH, HEIGHT);
        SelectObject(hmdc, hbmp);
        SelectObject(hmdc, GetStockObject(WHITE_BRUSH));
        PatBlt(hmdc, 0, 0, WIDTH, HEIGHT, PATCOPY);
        surf = cairo_win32_surface_create(hmdc);
        draw_hello(surf);
        cairo_surface_destroy(surf);
        ReleaseDC(hwnd, hdc);
        break;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        BitBlt(hdc, 0, 0, WIDTH, HEIGHT, hmdc, 0, 0, SRCCOPY);
        EndPaint(hwnd, &ps);
        break;
    case WM_DESTROY:
        DeleteObject(hbmp);
        DeleteDC(hmdc);
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
        break;
    }
    return FALSE;
}

void draw_hello(cairo_surface_t *surf)
{
...snip

WM_CREATE時にオフスクリーンデバイスを作成し、
オフスクリーン用のイメージに関連付け、
そのデバイスコンテクストからcairoサーフェスを作成する。
これにdraw_hello()で描画し、
WM_PAINT時にオフスクリーンから作成したイメージをコピーしてくる。
相変わらずcairoによる描画部分は今までと共通で変更は不要である。