Direct2D入门
一. 资源管理(Resource management)
和Direct3D一样,Direct2D程序需要处理设备丢失(Device lost)问题。Direct2D中的资源分为设备独立资源(Device independent resource)和设备依赖资源(Device dependent resource)。
设备独立资源包括:
- ID2D1DrawingStateBlock
- ID2D1Factory
- ID2D1Geometry 和由此继承而来的接口
- ID2D1GeometrySink and ID2D1SimplifiedGeometrySink
- ID2D1StrokeStyle
设备依赖资源包括:
- ID2D1Brush 和由此继承而来的接口
- ID2D1Layer
- ID2D1RenderTarget 和由此继承而来的接口
- 其他资源
具体参见:http://msdn.microsoft.com/en-us/library/dd756757(v=VS.85).aspx
二. Direct2D程序的结构
- 在程序初始化函数处创建设备独立资源,如ID2D1Factory,IDWriteFactory等;
- 创建设备依赖资源,如果运行过程中出现设备丢失,需要重新创建;
- 响应WM_PAINT消息,在OnPaint()或OnDraw()等处,用创建的资源Render;
- 响应WM_SIZE消息,在OnSize()处调用ID2D1RenderTarget::Resize();
- 响应WM_ERASEBKGND,在OnEraseBkgnd()处返回FALSE,阻止GDI重绘客户区背景色,设置背景色的工作交给Direct2D在Render时设置,否则在Resize时会出现窗口闪烁的问题;
- 退出程序前,清理资源。
为提高程序的性能,尽量减少资源的创建和销毁操作,将能够重复利用的资源接口变量申明为View类的成员变量。
三. Direct2D demo
一个简单的MFC程序,用于演示Direct2D程序的结构和一些简单绘图操作。
1.Direct2D prerequisite
D2dPrerequisite.h,包含一些编译Direct2D程序所需要用到的头文件,lib库文件,帮助宏和预处理指令:
1: //D2dPrerequisite.h
2: #pragma once3:4: //////////////////////////////////////////////////////////////////////////
5: //Header files and lib files for Direct2D and DirectWrite
6: #include <d2d1.h> //Direct2D,for normal rendering task
7: #include <d2d1helper.h>8: #include <dwrite.h> //DirectWrite,for drawing text
9: #include <wincodec.h> //Windows imaging component,for image decoding
10:11: #pragma comment(lib,"d2d1.lib")
12: #pragma comment(lib,"dwrite.lib")
13: #pragma comment(lib,"windowscodecs.lib")
14:15: //////////////////////////////////////////////////////////////////////////
16: //Helper template for resource releasing
17: template<class Interface>18: inline void SafeRelease(Interface **ppInterfaceToRelease)19: {20: if (*ppInterfaceToRelease != NULL)
21: {22: (*ppInterfaceToRelease)->Release();23: (*ppInterfaceToRelease) = NULL;24: }25: }26:27: #ifndef Assert28: #if defined( DEBUG ) || defined( _DEBUG )
29: #define Assert(b) do {if (!(b)) {OutputDebugStringA("Assert: " #b "\n");}} while(0)30: #else
31: #define Assert(b)32: #endif //DEBUG || _DEBUG
33: #endif34:35: #ifndef HINST_THISCOMPONENT36: EXTERN_C IMAGE_DOS_HEADER __ImageBase;37: #define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)38: #endif
2.View类中的成员变量:
1: //View.h
2: private:
3: //Direct2D interface
4: ID2D1Factory* m_pD2d1Factory;5: ID2D1HwndRenderTarget* m_pHwndRenderTarget;6: ID2D1SolidColorBrush* m_pSolidColorBrush;7: ID2D1LinearGradientBrush* m_pLinearGradientBrush;8: ID2D1RadialGradientBrush* m_pRadialGradientBrush;9:10: //DirectWrite interface
11: IDWriteFactory* m_pDWriteFactory;12: IDWriteTextFormat* m_pTextFormat;
3.创建设备独立资源
1: //View.cpp
2: BOOL CBasicView::CreateDeviceIndependentResource()3: {4: HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,&m_pD2d1Factory);5: ASSERT(hr == S_OK);6:7: if (SUCCEEDED(hr))
8: {9: hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED,10: __uuidof(m_pDWriteFactory),11: reinterpret_cast<IUnknown**>(&m_pDWriteFactory));
12: ASSERT(hr == S_OK);13: }14:15: //Create TextFormat object with IDWriteFactory
16: if (SUCCEEDED(hr))
17: {18: const CString fontName = _T("Verdana");19: const FLOAT fontSize = 32.0f;
20: hr = m_pDWriteFactory->CreateTextFormat(21: fontName,22: NULL,23: DWRITE_FONT_WEIGHT_NORMAL,24: DWRITE_FONT_STYLE_NORMAL,25: DWRITE_FONT_STRETCH_NORMAL,26: fontSize,27: L"", //locale
28: &m_pTextFormat29: );30: ASSERT(hr == S_OK);31: if (SUCCEEDED(hr))
32: {33: //Center alignment vertically and horizontally
34: m_pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);35: m_pTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);36: }37: }38: return (hr == S_OK);
39: }
4.创建设备依赖资源
1: //View.cpp
2: BOOL CBasicView::CreateDeviceDependentResource()3: {4: ASSERT(m_pD2d1Factory != NULL);5: if (m_pHwndRenderTarget != NULL) //There is no need to create render target6: return TRUE;
7:8: RECT rc;9: GetClientRect(&rc);10: D2D1_SIZE_U size = SizeU(rc.right-rc.left,rc.bottom-rc.top);11: HRESULT hr = m_pD2d1Factory->CreateHwndRenderTarget(12: RenderTargetProperties(),13: HwndRenderTargetProperties(m_hWnd,size),//Bind the HwndRenderTarget to view window
14: &m_pHwndRenderTarget);15: ASSERT(hr == S_OK);16: if (SUCCEEDED(hr))
17: {18: //Create solid color brush
19: hr = m_pHwndRenderTarget->CreateSolidColorBrush(20: ColorF(ColorF::LightGreen),21: &m_pSolidColorBrush);22: ASSERT(hr == S_OK);23:24: //Create gradient stops collection,
25: //used by linear gradient brush or radial gradient brush
26: ID2D1GradientStopCollection* pGradientStops = NULL;27: D2D1_GRADIENT_STOP stops[2];28: stops[0].color = ColorF(ColorF::Yellow);29: stops[0].position = 0.0f;30: stops[1].color = ColorF(ColorF::Red);31: stops[1].position = 1.0f;32: HRESULT hr = m_pHwndRenderTarget->CreateGradientStopCollection(33: stops,34: 2,35: D2D1_GAMMA_2_2,36: D2D1_EXTEND_MODE_CLAMP,37: &pGradientStops);38: ASSERT(hr == S_OK);39:40: //Create linear gradient brush
41: hr = m_pHwndRenderTarget->CreateLinearGradientBrush(42: LinearGradientBrushProperties(Point2F(210,110),Point2F(290,190)),43: pGradientStops,44: &m_pLinearGradientBrush);45: ASSERT(hr == S_OK);46:47: //Create radial gradient brush
48: hr = m_pHwndRenderTarget->CreateRadialGradientBrush(49: RadialGradientBrushProperties(Point2F(350,150),Point2F(0,0),50,50),50: pGradientStops,51: &m_pRadialGradientBrush);52: ASSERT(hr == S_OK);53:54: SafeRelease(&pGradientStops);55: }56:57: return (hr == S_OK);
58: }
5.Render
1: //View.cpp
2: void CBasicView::Render()
3: {4: ASSERT(m_pD2d1Factory != NULL);5: if (!m_pHwndRenderTarget) //Render target need to be recreated6: {7: //Recreate device dependent resource
8: BOOL succeeded = CreateDeviceDependentResource();9: if (!succeeded)
10: return;
11: }12:13: const D2D1_COLOR_F redColor = ColorF(ColorF::Red);
14: const D2D1_COLOR_F greenColor = ColorF(ColorF::Green);
15: const D2D1_COLOR_F blueColor = ColorF(ColorF::Blue);
16: const D2D1_COLOR_F yellowColor = ColorF(ColorF::Yellow);
17: const D2D1_COLOR_F pinkColor = ColorF(ColorF::Pink);
18: const D2D1_COLOR_F lightBlue = ColorF(ColorF::LightBlue);
19: const D2D1_COLOR_F lightGreen = ColorF(ColorF::LightGreen);
20:21: m_pHwndRenderTarget->BeginDraw();22: m_pHwndRenderTarget->Clear(ColorF(ColorF::White)); //Clear the background
23:24: //Draw line
25: //We can set the color and opacity of solid color brush at any time,
26: //so there is no need to create brushes for different colors
27: m_pSolidColorBrush->SetColor(redColor);28: D2D1_POINT_2F startPoint = Point2F(10,10);29: D2D1_POINT_2F endPoint = Point2F(90,90);30: m_pHwndRenderTarget->DrawLine(startPoint,endPoint,m_pSolidColorBrush,5.0);31:32: //Draw rectangle
33: m_pSolidColorBrush->SetColor(greenColor);34: D2D1_RECT_F rect = RectF(110,10,190,90);35: m_pHwndRenderTarget->DrawRectangle(rect,m_pSolidColorBrush,4.0f);36:37: //Draw rounded rectangle
38: m_pSolidColorBrush->SetColor(blueColor);39: rect = RectF(210,10,290,90);40: D2D1_ROUNDED_RECT roundedRect = RoundedRect(rect,10,10);41: m_pHwndRenderTarget->DrawRoundedRectangle(roundedRect,m_pSolidColorBrush,3.0f);42:43: //Draw ellipse
44: m_pSolidColorBrush->SetColor(redColor);45: D2D1_POINT_2F center = D2D1::Point2F(350,50);46: D2D1_ELLIPSE ellipse = D2D1::Ellipse(center,40,30);47: m_pHwndRenderTarget->DrawEllipse(ellipse,m_pSolidColorBrush,3.0f);48:49: //Fill rectangle
50: m_pSolidColorBrush->SetColor(pinkColor);51: rect = RectF(10,110,90,190);52: m_pHwndRenderTarget->FillRectangle(rect,m_pSolidColorBrush);53:54: //Fill rounded rectangle
55: m_pSolidColorBrush->SetColor(blueColor);56: m_pSolidColorBrush->SetOpacity(0.3f);57: rect = RectF(110,110,190,190);58: roundedRect = RoundedRect(rect,20,20);59: m_pHwndRenderTarget->FillRoundedRectangle(roundedRect,m_pSolidColorBrush);60:61: //Fill rectangle with linear gradient brush
62: rect = RectF(210,110,290,190);63: m_pHwndRenderTarget->FillRectangle(rect,m_pLinearGradientBrush);64:65: //Fill ellipse with gradient brush
66: ellipse = D2D1::Ellipse(Point2F(350,150),40,40);67: m_pHwndRenderTarget->FillEllipse(ellipse,m_pRadialGradientBrush);68:69: //Draw text with a linear gradient brush
70: const CString text = _T("Text drawed with Direct2D & DWrite!");71: rect = RectF(20,210,380,290);72: m_pHwndRenderTarget->DrawTextW(73: text,74: text.GetLength(),75: m_pTextFormat,76: rect,77: m_pLinearGradientBrush);78:79: HRESULT hr = m_pHwndRenderTarget->EndDraw();80:81: if (hr == D2DERR_RECREATE_TARGET) //Render target need to be recreated82: {83: //Discard all device dependent resources,
84: //and recreate them in the next render procedure
85: DiscardDeviceDependentResource();86: }87: }
6. 销毁设备依赖资源,以备下次Render时再次创建
1: //View.cpp
2: void CBasicView::DiscardDeviceDependentResource()
3: {4: SafeRelease(&m_pRadialGradientBrush);5: SafeRelease(&m_pLinearGradientBrush);6: SafeRelease(&m_pSolidColorBrush);7: SafeRelease(&m_pHwndRenderTarget);8: }
7.Resize
1: //View.cpp
2: void CBasicView::Resize(int width,int height)3: {4: if (m_pHwndRenderTarget)
5: {6: m_pHwndRenderTarget->Resize(SizeU(width,height));7: }8: }