LoginSignup
0
0

More than 1 year has passed since last update.

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

Posted at

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

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

    case WM_PAINT:
    	hdc = BeginPaint(hwnd, &ps);
        ~
        EndPaint(hwnd, &ps);

Direct2Dで描画する場合は、Win32APIでのお約束である上記コードも不要です。
これは、試しに消してみたら問題なかったです。
WM_CREATE内でhwndと連携させてるからですかね。

サンプル用の全ソースコード
#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);
}

ソースコードをコンパイルすると以下のような見た目が表示されます。
スクリーンショット 2022-08-17 020308.png

参考サイト:

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0