http://msdn.microsoft.com/zh-cn/library/windows/desktop/hh404478
This topic walks you through the process of creating the DemoApp class, which creates a window and uses Direct2D to draw a grid and two rectangles. In this tutorial, you learn how to create Direct2D resources and draw basic shapes. You also learn how to structure your application to enhance performance by minimizing resource creation.
To follow the tutorial, you can use Microsoft Visual Studio 2008 to create a Win32 project and then replace the code in the main application header and cpp file with the code described in this tutorial, or you can copy the code from the Draw Rectangle Example.
Note If you want to create a Windows Store app that uses Direct2D, see the Direct2D Quickstart for Windows 8 topic.
For an overview of the interfaces you can use to create Direct2D content, see the Direct2D API Overview.
This tutorial contains the following parts:
Upon completion, the DemoApp class produces the output shown in the following illustration.
In this step, you set up your application to use Direct2D by adding the necessary headers and macros. You also declare the methods and data members you'll use in later parts of this tutorial.
// 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(); (*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
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 );
};
private:
HWND m_hwnd;
ID2D1Factory* m_pDirect2dFactory;
ID2D1HwndRenderTarget* m_pRenderTarget;
ID2D1SolidColorBrush* m_pLightSlateGrayBrush;
ID2D1SolidColorBrush* m_pCornflowerBlueBrush;
In this part, you implement the DemoApp constructor and destructor, its initialization and message looping methods, and the WinMain function. Most of these methods look the same as those found in any other Win32 application. The only exception is the Initialize method, which calls the CreateDeviceIndependentResources method (which you define in the next part) that creates several Direct2D resources.
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)) { // 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); // Because the CreateWindow function takes its size in pixels, // obtain the system DPI and use it to scale the window size. FLOAT dpiX, dpiY; // The factory returns the current system DPI. This is also the value it will use // to create its own windows. m_pDirect2dFactory->GetDesktopDpi(&dpiX, &dpiY); // Create the window. m_hwnd = CreateWindow( L"D2DDemoApp", L"Direct2D Demo App", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, static_cast<UINT>(ceil(640.f * dpiX / 96.f)), static_cast<UINT>(ceil(480.f * dpiY / 96.f)), NULL, NULL, HINST_THISCOMPONENT, this ); hr = m_hwnd ? S_OK : E_FAIL; if (SUCCEEDED(hr)) { 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))) { { DemoApp app; if (SUCCEEDED(app.Initialize())) { app.RunMessageLoop(); } } CoUninitialize(); } return 0; }
In this part, you create the Direct2D resources that you use to draw. Direct2D provides two types of resources: device-independent resources that can last for the duration of the application, and device-dependent resources. Device-dependent resources are associated with a particular rendering device and will cease to function if that device is removed.
HRESULT DemoApp::CreateDeviceIndependentResources() { HRESULT hr = S_OK; // Create a Direct2D factory. hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory); return hr; }
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 ); }
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; }
void DemoApp::DiscardDeviceResources()
{
SafeRelease(&m_pRenderTarget);
SafeRelease(&m_pLightSlateGrayBrush);
SafeRelease(&m_pCornflowerBlueBrush);
}
In this part, you implement the windows procedure, the OnRender method that paints content, and the OnResize method that adjusts the size of the render target when the window is resized.
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, PtrToUlong(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; }
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. m_pRenderTarget->Resize(D2D1::SizeU(width, height)); } }
You've completed the tutorial. To see a completed version of the DemoApp code, see the Draw Rectangle Example.
Note To use Direct2D, ensure that your application includes the d2d1.h header file and compiles against the d2d1.lib library. You can find d2d1.h and d2d1.lib in Windows Software Development Kit (SDK) for Windows 7.
In this tutorial, you learned how to create Direct2D resources and draw basic shapes. You also learned how to structure your application to enhance performance by minimizing resource creation.