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程序的结构
- 在程序初始化函数处(Constructor)创建设备独立资源,如ID2D1Factory,IDWriteFactory等;
- 创建设备依赖资源WM_CREATE,如果运行过程中出现设备丢失,需要重新创建;
- 响应WM_PAINT消息,在OnPaint()或OnDraw()等处,用创建的资源Render;
- 响应WM_SIZE消息,在OnSize()处调用ID2D1RenderTarget::Resize();
- 响应WM_ERASEBKGND,在OnEraseBkgnd()处返回FALSE,阻止GDI重绘客户区背景色,设置背景色的工作交给Direct2D在Render时设置,否则在Resize时会出现窗口闪烁的问题;
- 退出程序前WM_DESTROY,清理资源。
为提高程序的性能,尽量减少资源的创建和销毁操作,将能够重复利用的资源接口变量申明为View类的成员变量。
三. Direct2D demo
一个简单的MFC程序,用于演示Direct2D程序的结构和一些简单绘图操作。
1.Direct2D prerequisite
D2dPrerequisite.h,包含一些编译Direct2D程序所需要用到的头文件,lib库文件,帮助宏和预处理指令:
//D2dPrerequisite.h
#pragma once
//
//Header files and lib files for Direct2D and DirectWrite
#include <d2d1.h> //Direct2D,for normal rendering task
#include <d2d1helper.h>
#include <dwrite.h> //DirectWrite,for drawing text
#include <wincodec.h> //Windows imaging component,for image decoding
#pragma comment(lib,"d2d1.lib")
#pragma comment(lib,"dwrite.lib")
#pragma comment(lib,"windowscodecs.lib")
using namespace D2D1;
//
//Helper template for resource releasing
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
2.View类中的成员变量:#include "D2dPrerequisite.h"
//View.h
private:
//Direct2D interface
ID2D1Factory* m_pD2d1Factory;
ID2D1HwndRenderTarget* m_pHwndRenderTarget;
ID2D1SolidColorBrush* m_pSolidColorBrush;
ID2D1LinearGradientBrush* m_pLinearGradientBrush;
ID2D1RadialGradientBrush* m_pRadialGradientBrush;
//DirectWrite interface
IDWriteFactory* m_pDWriteFactory;
IDWriteTextFormat* m_pTextFormat;
#include "D2dPrerequisite.h"
//View.h
private:
//Direct2D interface
ID2D1Factory* m_pD2d1Factory;
ID2D1HwndRenderTarget* m_pHwndRenderTarget;
ID2D1SolidColorBrush* m_pSolidColorBrush;
ID2D1LinearGradientBrush* m_pLinearGradientBrush;
ID2D1RadialGradientBrush* m_pRadialGradientBrush;
//DirectWrite interface
IDWriteFactory* m_pDWriteFactory;
IDWriteTextFormat* m_pTextFormat;
3.创建设备独立资源//View.cpp
BOOL CD2D1_startView::CreateDeviceIndependentResource()
{
HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,&m_pD2d1Factory);
ASSERT(hr == S_OK);
if (SUCCEEDED(hr))
{
hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED,
__uuidof(m_pDWriteFactory),
reinterpret_cast<IUnknown**>(&m_pDWriteFactory));
ASSERT(hr == S_OK);
}
//Create TextFormat object with IDWriteFactory
if (SUCCEEDED(hr))
{
const CString fontName = _T("Verdana");
const FLOAT fontSize = 32.0f;
hr = m_pDWriteFactory->CreateTextFormat(
fontName,
NULL,
DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
fontSize,
L"", //locale
&m_pTextFormat
);
ASSERT(hr == S_OK);
if (SUCCEEDED(hr))
{
//Center alignment vertically and horizontally
m_pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
m_pTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
}
}
return (hr == S_OK);
}
//View.cpp
BOOL CD2D1_startView::CreateDeviceIndependentResource()
{
HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,&m_pD2d1Factory);
ASSERT(hr == S_OK);
if (SUCCEEDED(hr))
{
hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED,
__uuidof(m_pDWriteFactory),
reinterpret_cast<IUnknown**>(&m_pDWriteFactory));
ASSERT(hr == S_OK);
}
//Create TextFormat object with IDWriteFactory
if (SUCCEEDED(hr))
{
const CString fontName = _T("Verdana");
const FLOAT fontSize = 32.0f;
hr = m_pDWriteFactory->CreateTextFormat(
fontName,
NULL,
DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
fontSize,
L"", //locale
&m_pTextFormat
);
ASSERT(hr == S_OK);
if (SUCCEEDED(hr))
{
//Center alignment vertically and horizontally
m_pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
m_pTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
}
}
return (hr == S_OK);
}
4.创建设备依赖资源//View.cpp
BOOL CD2D1_startView::CreateDeviceDependentResource()
{
ASSERT(m_pD2d1Factory != NULL);
if (m_pHwndRenderTarget != NULL) //There is no need to create render target
return TRUE;
RECT rc;
GetClientRect(&rc);
D2D1_SIZE_U size = SizeU(rc.right-rc.left,rc.bottom-rc.top);
HRESULT hr = m_pD2d1Factory->CreateHwndRenderTarget(
RenderTargetProperties(),
HwndRenderTargetProperties(m_hWnd,size),//Bind the HwndRenderTarget to view window
&m_pHwndRenderTarget);
ASSERT(hr == S_OK);
if (SUCCEEDED(hr))
{
//Create solid color brush
hr = m_pHwndRenderTarget->CreateSolidColorBrush(
ColorF(ColorF::LightGreen),
&m_pSolidColorBrush);
ASSERT(hr == S_OK);
//Create gradient stops collection,
//used by linear gradient brush or radial gradient brush
ID2D1GradientStopCollection* pGradientStops = NULL;
D2D1_GRADIENT_STOP stops[2];
stops[0].color = ColorF(ColorF::Yellow);
stops[0].position = 0.0f;
stops[1].color = ColorF(ColorF::Red);
stops[1].position = 1.0f;
HRESULT hr = m_pHwndRenderTarget->CreateGradientStopCollection(
stops,
2,
D2D1_GAMMA_2_2,
D2D1_EXTEND_MODE_CLAMP,
&pGradientStops);
ASSERT(hr == S_OK);
//Create linear gradient brush
hr = m_pHwndRenderTarget->CreateLinearGradientBrush(
LinearGradientBrushProperties(Point2F(210,110),Point2F(290,190)),
pGradientStops,
&m_pLinearGradientBrush);
ASSERT(hr == S_OK);
//Create radial gradient brush
hr = m_pHwndRenderTarget->CreateRadialGradientBrush(
RadialGradientBrushProperties(Point2F(350,150),Point2F(0,0),50,50),
pGradientStops,
&m_pRadialGradientBrush);
ASSERT(hr == S_OK);
SafeRelease(&pGradientStops);
}
return (hr == S_OK);
}
//View.cpp
BOOL CD2D1_startView::CreateDeviceDependentResource()
{
ASSERT(m_pD2d1Factory != NULL);
if (m_pHwndRenderTarget != NULL) //There is no need to create render target
return TRUE;
RECT rc;
GetClientRect(&rc);
D2D1_SIZE_U size = SizeU(rc.right-rc.left,rc.bottom-rc.top);
HRESULT hr = m_pD2d1Factory->CreateHwndRenderTarget(
RenderTargetProperties(),
HwndRenderTargetProperties(m_hWnd,size),//Bind the HwndRenderTarget to view window
&m_pHwndRenderTarget);
ASSERT(hr == S_OK);
if (SUCCEEDED(hr))
{
//Create solid color brush
hr = m_pHwndRenderTarget->CreateSolidColorBrush(
ColorF(ColorF::LightGreen),
&m_pSolidColorBrush);
ASSERT(hr == S_OK);
//Create gradient stops collection,
//used by linear gradient brush or radial gradient brush
ID2D1GradientStopCollection* pGradientStops = NULL;
D2D1_GRADIENT_STOP stops[2];
stops[0].color = ColorF(ColorF::Yellow);
stops[0].position = 0.0f;
stops[1].color = ColorF(ColorF::Red);
stops[1].position = 1.0f;
HRESULT hr = m_pHwndRenderTarget->CreateGradientStopCollection(
stops,
2,
D2D1_GAMMA_2_2,
D2D1_EXTEND_MODE_CLAMP,
&pGradientStops);
ASSERT(hr == S_OK);
//Create linear gradient brush
hr = m_pHwndRenderTarget->CreateLinearGradientBrush(
LinearGradientBrushProperties(Point2F(210,110),Point2F(290,190)),
pGradientStops,
&m_pLinearGradientBrush);
ASSERT(hr == S_OK);
//Create radial gradient brush
hr = m_pHwndRenderTarget->CreateRadialGradientBrush(
RadialGradientBrushProperties(Point2F(350,150),Point2F(0,0),50,50),
pGradientStops,
&m_pRadialGradientBrush);
ASSERT(hr == S_OK);
SafeRelease(&pGradientStops);
}
return (hr == S_OK);
}
5.Render//View.cpp
void CD2D1_startView::Render()
{
ASSERT(m_pD2d1Factory != NULL);
if (!m_pHwndRenderTarget) //Render target need to be recreated
{
//Recreate device dependent resource
BOOL succeeded = CreateDeviceDependentResource();
if (!succeeded)
return;
}
const D2D1_COLOR_F redColor = ColorF(ColorF::Red);
const D2D1_COLOR_F greenColor = ColorF(ColorF::Green);
const D2D1_COLOR_F blueColor = ColorF(ColorF::Blue);
const D2D1_COLOR_F yellowColor = ColorF(ColorF::Yellow);
const D2D1_COLOR_F pinkColor = ColorF(ColorF::Pink);
const D2D1_COLOR_F lightBlue = ColorF(ColorF::LightBlue);
const D2D1_COLOR_F lightGreen = ColorF(ColorF::LightGreen);
m_pHwndRenderTarget->BeginDraw();
m_pHwndRenderTarget->Clear(ColorF(ColorF::White)); //Clear the background
//Draw line
//We can set the color and opacity of solid color brush at any time,
//so there is no need to create brushes for different colors
m_pSolidColorBrush->SetColor(redColor);
D2D1_POINT_2F startPoint = Point2F(10,10);
D2D1_POINT_2F endPoint = Point2F(90,90);
m_pHwndRenderTarget->DrawLine(startPoint,endPoint,m_pSolidColorBrush,5.0);
//Draw rectangle
m_pSolidColorBrush->SetColor(greenColor);
D2D1_RECT_F rect = RectF(110,10,190,90);
m_pHwndRenderTarget->DrawRectangle(rect,m_pSolidColorBrush,4.0f);
//Draw rounded rectangle
m_pSolidColorBrush->SetColor(blueColor);
rect = RectF(210,10,290,90);
D2D1_ROUNDED_RECT roundedRect = RoundedRect(rect,10,10);
m_pHwndRenderTarget->DrawRoundedRectangle(roundedRect,m_pSolidColorBrush,3.0f);
//Draw ellipse
m_pSolidColorBrush->SetColor(redColor);
D2D1_POINT_2F center = D2D1::Point2F(350,50);
D2D1_ELLIPSE ellipse = D2D1::Ellipse(center,40,30);
m_pHwndRenderTarget->DrawEllipse(ellipse,m_pSolidColorBrush,3.0f);
//Fill rectangle
m_pSolidColorBrush->SetColor(pinkColor);
rect = RectF(10,110,90,190);
m_pHwndRenderTarget->FillRectangle(rect,m_pSolidColorBrush);
//Fill rounded rectangle
m_pSolidColorBrush->SetColor(blueColor);
m_pSolidColorBrush->SetOpacity(0.3f);
rect = RectF(110,110,190,190);
roundedRect = RoundedRect(rect,20,20);
m_pHwndRenderTarget->FillRoundedRectangle(roundedRect,m_pSolidColorBrush);
//Fill rectangle with linear gradient brush
rect = RectF(210,110,290,190);
m_pHwndRenderTarget->FillRectangle(rect,m_pLinearGradientBrush);
//Fill ellipse with gradient brush
ellipse = D2D1::Ellipse(Point2F(350,150),40,40);
m_pHwndRenderTarget->FillEllipse(ellipse,m_pRadialGradientBrush);
//Draw text with a linear gradient brush
const CString text = _T("Text drawed with Direct2D & DWrite!");
rect = RectF(20,210,380,290);
m_pHwndRenderTarget->DrawTextW(
text,
text.GetLength(),
m_pTextFormat,
rect,
m_pLinearGradientBrush);
HRESULT hr = m_pHwndRenderTarget->EndDraw();
if (hr == D2DERR_RECREATE_TARGET) //Render target need to be recreated
{
//Discard all device dependent resources,
//and recreate them in the next render procedure
DiscardDeviceDependentResource();
}
}
//View.cpp
void CD2D1_startView::Render()
{
ASSERT(m_pD2d1Factory != NULL);
if (!m_pHwndRenderTarget) //Render target need to be recreated
{
//Recreate device dependent resource
BOOL succeeded = CreateDeviceDependentResource();
if (!succeeded)
return;
}
const D2D1_COLOR_F redColor = ColorF(ColorF::Red);
const D2D1_COLOR_F greenColor = ColorF(ColorF::Green);
const D2D1_COLOR_F blueColor = ColorF(ColorF::Blue);
const D2D1_COLOR_F yellowColor = ColorF(ColorF::Yellow);
const D2D1_COLOR_F pinkColor = ColorF(ColorF::Pink);
const D2D1_COLOR_F lightBlue = ColorF(ColorF::LightBlue);
const D2D1_COLOR_F lightGreen = ColorF(ColorF::LightGreen);
m_pHwndRenderTarget->BeginDraw();
m_pHwndRenderTarget->Clear(ColorF(ColorF::White)); //Clear the background
//Draw line
//We can set the color and opacity of solid color brush at any time,
//so there is no need to create brushes for different colors
m_pSolidColorBrush->SetColor(redColor);
D2D1_POINT_2F startPoint = Point2F(10,10);
D2D1_POINT_2F endPoint = Point2F(90,90);
m_pHwndRenderTarget->DrawLine(startPoint,endPoint,m_pSolidColorBrush,5.0);
//Draw rectangle
m_pSolidColorBrush->SetColor(greenColor);
D2D1_RECT_F rect = RectF(110,10,190,90);
m_pHwndRenderTarget->DrawRectangle(rect,m_pSolidColorBrush,4.0f);
//Draw rounded rectangle
m_pSolidColorBrush->SetColor(blueColor);
rect = RectF(210,10,290,90);
D2D1_ROUNDED_RECT roundedRect = RoundedRect(rect,10,10);
m_pHwndRenderTarget->DrawRoundedRectangle(roundedRect,m_pSolidColorBrush,3.0f);
//Draw ellipse
m_pSolidColorBrush->SetColor(redColor);
D2D1_POINT_2F center = D2D1::Point2F(350,50);
D2D1_ELLIPSE ellipse = D2D1::Ellipse(center,40,30);
m_pHwndRenderTarget->DrawEllipse(ellipse,m_pSolidColorBrush,3.0f);
//Fill rectangle
m_pSolidColorBrush->SetColor(pinkColor);
rect = RectF(10,110,90,190);
m_pHwndRenderTarget->FillRectangle(rect,m_pSolidColorBrush);
//Fill rounded rectangle
m_pSolidColorBrush->SetColor(blueColor);
m_pSolidColorBrush->SetOpacity(0.3f);
rect = RectF(110,110,190,190);
roundedRect = RoundedRect(rect,20,20);
m_pHwndRenderTarget->FillRoundedRectangle(roundedRect,m_pSolidColorBrush);
//Fill rectangle with linear gradient brush
rect = RectF(210,110,290,190);
m_pHwndRenderTarget->FillRectangle(rect,m_pLinearGradientBrush);
//Fill ellipse with gradient brush
ellipse = D2D1::Ellipse(Point2F(350,150),40,40);
m_pHwndRenderTarget->FillEllipse(ellipse,m_pRadialGradientBrush);
//Draw text with a linear gradient brush
const CString text = _T("Text drawed with Direct2D & DWrite!");
rect = RectF(20,210,380,290);
m_pHwndRenderTarget->DrawTextW(
text,
text.GetLength(),
m_pTextFormat,
rect,
m_pLinearGradientBrush);
HRESULT hr = m_pHwndRenderTarget->EndDraw();
if (hr == D2DERR_RECREATE_TARGET) //Render target need to be recreated
{
//Discard all device dependent resources,
//and recreate them in the next render procedure
DiscardDeviceDependentResource();
}
}
6. 销毁设备依赖资源,以备下次Render时再次创建//View.cpp
void CD2D1_startView::DiscardDeviceDependentResource()
{
SafeRelease(&m_pRadialGradientBrush);
SafeRelease(&m_pLinearGradientBrush);
SafeRelease(&m_pSolidColorBrush);
SafeRelease(&m_pHwndRenderTarget);
}
//View.cpp
void CD2D1_startView::DiscardDeviceDependentResource()
{
SafeRelease(&m_pRadialGradientBrush);
SafeRelease(&m_pLinearGradientBrush);
SafeRelease(&m_pSolidColorBrush);
SafeRelease(&m_pHwndRenderTarget);
}
7.Resize//View.cpp
void CD2D1_startView::Resize(int width,int height)
{
if (m_pHwndRenderTarget)
{
m_pHwndRenderTarget->Resize(SizeU(width,height));
}
}
//View.cpp
void CD2D1_startView::Resize(int width,int height)
{
if (m_pHwndRenderTarget)
{
m_pHwndRenderTarget->Resize(SizeU(width,height));
}
}