potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C++20 Win32 APIでウィンドウにDIBセクションを描画する

Win32 APIでウィンドウを表示して、CreateDIBSection関数で作成したビットマップを描画するサンプルコードです。

CreateDIBSection関数で作成したビットマップはppvBits引数に与えた変数の内容をビットマップデータとして直接操作できます。ここでは赤と黒の横縞を描画しています。

std::spanを使用するためC++20以降対応です。

#include <memory>
#include <span>

#define STRICT
#define NOMINMAX
#include <Windows.h>

static const auto MainWindowClassName = L"MainWindow";
static const auto MainWindowTitle = L"MainWindow";
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

using unique_bitmap_handle = std::unique_ptr<std::remove_pointer_t<HBITMAP>, decltype(&DeleteObject)>;
using unique_dc_handle = std::unique_ptr<std::remove_pointer_t<HDC>, decltype(&DeleteDC)>;
unique_bitmap_handle make_unique_bitmap_handle(HBITMAP hbmp) noexcept;
unique_dc_handle make_unique_dc_handle(HDC hdc) noexcept;

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR, int iCmdShow)
{
    // メインウィンドウのウィンドウクラス登録
    WNDCLASSEXW wcex{
        sizeof(WNDCLASSEXW),
        CS_HREDRAW | CS_VREDRAW,
        WndProc,
        0,
        0,
        hInstance,
        nullptr,
        LoadCursor(nullptr, IDC_ARROW),
        reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1),
        nullptr,
        MainWindowClassName };
    if (!RegisterClassExW(&wcex))
    {
        return GetLastError();
    }

    // メインウィンドウの作成・表示
    auto hwnd = CreateWindowExW(
        WS_EX_OVERLAPPEDWINDOW,
        MainWindowClassName,
        MainWindowTitle,
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        nullptr, nullptr, hInstance, nullptr);
    if (hwnd == nullptr)
    {
        return GetLastError();
    }
    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);

    // メッセージループ
    MSG msg;
    int ret;
    while (ret = GetMessageW(&msg, nullptr, 0, 0))
    {
        if (ret == -1)
        {
            break;
        }
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }

    return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            auto hdc = BeginPaint(hwnd, &ps);

            const int width = 100;
            const int height = 100;
            const int width_bytes = width + (width % 4);
            BITMAPINFO bmi{ sizeof(BITMAPINFO), width, -height, 1, 32, BI_RGB };
            LPVOID pbits = nullptr;
            auto hbmp = make_unique_bitmap_handle(CreateDIBSection(
                nullptr, &bmi, DIB_RGB_COLORS, &pbits, nullptr, 0));
            auto pixels = std::span(
                reinterpret_cast<LPBYTE>(pbits),
                reinterpret_cast<LPBYTE>(pbits) + width_bytes * height * 4);
            // 赤と黒の横縞
            // 赤を指定しない部分は自動的に黒
            for (int h = 0; h < height; h += 2)
            {
                const int j = h * width_bytes;
                for (int w = 0; w < width; w++)
                {
                    int i = (j + w) * 4;
                    pixels[i + 2] = 0xff; // R
                    pixels[i + 1] = 0x00; // G
                    pixels[i + 0] = 0x00; // B
                }
            }

            auto hdc2 = make_unique_dc_handle(CreateCompatibleDC(hdc));
            SelectObject(hdc2.get(), hbmp.get());
            BitBlt(hdc, 0, 0, 100, 100, hdc2.get(), 0, 0, SRCCOPY);

            EndPaint(hwnd, &ps);
        }
        return TRUE;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}

unique_bitmap_handle make_unique_bitmap_handle(HBITMAP hbmp) noexcept
{
    return unique_bitmap_handle(hbmp, &DeleteObject);
}

unique_dc_handle make_unique_dc_handle(HDC hdc) noexcept
{
    return unique_dc_handle(hdc, &DeleteDC);
}