potisanのプログラミングメモ

プログラミング素人です。昔の自分を育ててくれたネット情報に少しでも貢献できるよう、情報を貯めていこうと思っています。Windows環境のC++やC#がメインです。

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);
}