Microsoft Learnよりチュートリアルをやってみました。
GDIと違って、描画するまでに手続きが多いです。
サンプルプログラムを見ていきます。
まず、Direct2D を使用するために、
ファクトリというものを作成します。
D2D1CreateFactoryメソッドで作成できます。
サンプルでのコードです。
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory);
シングルスレッドのファクトリを作成します。
作成されたファクトリオブジェクトはm_pDirect2dFactoryで参照されます。
次に描画をするために、
レンダ―ターゲットを作成します。
ウィンドウをターゲットにしますので、
CreateHwndRenderTargetメソッドを使います。
サンプルでのコードです。
hr = m_pDirect2dFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(), // デフォルトのプロパティ
D2D1::HwndRenderTargetProperties(m_hwnd, size), // ウィンドウ ハンドル、初期サイズ
&m_pRenderTarget // 作成されたレンダ―ターゲットオブジェクトへのポインター
レンダ―ターゲットが作成できましたので、
各描画に関するメソッドを使うことができます。
描画に必要なブラシを作ります。
CreateSolidColorBrushメソッドで作成できます。
サンプルコードです。
hr = m_pRenderTarget->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::LightSlateGray),
&m_pLightSlateGrayBrush //作成したブラシへのポインター
グレーのブラシを作成します。
描画は、WM_PAINTのメッセージ処理で行います。
レンダ―ターゲットに描画を開始するためには、
BeginDrawメソッドを実行します。
これで、実際に描画するメソッドを使えます。
線を描画するには、
DrawLineメソッドを使います。
内部を塗りつぶした四角形を描画するには、FillRectangleメソッドを使います。
また、四角形を描画するには、DrawRectangleメソッドを使います。
描画を終えたら、
EndDrawメソッドを呼び出します。
これによって、表示がされます。
ダブルバッファリングを使用していますので、
実際に線を描画しているところなどは見れません。
ファクトリ、レンダ―ターゲット、ブラシのオブジェクトは、
最後に解放しなければなりません。
Release()メソッドで行います。
〇サンプルプログラム
1.ヘッダーファイル
#pragma comment(lib, "d2d1")
// Windows Header Files:
#include <windows.h>
// C RunTime Header Files:
// よく使用されるヘッダー
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <wchar.h>
#include <math.h>
#include <d2d1.h>
#include <d2d1helper.h>
#include <dwrite.h>
#include <wincodec.h>
/* インターフェースを開放するためのクラス*/
template<class Interface>
inline void SafeRelease(
Interface** ppInterfaceToRelease)
{
if (*ppInterfaceToRelease != NULL)
{
(*ppInterfaceToRelease)->Release(); // COM オブジェクトのインターフェイスの参照カウントをデクリメントします。
(*ppInterfaceToRelease) = NULL;
}
}
// デバッグ用コード
#ifndef Assert // Assertは定義されていない。
#if defined( DEBUG ) || defined( _DEBUG ) // _DEBUGは定義されている
#define Assert(b) do {if (!(b)) {OutputDebugStringA("Assert: " #b "\n");}} while(0)
#else
#define Assert(b)
#endif //DEBUG || _DEBUG
#endif
#ifndef HINST_THISCOMPONENT
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)
#endif
class DemoApp
{
public:
DemoApp(); // コンストラクタ
~DemoApp(); // デストラクタ
//描画リソースをインスタンス化するためのウィンドウクラスと呼び出しメソッドを登録します
HRESULT Initialize(); // デバイスファクトリの作成
// メッセージの処理とディスパッチ
void RunMessageLoop();
private:
// デバイスに依存しないリソースの初期化
HRESULT CreateDeviceIndependentResources();
// デバイスに依存するリソースの初期化
HRESULT CreateDeviceResources();
// リソースを解放する.
void DiscardDeviceResources();
// 描画する
HRESULT OnRender();
//レンダーターゲットをリサイズする
void OnResize(
UINT width,
UINT height
);
// ウィンドウプロシージャ
static LRESULT CALLBACK WndProc(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam
);
private:
HWND m_hwnd; // ウィンドウハンドル
ID2D1Factory* m_pDirect2dFactory; // ファクトリ
ID2D1HwndRenderTarget* m_pRenderTarget; // レンダ―ターゲット
ID2D1SolidColorBrush* m_pLightSlateGrayBrush; // グレーブラシ
ID2D1SolidColorBrush* m_pCornflowerBlueBrush; // ブルーブラシ
};
2.本体プログラム
# include "sample.h"
//HINSTANCE hInstance2;
// コンストラクタ
// メンバーをNULLで初期化
DemoApp::DemoApp() :
m_hwnd(NULL),
m_pDirect2dFactory(NULL),
m_pRenderTarget(NULL),
m_pLightSlateGrayBrush(NULL),
m_pCornflowerBlueBrush(NULL)
{} // 引数の初期化のみなのでコードは不要。
// インターフェースを開放
DemoApp::~DemoApp()
{
SafeRelease(&m_pDirect2dFactory); // ファクトリの解放
SafeRelease(&m_pRenderTarget); // レンダ―ターゲットの解放
SafeRelease(&m_pLightSlateGrayBrush); // ブラシの解放
SafeRelease(&m_pCornflowerBlueBrush); // ブラシの解放
}
// メッセージループ
void DemoApp::RunMessageLoop()
{
MSG msg; // メッセージ構造体
while (GetMessage(&msg, NULL, 0, 0)) // メッセージを受け取る
{
TranslateMessage(&msg); // キー入力などを変換
DispatchMessage(&msg); // ウィンドウ プロシージャにメッセージをディスパッチする。
}
}
// 描画リソースをインスタンス化するためのウィンドウクラスと呼び出しメソッドを登録します
HRESULT DemoApp::Initialize()
{
HRESULT hr;
// Initialize device-indpendent resources, such
// as the Direct2D factory.
// シングルスレッド ファクトリ インスタンスを作成
hr = CreateDeviceIndependentResources();
if (SUCCEEDED(hr))
{
// ウィンドウクラスの登録
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = DemoApp::WndProc; // ウィンドウプロシージャ
wcex.cbClsExtra = 0;
wcex.cbWndExtra = sizeof(LONG_PTR);
wcex.hInstance = HINST_THISCOMPONENT;
wcex.hbrBackground = NULL;
wcex.lpszMenuName = NULL;
wcex.hCursor = LoadCursor(NULL, IDI_APPLICATION);
wcex.lpszClassName = L"D2DDemoApp";
RegisterClassEx(&wcex); // ウィンドウクラスの登録
// In terms of using the correct DPI, to create a window at a specific size
// like this, the procedure is to first create the window hidden. Then we get
// the actual DPI from the HWND (which will be assigned by whichever monitor
// the window is created on). Then we use SetWindowPos to resize it to the
// correct DPI-scaled size, then we use ShowWindow to show it.
// 正しいDPIを使用するという点では、このような特定のサイズのウィンドウを作成するには、
// まず非表示のウィンドウを作成します。
// 次に、HWNDから実際のDPIを取得します(HWNDは、ウィンドウが作成されたモニタによって割り当てられます)。
// 次に、SetWindowPosを使用して正しいDPIスケールのサイズにサイズを変更し、
// ShowWindowを使用して表示します。
// ウィンドウの登録
m_hwnd = CreateWindow(
L"D2DDemoApp",
L"Direct2D demo application",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
NULL,
NULL,
HINST_THISCOMPONENT,
this); // DemoAppクラスのポインタを渡す。
if (m_hwnd)
{
// Because the SetWindowPos function takes its size in pixels, we
// obtain the window's DPI, and use it to scale the window size.
// 指定したウィンドウの 1 インチあたりのドット数 (dpi) の値を返します。
float dpi = GetDpiForWindow(m_hwnd);
SetWindowPos(
m_hwnd,
NULL,
NULL,
NULL,
static_cast<int>(ceil(640.f * dpi / 96.f)), // ウィンドウの幅
static_cast<int>(ceil(480.f * dpi / 96.f)), // ウィンドウの高さ
SWP_NOMOVE); // 現在位置を保持します
ShowWindow(m_hwnd, SW_SHOWNORMAL); // ウィンドウの表示
UpdateWindow(m_hwnd); // ウィンドウのクライアント領域を更新
}
}
return hr;
}
int WINAPI WinMain(
HINSTANCE /* hInstance */,
HINSTANCE /* hPrevInstance */,
LPSTR /* lpCmdLine */,
int /* nCmdShow */
)
{
// Use HeapSetInformation to specify that the process should
// terminate if the heap manager detects an error in any heap used
// by the process.
// The return value is ignored, because we want to continue running in the
// unlikely event that HeapSetInformation fails.
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
if (SUCCEEDED(CoInitialize(NULL))) // COM ライブラリを初期化
{
{
DemoApp app;
if (SUCCEEDED(app.Initialize())) // DemoAppの初期化
{
app.RunMessageLoop(); // メッセージループの開始
}
}
CoUninitialize(); // COM ライブラリを閉じる。
}
return 0;
}
HRESULT DemoApp::CreateDeviceIndependentResources()
{
HRESULT hr = S_OK;
// Create a Direct2D factory.
// Direct2Dファクトリを作成
// シングルスレッド ファクトリ インスタンスを作成
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory);
return hr;
}
// ウィンドウのデバイスに依存するリソース、
// レンダー ターゲット、および 2 つのブラシを作成します。
HRESULT DemoApp::CreateDeviceResources()
{
HRESULT hr = S_OK;
if (!m_pRenderTarget) // レンダ―ターゲットがない場合
{
RECT rc;
GetClientRect(m_hwnd, &rc); // クライアントのサイズを得る。
D2D1_SIZE_U size = D2D1::SizeU(
rc.right - rc.left,
rc.bottom - rc.top
);
// Create a Direct2D render target.
// Direct2Dのレンダ―ターゲットを作成する
hr = m_pDirect2dFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(), //
D2D1::HwndRenderTargetProperties(m_hwnd, size),
&m_pRenderTarget // 作成されたレンダ―ターゲットへのポインタ
);
if (SUCCEEDED(hr))
{
// Create a gray brush.
// グレーのブラシを作成
hr = m_pRenderTarget->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::LightSlateGray),
&m_pLightSlateGrayBrush //作成したブラシへのポインター
);
}
if (SUCCEEDED(hr))
{
// Create a blue brush.
// ブルーのブラシを作成
hr = m_pRenderTarget->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::CornflowerBlue),
&m_pCornflowerBlueBrush // 作成したブラシへのポインター
);
}
}
return hr; // レンダーターゲットのハンドルを返す。
}
//
void DemoApp::DiscardDeviceResources()
{
SafeRelease(&m_pRenderTarget); // レンダ―ターゲットの解放
SafeRelease(&m_pLightSlateGrayBrush); // ブラシの解放
SafeRelease(&m_pCornflowerBlueBrush); // ブラシの解放
}
LRESULT CALLBACK DemoApp::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
if (message == WM_CREATE) // ウィンドウ作成時
{
LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
DemoApp* pDemoApp = (DemoApp*)pcs->lpCreateParams; // Demoクラスへのポインタ
::SetWindowLongPtrW( // 指定したウィンドウの属性を変更します。
hwnd,
GWLP_USERDATA, //ウィンドウに関連付けられているユーザー データを設定します
reinterpret_cast<LONG_PTR>(pDemoApp)
);
result = 1;
}
else
{
DemoApp* pDemoApp = reinterpret_cast<DemoApp*>(static_cast<LONG_PTR>(
::GetWindowLongPtrW( // 指定したウィンドウに関する情報を取得します。
hwnd,
GWLP_USERDATA //ユーザデータの取得
)));
bool wasHandled = false;
if (pDemoApp) // DemoAppがある場合
{
switch (message) // メッセージを処理する。
{
case WM_SIZE: // サイズ変更
{
UINT width = LOWORD(lParam);
UINT height = HIWORD(lParam);
pDemoApp->OnResize(width, height);
}
result = 0;
wasHandled = true;
break;
case WM_DISPLAYCHANGE: // ディスプレイの解像度が変更されたとき
{
InvalidateRect(hwnd, NULL, FALSE); // ウィンドウの更新をする
}
result = 0;
wasHandled = true;
break;
case WM_PAINT: // 描画
{
pDemoApp->OnRender(); // ウィンドウに描画する
ValidateRect(hwnd, NULL); // 指定したウィンドウの更新領域から四角形を削除する
}
result = 0;
wasHandled = true;
break;
case WM_DESTROY:
{
PostQuitMessage(0);
}
result = 1;
wasHandled = true;
break;
}
}
if (!wasHandled) // メッセージが処理されなかったらデフォルトを呼ぶ
{
result = DefWindowProc(hwnd, message, wParam, lParam);
}
}
return result;
}
// ウィンドウに描画する
HRESULT DemoApp::OnRender()
{
HRESULT hr = S_OK;
// レンダ―デバイスと二つのブラシを作成
hr = CreateDeviceResources();
if (SUCCEEDED(hr))
{
m_pRenderTarget->BeginDraw(); // 描画を開始
m_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity()); // 恒等行列変換
m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White)); // 指定した色の描画領域をクリアします。
D2D1_SIZE_F rtSize = m_pRenderTarget->GetSize(); // レンダー ターゲットの現在のサイズを得る
// Draw a grid background.
// グリッド背景の描画
int width = static_cast<int>(rtSize.width);
int height = static_cast<int>(rtSize.height);
// 縦線の描画
for (int x = 0; x < width; x += 10)
{
m_pRenderTarget->DrawLine(
D2D1::Point2F(static_cast<FLOAT>(x), 0.0f), // 行の始点
D2D1::Point2F(static_cast<FLOAT>(x), rtSize.height), // 行の終点
m_pLightSlateGrayBrush, // 描画ブラシ
0.5f // ストローク幅
);
}
// 横線の描画
for (int y = 0; y < height; y += 10)
{
m_pRenderTarget->DrawLine(
D2D1::Point2F(0.0f, static_cast<FLOAT>(y)),
D2D1::Point2F(rtSize.width, static_cast<FLOAT>(y)),
m_pLightSlateGrayBrush,
0.5f
);
}
// Draw two rectangles.
// 小さい四角(塗りつぶし用)
D2D1_RECT_F rectangle1 = D2D1::RectF(
rtSize.width / 2 - 50.0f,
rtSize.height / 2 - 50.0f,
rtSize.width / 2 + 50.0f,
rtSize.height / 2 + 50.0f
);
// 大きい四角(枠のみ)
D2D1_RECT_F rectangle2 = D2D1::RectF(
rtSize.width / 2 - 100.0f,
rtSize.height / 2 - 100.0f,
rtSize.width / 2 + 100.0f,
rtSize.height / 2 + 100.0f
);
// Draw a filled rectangle.
// 四角形を塗りつぶす
m_pRenderTarget->FillRectangle(&rectangle1, m_pLightSlateGrayBrush);
// Draw the outline of a rectangle.
// 四角形を描画する
m_pRenderTarget->DrawRectangle(&rectangle2, m_pCornflowerBlueBrush);
hr = m_pRenderTarget->EndDraw(); // 描画終了
}
if (hr == D2DERR_RECREATE_TARGET) // デバイス損失の確認
{
hr = S_OK;
DiscardDeviceResources(); // リソースの解放
}
return hr; // 戻り値は使っていない。
}
// ウィンドウのサイズ変更時にレンダー ターゲットのサイズを調整する
void DemoApp::OnResize(UINT width, UINT height)
{
if (m_pRenderTarget)
{
// Note: This method can fail, but it's okay to ignore the
// error here, because the error will be returned again
// the next time EndDraw is called.
// 注:このメソッドは失敗する可能性がありますが、
// 次にEndDrawが呼び出されたときに再びエラーが返されるため、
// ここではエラーを無視しても問題ありません。
m_pRenderTarget->Resize(D2D1::SizeU(width, height)); // サイズ変更
}
}
〇実行結果