Direct2Dを使いGraphics Device Interface (GDI) の代わりに描画を行う方法

Win32APIの描画処理には、Graphics Device Interface (GDI) という仕組みが使われてますが
10年ぐらい前からDirect2Dという描画方法が現れ今更触ってみました。
Direct・・と聞くとDirectXを連想してしまい、とても難しいコーディングが
必要なのでは・・？と思いきや、とても簡単に実装できました。

:::note info
[主な実装方法]
・ID2D1Factoryを作成
・ID2D1HwndRenderTargetを作成
・WM_PAINT内でID2D1HwndRenderTargetを用いBeginDraw()~EndDraw()
:::

```diff_c
    case WM_PAINT:
    	hdc = BeginPaint(hwnd, &ps);
        ~
        EndPaint(hwnd, &ps);
```
Direct2Dで描画する場合は、Win32APIでのお約束である上記コードも不要です。
これは、試しに消してみたら問題なかったです。
WM_CREATE内でhwndと連携させてるからですかね。

<details><summary>サンプル用の全ソースコード</summary>


```diff_c
#include <Windows.h>
#include <tchar.h>
#include <d2d1.h>
// lib	Direct2D
#pragma comment( lib, "d2d1.lib" )

#define WINDOW_WIDTH  1080
#define WINDOW_HEIGHT 720

LRESULT CALLBACK    WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
#define CLASS_NAME  "CLASS TEST01"
#define PROC_NAME   "test01"


template<class T>
inline void safe_release(T& p) {
	if (p) {
		p->Release();
		p = nullptr;
	}
}

// グローバル変数として定義
//	Direct2D用のファクトリ
ID2D1Factory* g_pD2d1Factory = nullptr;
//	RenderTarget
ID2D1HwndRenderTarget* g_pRenderTarget = nullptr;

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPreInst, LPTSTR lpCmdLine, int nCmdShow)
{
	HWND            hwnd;
	MSG             msg;
	if (!hPreInst) {
		WNDCLASS    my_prog;
		my_prog.style = CS_HREDRAW | CS_VREDRAW;
		my_prog.lpfnWndProc = WndProc;
		my_prog.cbClsExtra = 0;
		my_prog.cbWndExtra = 0;
		my_prog.hInstance = hInstance;
		my_prog.hIcon = NULL;
		my_prog.hCursor = LoadCursor(NULL, IDC_ARROW);
		my_prog.hbrBackground = NULL;
		my_prog.lpszMenuName = NULL;
		my_prog.lpszClassName = _T(CLASS_NAME);

		if (!RegisterClass(&my_prog)) {
			return FALSE;
		}
	}

	RECT rect = {
		static_cast<LONG>(0),
		static_cast<LONG>(0),
		static_cast<LONG>(WINDOW_WIDTH),
		static_cast<LONG>(WINDOW_HEIGHT)
	};
	AdjustWindowRect(
		&rect,                                    // クライアント矩形
		WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION,  // ウィンドウスタイル
		FALSE                                     // メニューフラグ
	);
	hwnd = CreateWindow(_T(CLASS_NAME),
		_T(PROC_NAME),
	//	WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION,
		WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_THICKFRAME | WS_MAXIMIZEBOX,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		rect.right - rect.left,           // ウィンドウの幅
		rect.bottom - rect.top,           // ウィンドウの高さ
		NULL,
		NULL,
		hInstance,
		NULL);

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);

	do {
		if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) {
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	} while (msg.message != WM_QUIT);
	return (int)(msg.wParam);
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) {

	case WM_SIZE: {

		D2D1_SIZE_U oPixelSize = { LOWORD(lParam), HIWORD(lParam) };
		// ターゲットリサイズ
		g_pRenderTarget->Resize(&oPixelSize);
		break;
	}
		
	case WM_DESTROY: {

		// ID2D1HwndRenderTargetの破棄
		safe_release(g_pRenderTarget);
		// ID2D1Factoryの破棄
		safe_release(g_pD2d1Factory);

		PostQuitMessage(0);
		break;
	}
	case WM_CREATE: {
		CREATESTRUCT* tpCreateSt = (CREATESTRUCT*)lParam;
		HRESULT hResult = S_OK;

		//	Direct2D用のファクトリーの作成
		hResult = D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, &g_pD2d1Factory);

		if (SUCCEEDED(hResult)) {

			//	RenderTargetのサイズを指定
			D2D1_SIZE_U oPixelSize = {
								 static_cast<UINT32>(tpCreateSt->cx)
							   , static_cast<UINT32>(tpCreateSt->cy)
			};
			D2D1_RENDER_TARGET_PROPERTIES oRenderTargetProperties = D2D1::RenderTargetProperties();
			D2D1_HWND_RENDER_TARGET_PROPERTIES oHwndRenderTargetProperties = D2D1::HwndRenderTargetProperties(hwnd, oPixelSize);

			//	Direct2DのRenderTargetの作成
			hResult = g_pD2d1Factory->CreateHwndRenderTarget(
				oRenderTargetProperties
				, oHwndRenderTargetProperties
				, &g_pRenderTarget
			);
		}
		break;
	}
	case WM_PAINT: {


		D2D1_SIZE_F oTargetSize = g_pRenderTarget->GetSize();

		// 描画開始(Direct2D)
		g_pRenderTarget->BeginDraw();
		//	初期化マトリクスを行列にセット
		g_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
		// 背景の色を赤でクリア
		D2D1_COLOR_F oBKColor = { 1.0f, 0.0f, 0.0f, 1.0f };
		g_pRenderTarget->Clear(oBKColor);

		//	ブラシの作成
		ID2D1SolidColorBrush* pLineBrush = nullptr;
		ID2D1SolidColorBrush* pFillBrush = nullptr;

		//	青いブラシを作成
		g_pRenderTarget->CreateSolidColorBrush(
			D2D1::ColorF(0.0f        // R
				, 0.0f                                          // G
				, 1.0f                                          // B
				, 1.0f                                          // A
			)
			, &pLineBrush
		);
		//	緑ブラシを作成
		g_pRenderTarget->CreateSolidColorBrush(
			D2D1::ColorF(0.0f        // R
				, 1.0f                                          // G
				, 0.0f                                          // B
				, 1.0f                                          // A
			)
			, &pFillBrush
		);

		// 描画矩形
		D2D1_RECT_F tRectF = D2D1::RectF(
			0.0f
			, 0.0f
			, static_cast<FLOAT>(oTargetSize.width/2)
			, static_cast<FLOAT>(oTargetSize.height/2)
		);

		// 線の幅
		float fStrokeWidth = 4.0f;
		// 四角形を塗りつぶす
		g_pRenderTarget->FillRectangle(&tRectF, pFillBrush);
		// 四角形の線を引く
		g_pRenderTarget->DrawRectangle(&tRectF, pLineBrush, fStrokeWidth);
		//	ラインを引く
		g_pRenderTarget->DrawLine(
			D2D1::Point2F(static_cast<FLOAT>(oTargetSize.width / 2), static_cast<FLOAT>(oTargetSize.height / 2)),
			D2D1::Point2F(static_cast<FLOAT>(oTargetSize.width- oTargetSize.width / 4), static_cast<FLOAT>(oTargetSize.height- oTargetSize.height / 4)),
			pLineBrush,
			fStrokeWidth
		);

		// ブラシの破棄
		safe_release(pFillBrush);
		safe_release(pLineBrush);
	
		//	描画終了(Direct2D)
		g_pRenderTarget->EndDraw();

		break;
	}
	default:
		return(DefWindowProc(hwnd, msg, wParam, lParam));
	}
	return (0L);
}
```
</details>

ソースコードをコンパイルすると以下のような見た目が表示されます。
![スクリーンショット 2022-08-17 020308.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784544/a0303a8f-fb59-3bc0-6b41-0e353df49a76.png)


参考サイト：

https://docs.microsoft.com/ja-jp/windows/win32/direct2d/direct2d-portal