Membuat aplikasi Direct2D sederhana
Topik ini memandu Anda melalui proses pembuatan kelas DemoApp, yang menciptakan jendela dan menggunakan Direct2D untuk menggambar konten. Dalam tutorial ini, Anda mempelajari cara membuat sumber daya Direct2D, dan menggambar bentuk dasar. Anda juga mempelajari cara menyusun aplikasi untuk meningkatkan performa dengan meminimalkan pembuatan sumber daya.
Untuk mengikuti tutorial, Anda dapat menggunakan Microsoft Visual Studio untuk membuat proyek Win32, lalu mengganti kode di header aplikasi utama dan file .cpp
dengan kode yang dijelaskan dalam tutorial ini.
Lihat juga aplikasi sampel Simple Direct2D di GitHub.
Nota
Jika Anda ingin membuat aplikasi Universal Windows Platform (UWP) yang menggunakan Direct2D, lihat panduan cepat Direct2D untuk topik Windows 8 di .
Untuk gambaran umum antarmuka yang dapat Anda gunakan untuk membuat konten Direct2D, lihat gambaran umum API Direct2D .
Setelah menyelesaikan tutorial, kelas DemoApp menghasilkan output yang ditunjukkan dalam ilustrasi berikut.
Bagian 1: Buat Header DemoApp
Dalam langkah ini, Anda menyiapkan aplikasi untuk menggunakan Direct2D dengan menambahkan header dan makro yang diperlukan. Anda juga mendeklarasikan metode dan anggota data yang akan Anda gunakan di bagian selanjutnya dari tutorial ini.
Dalam file header aplikasi Anda, sertakan header yang sering digunakan berikut.
// 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>
Deklarasikan fungsi tambahan untuk merilis antarmuka, dan makro untuk penanganan kesalahan dan mengambil alamat dasar modul.
template<class Interface> inline void SafeRelease( Interface **ppInterfaceToRelease) { if (*ppInterfaceToRelease != NULL) { (*ppInterfaceToRelease)->Release(); (*ppInterfaceToRelease) = NULL; } } #ifndef Assert #if defined( DEBUG ) || defined( _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
Deklarasikan metode untuk menginisialisasi kelas, membuat dan membuang sumber daya, menangani perulangan pesan, merender konten, dan prosedur windows.
class DemoApp { public: DemoApp(); ~DemoApp(); // Register the window class and call methods for instantiating drawing resources HRESULT Initialize(); // Process and dispatch messages void RunMessageLoop(); private: // Initialize device-independent resources. HRESULT CreateDeviceIndependentResources(); // Initialize device-dependent resources. HRESULT CreateDeviceResources(); // Release device-dependent resource. void DiscardDeviceResources(); // Draw content. HRESULT OnRender(); // Resize the render target. void OnResize( UINT width, UINT height ); // The windows procedure. static LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ); };
Sebagai anggota kelas, deklarasikan pointer untuk objek ID2D1Factory, objek ID2D1HwndRenderTarget, dan dua objek ID2D1SolidColorBrush.
private: HWND m_hwnd; ID2D1Factory* m_pDirect2dFactory; ID2D1HwndRenderTarget* m_pRenderTarget; ID2D1SolidColorBrush* m_pLightSlateGrayBrush; ID2D1SolidColorBrush* m_pCornflowerBlueBrush;
Bagian 2: Menerapkan infrastruktur kelas
Di bagian ini, Anda menerapkan konstruktor dan destruktor DemoApp, metode inisialisasi dan perulangan pesannya, dan fungsi WinMain. Sebagian besar metode ini terlihat sama dengan yang ditemukan di aplikasi Win32 lainnya. Satu-satunya pengecualian adalah metode Inisialisasi, yang memanggil metode CreateDeviceIndependentResources (yang akan Anda tentukan di bagian berikutnya), yang membuat beberapa sumber daya Direct2D.
Dalam file implementasi kelas, terapkan konstruktor kelas dan destruktor. Konstruktor harus menginisialisasi anggotanya ke
NULL
. Destruktor harus melepaskan antarmuka apa pun yang disimpan sebagai anggota kelas.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); }
Terapkan metode DemoApp::RunMessageLoop, yang menerjemahkan dan mengirimkan pesan.
void DemoApp::RunMessageLoop() { MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } }
Terapkan metode Initialize, yang berfungsi untuk membuat jendela, menampilkannya, dan memanggil metode DemoApp::CreateDeviceIndependentResources. Anda akan menerapkan metode CreateDeviceIndependentResources di bagian berikutnya.
HRESULT DemoApp::Initialize() { HRESULT hr; // Initialize device-independent resources, such // as the Direct2D factory. hr = CreateDeviceIndependentResources(); if (SUCCEEDED(hr)) { // Register the window class. 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. m_hwnd = CreateWindow( L"D2DDemoApp", L"Direct2D demo application", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, NULL, NULL, HINST_THISCOMPONENT, this); 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. 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; }
Terapkan metode WinMain, yang berfungsi sebagai titik masuk aplikasi. Inisialisasi instans kelas DemoApp dan mulai perulangan pesannya.
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))) { { DemoApp app; if (SUCCEEDED(app.Initialize())) { app.RunMessageLoop(); } } CoUninitialize(); } return 0; }
Bagian 3: Membuat sumber daya Direct2D
Di bagian ini, Anda membuat sumber daya Direct2D yang Anda gunakan untuk menggambar. Direct2D menyediakan dua jenis sumber daya—sumber daya independen perangkat yang dapat bertahan selama durasi aplikasi, dan sumber daya yang bergantung pada perangkat. Sumber daya yang bergantung pada perangkat dikaitkan dengan perangkat penyajian tertentu, dan akan berhenti berfungsi jika perangkat tersebut dihapus.
Terapkan metode DemoApp::CreateDeviceIndependentResources. Dalam metode ini, buat ID2D1Factory, yang merupakan sumber daya independen dari perangkat untuk membuat sumber daya Direct2D lainnya. Gunakan anggota kelas
m_pDirect2DdFactory
untuk menyimpan factory.HRESULT DemoApp::CreateDeviceIndependentResources() { HRESULT hr = S_OK; // Create a Direct2D factory. hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory); return hr; }
Terapkan DemoApp::CreateDeviceResources metode. Metode ini membuat sumber daya yang bergantung pada perangkat jendela, sasaran render, dan dua kuas. Ambil ukuran area klien dan buatID2D1HwndRenderTargetdengan ukuran yang sama yang dirender ke HWNDjendela . Simpan target render di anggota kelas
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. hr = m_pDirect2dFactory->CreateHwndRenderTarget( D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties(m_hwnd, size), &m_pRenderTarget);
Gunakan target render untuk membuat abu-abu ID2D1SolidColorBrush dan biru cornflower ID2D1SolidColorBrush.
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 ); }
Karena metode ini akan dipanggil berulang kali, tambahkan pernyataan
if
untuk memeriksa apakah target render (m_pRenderTarget
) sudah ada. Kode berikut menunjukkan metode lengkap CreateDeviceResources.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. 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; }
Terapkan metode DemoApp::DiscardDeviceResources. Dalam metode ini, lepaskan target render dan dua kuas yang Anda buat di metode DemoApp::CreateDeviceResources.
void DemoApp::DiscardDeviceResources() { SafeRelease(&m_pRenderTarget); SafeRelease(&m_pLightSlateGrayBrush); SafeRelease(&m_pCornflowerBlueBrush); }
Bagian 4: Merender konten Direct2D
Di bagian ini, Anda menerapkan prosedur windows, metode OnRender (yang melukis konten), dan metode OnResize (yang menyesuaikan ukuran target render saat jendela diubah ukurannya).
Terapkan metode DemoApp::WndProc untuk menangani pesan jendela. Untuk pesan WM_SIZE, panggil metode DemoApp::OnResize, dan berikan lebar dan tinggi baru. Untuk pesan WM_PAINT dan WM_DISPLAYCHANGE, panggil metode DemoApp::OnRender untuk melukis jendela. Anda akan menerapkan metode OnRender dan OnResize dalam langkah-langkah berikut.
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; ::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) { 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; }
Terapkan metode DemoApp::OnRender. Pertama, tentukan HRESULT. Kemudian panggil metode CreateDeviceResource. Metode itu dipanggil setiap kali jendela dicat. Ingat bahwa, di langkah 4 Bagian 3, Anda menambahkan pernyataan
if
untuk mencegah metode melakukan pekerjaan apa pun jika target render sudah ada.HRESULT DemoApp::OnRender() { HRESULT hr = S_OK; hr = CreateDeviceResources();
Verifikasi bahwa metode CreateDeviceResource berhasil. Jika tidak, maka jangan melakukan penggambaran apa pun.
if (SUCCEEDED(hr)) {
Di dalam pernyataan
if
yang baru saja Anda tambahkan, mulailah menggambar dengan memanggil metode BeginDraw target render. Atur transformasi target render ke matriks identitas, dan hapus jendela.m_pRenderTarget->BeginDraw(); m_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity()); m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
Ambil ukuran area gambar.
D2D1_SIZE_F rtSize = m_pRenderTarget->GetSize();
Gambar latar belakang kisi dengan menggunakan perulangan
for
dan metode DrawLine dari target render untuk menggambar serangkaian garis.// 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 ); }
Buat dua primitif persegi panjang yang berpusat di layar.
// 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 );
Gunakan metode FillRectangle target render untuk mengisi bagian dalam persegi panjang pertama dengan kuas abu-abu.
// Draw a filled rectangle. m_pRenderTarget->FillRectangle(&rectangle1, m_pLightSlateGrayBrush);
Gunakan metode DrawRectangle dari target render untuk melukis garis luar persegi panjang kedua dengan kuas biru cornflower.
// Draw the outline of a rectangle. m_pRenderTarget->DrawRectangle(&rectangle2, m_pCornflowerBlueBrush);
Panggil metode EndDraw dari target render. Metode EndDraw mengembalikan HRESULT untuk menunjukkan apakah operasi menggambar berhasil. Tutup ruang lingkup pernyataan
if
yang Anda mulai di Langkah 3.hr = m_pRenderTarget->EndDraw(); }
Periksa HRESULT yang dikembalikan oleh EndDraw. Jika ditunjukkan bahwa target render perlu dibuat ulang, panggil metode DemoApp::DiscardDeviceResources untuk merilisnya; ini akan dibuat ulang saat jendela menerima pesan berikutnya WM_PAINT atau WM_DISPLAYCHANGE.
if (hr == D2DERR_RECREATE_TARGET) { hr = S_OK; DiscardDeviceResources(); }
Kembalikan HRESULT, dan tutup cakupan metode.
return hr; }
Terapkan metode DemoApp::OnResize sehingga mengubah ukuran target render ke ukuran baru jendela.
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. m_pRenderTarget->Resize(D2D1::SizeU(width, height)); } }
Anda sekarang telah menyelesaikan tutorial.
Nota
Untuk menggunakan Direct2D, pastikan aplikasi Anda menyertakan file header d2d1.h
, dan kompilasi terhadap pustaka d2d1.lib
. Anda dapat menemukan d2d1.h
dan d2d1.lib
di Windows SDK.
Ringkasan
Dalam tutorial ini, Anda mempelajari cara membuat sumber daya Direct2D, dan menggambar bentuk dasar. Anda juga mempelajari cara menyusun aplikasi untuk meningkatkan performa dengan meminimalkan pembuatan sumber daya.