2D の描画ならこれでいいんじゃないでしょうか?
ちなみに DirectX12 を使って 2D 描画するより、Direct2D による 2D 描画の方が速いそうです。
もちろん GDI より速いそうです。
やや初期化が面倒なので、GDI で描いてみて遅かったら使うぐらいの感じでよいと思います。
サンプル
Windows Application のコードを全て貼り付けておきます。
#include <windows.h> #include <d2d1.h> #include <dwrite.h> #include "resource.h" #pragma comment(lib, "d2d1") #pragma comment(lib, "dwrite") #define MAX_LOADSTRING 100 // グローバル変数 HINSTANCE hInst; WCHAR szTitle[MAX_LOADSTRING]; WCHAR szWindowClass[MAX_LOADSTRING]; // Direct2D/DirectWrite リソース ID2D1Factory* pD2DFactory = nullptr; ID2D1HwndRenderTarget* pRenderTarget = nullptr; ID2D1SolidColorBrush* pBrush = nullptr; IDWriteFactory* pDWriteFactory = nullptr; IDWriteTextFormat* pTextFormat = nullptr; // 関数プロトタイプ ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); HRESULT InitDirect2D(HWND hWnd) { HRESULT hr; hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory); if (FAILED(hr)) return hr; RECT rc; GetClientRect(hWnd, &rc); D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top); D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(); D2D1_HWND_RENDER_TARGET_PROPERTIES hwndProps = D2D1::HwndRenderTargetProperties(hWnd, size); hr = pD2DFactory->CreateHwndRenderTarget(props, hwndProps, &pRenderTarget); if (FAILED(hr)) return hr; hr = pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &pBrush); if (FAILED(hr)) return hr; hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&pDWriteFactory)); if (FAILED(hr)) return hr; hr = pDWriteFactory->CreateTextFormat( L"Segoe UI", nullptr, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 36.0f, L"ja-jp", &pTextFormat); return hr; } int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadStringW(hInstance, IDC_SAMPLEDIRECT2DWIN32, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); if (!InitInstance(hInstance, nCmdShow)) return FALSE; HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_SAMPLEDIRECT2DWIN32)); MSG msg; while (GetMessage(&msg, nullptr, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return (int)msg.wParam; } ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEXW wcex = {}; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.hInstance = hInstance; wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wcex.lpszClassName = szWindowClass; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SAMPLEDIRECT2DWIN32)); wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_SAMPLEDIRECT2DWIN32); wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassExW(&wcex); } BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { hInst = hInstance; HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); if (!hWnd) return FALSE; if (FAILED(InitDirect2D(hWnd))) { MessageBox(hWnd, L"Direct2D 初期化失敗", L"エラー", MB_OK); return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_COMMAND: { int wmId = LOWORD(wParam); switch (wmId) { case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; } case WM_CREATE: { HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); if (FAILED(hr)) { MessageBox(nullptr, L"COM初期化失敗", L"エラー", MB_OK); break; } break; } case WM_PAINT: { PAINTSTRUCT ps; BeginPaint(hWnd, &ps); if (pRenderTarget) { pRenderTarget->BeginDraw(); // 背景を白でクリア pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White)); // 塗りつぶし(灰色) D2D1_SIZE_F size = pRenderTarget->GetSize(); D2D1_RECT_F fullRect = D2D1::RectF(0, 0, size.width, size.height); pBrush->SetColor(D2D1::ColorF(D2D1::ColorF::Gray)); pRenderTarget->FillRectangle(fullRect, pBrush); // テキスト描画(青) const wchar_t* text = L"Hello, Direct2D!"; D2D1_RECT_F layoutRect = D2D1::RectF(50, 50, 500, 150); pBrush->SetColor(D2D1::ColorF(D2D1::ColorF::Blue)); pRenderTarget->DrawText(text, wcslen(text), pTextFormat, &layoutRect, pBrush); // 線分(黒) pBrush->SetColor(D2D1::ColorF(D2D1::ColorF::Black)); pRenderTarget->DrawLine( D2D1::Point2F(100, 200), D2D1::Point2F(300, 200), pBrush, 2.0f ); // 矩形(緑枠) D2D1_RECT_F rect = D2D1::RectF(100, 220, 300, 270); pBrush->SetColor(D2D1::ColorF(D2D1::ColorF::Green)); pRenderTarget->DrawRectangle(rect, pBrush, 2.0f); // 多角形(紫線) ID2D1PathGeometry* pGeometry = nullptr; ID2D1GeometrySink* pSink = nullptr; if (SUCCEEDED(pD2DFactory->CreatePathGeometry(&pGeometry))) { if (SUCCEEDED(pGeometry->Open(&pSink))) { pSink->BeginFigure(D2D1::Point2F(100, 300), D2D1_FIGURE_BEGIN_FILLED); pSink->AddLine(D2D1::Point2F(150, 350)); pSink->AddLine(D2D1::Point2F(200, 300)); pSink->AddLine(D2D1::Point2F(250, 350)); pSink->AddLine(D2D1::Point2F(300, 300)); pSink->EndFigure(D2D1_FIGURE_END_OPEN); pSink->Close(); pSink->Release(); } pBrush->SetColor(D2D1::ColorF(D2D1::ColorF::Purple)); pRenderTarget->DrawGeometry(pGeometry, pBrush, 2.0f); pGeometry->Release(); } // 多角形塗りつぶし(オレンジ) if (SUCCEEDED(pD2DFactory->CreatePathGeometry(&pGeometry))) { if (SUCCEEDED(pGeometry->Open(&pSink))) { pSink->BeginFigure(D2D1::Point2F(100, 380), D2D1_FIGURE_BEGIN_FILLED); pSink->AddLine(D2D1::Point2F(150, 430)); pSink->AddLine(D2D1::Point2F(200, 380)); pSink->AddLine(D2D1::Point2F(250, 430)); pSink->AddLine(D2D1::Point2F(300, 380)); pSink->EndFigure(D2D1_FIGURE_END_CLOSED); pSink->Close(); pSink->Release(); } pBrush->SetColor(D2D1::ColorF(D2D1::ColorF::Orange)); pRenderTarget->FillGeometry(pGeometry, pBrush); pGeometry->Release(); } // 三次ベジェ曲線(シアン) if (SUCCEEDED(pD2DFactory->CreatePathGeometry(&pGeometry))) { if (SUCCEEDED(pGeometry->Open(&pSink))) { pSink->BeginFigure(D2D1::Point2F(100, 460), D2D1_FIGURE_BEGIN_HOLLOW); D2D1_BEZIER_SEGMENT bezierSegment = { D2D1::Point2F(150, 510), D2D1::Point2F(250, 410), D2D1::Point2F(300, 460) }; pSink->AddBezier(&bezierSegment); pSink->EndFigure(D2D1_FIGURE_END_OPEN); pSink->Close(); pSink->Release(); } pBrush->SetColor(D2D1::ColorF(D2D1::ColorF::Cyan)); pRenderTarget->DrawGeometry(pGeometry, pBrush, 2.0f); pGeometry->Release(); } pRenderTarget->EndDraw(); } EndPaint(hWnd, &ps); break; } case WM_SIZE: { D2D1_SIZE_U oPixelSize = { LOWORD(lParam), HIWORD(lParam) }; // ターゲットリサイズ pRenderTarget->Resize(&oPixelSize); break; } case WM_DESTROY: if (pTextFormat) pTextFormat->Release(); if (pDWriteFactory) pDWriteFactory->Release(); if (pBrush) pBrush->Release(); if (pRenderTarget) pRenderTarget->Release(); if (pD2DFactory) pD2DFactory->Release(); // 最後に COM を終了 CoUninitialize(); PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: return (INT_PTR)TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; } return (INT_PTR)FALSE; }
実行結果は以下です。
