DirectWriteを使いGraphics Device Interface (GDI)代わりにテキストを描画する方法


[前回](https://qiita.com/crysteltools/items/e3bd261097b809d8f19c)に引き続きDirect2Dの機能を使い、今度はテキストを描画します。
テキストはDirectWriteという方法で描画でき、GDIよりも高速かつ綺麗な表示が可能だそうです。
本当に綺麗かどうかは、後日検証をしたいと思います。
高速かも怪しいです。

DirectWriteも難しくありません。

凡その概要を書くと・・

:::note info
・ID2D1Factoryの作成
・ID2D1HwndRenderTargetの作成
・IDWriteFactoryの作成
・IDWriteTextFormatでフォントを作成
・DrawText(～）で描画
:::

です。
※但し、テキストの描画する違う方法として、文字の外枠の線をパス(曲線)に変換し、
　そのパスを描画する方法などもあります。（これは後日）

<details><summary>ソースコード</summary>

```diff_c
#include <Windows.h>
#include <tchar.h>
#include <iostream>
//	Direct2DのHeader
#include <d2d1.h>
//	DirectWriteのHeader
#include <dwrite.h>
// lib	Direct2D
#pragma comment( lib, "d2d1.lib" )
//	lib DirectWrite
#pragma comment( lib, "dwrite.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;
//	DirectWrite用のファクトリー
IDWriteFactory* g_pDWFactory = 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_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);
		//	IDWriteFactoryの破棄
		safe_release(g_pDWFactory);
		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
			);
			//	DirectWriteのFactryを作成
			if (SUCCEEDED(hResult)) {
				hResult = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&g_pDWFactory));
			}
		}
		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);

		//	テキストフォーマットの作成
		IDWriteTextFormat* pTextFormat = nullptr;
		{
			g_pDWFactory->CreateTextFormat(
				L"Meiryo"
				, NULL
				, DWRITE_FONT_WEIGHT_NORMAL
				, DWRITE_FONT_STYLE_NORMAL//DWRITE_FONT_STYLE_OBLIQUE
				, DWRITE_FONT_STRETCH_NORMAL
				, 64
				, L""
				, &pTextFormat
			);
		}


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

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


		std::wstring strText = L"コワーキングスペースでの開発";
		// テキストの描画
		g_pRenderTarget->DrawText(
			strText.c_str()   // 文字列
			, strText.size()    // 文字数
			, pTextFormat
			, &D2D1::RectF(0, 0, oTargetSize.width, oTargetSize.height)
			, pFontBrush
			, D2D1_DRAW_TEXT_OPTIONS_NONE
		);


		// ブラシの破棄
		safe_release(pFontBrush);
		//	フォントの破棄
		safe_release(pTextFormat);
		//	描画終了(Direct2D)
		g_pRenderTarget->EndDraw();

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

[ソースコードをコンパイルできると以下のような画面になります]
![スクリーンショット 2022-08-18 003600.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784544/b13ba53a-5f12-4694-7235-2f24a68b7d72.png)