Direct2D是一个用于创建2D图形的本地代码、即时模式API。本文演示了如何通过一个典型的Win32应用程序使用Direct2D绘制图像到窗口上。
本文包含以下部分:
- 画一个简单的矩形
- 步骤1:包含Direct2D头文件
- 步骤2:创建一个ID2D1Factory接口对象
- 步骤3:创建一个ID2D1HwndRenderTarget接口对象
- 步骤4:创建一个画刷对象
- 步骤5:绘制矩形
- 步骤6:释放资源
- 创建一个简单的Direct2D应用程序
画一个简单的矩形
要使用GDI绘制矩形,您需要处理WM_PAINT消息,如下面的代码所示:
switch(message)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
// Obtain the size of the drawing area.
RECT rc;
GetClientRect(
hwnd,
&rc
);
// Save the original object
HGDIOBJ original = NULL;
original = SelectObject(
ps.hdc,
GetStockObject(DC_PEN)
);
// Create a pen.
HPEN blackPen = CreatePen(PS_SOLID, 3, 0);
// Select the pen.
SelectObject(ps.hdc, blackPen);
// Draw a rectangle.
Rectangle(
ps.hdc,
rc.left + 100,
rc.top + 100,
rc.right - 100,
rc.bottom - 100);
DeleteObject(blackPen);
// Restore the original object
SelectObject(ps.hdc, original);
EndPaint(hwnd, &ps);
}
return 0;
// Code for handling other messages.
使用Direct2D绘制相同矩形的代码是类似的:创建绘图资源,描述要绘制的形状,绘制该形状,然后释放绘图资源。下面的小节将详细描述这些步骤。
步骤1:包含Direct2D头文件
除了Win32应用程序所必须包含的头文件以外,还需要包含d2d1.h头文件。
步骤2:创建一个ID2D1Factory接口对象
任何Direct2D应用程序首先都需要创建 ID2D1Factory接口对象。
ID2D1Factory* pD2DFactory = NULL;
HRESULT hr = D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
&pD2DFactory
);
ID2D1Factory接口是使用Direct2D的起点;所有的Direct2D资源都是通过ID2D1Factory接口创建的。
在创建ID2D1Factory接口对象时,可以指定它的工作方式是多线程的还是单线程的。在这里我们使用单线程模式。
通常,您的应用程序应该只创建ID2D1Factory 接口对象一次,并在应用程序的生命周期内保留它,直到程序销毁。
步骤3:创建一个ID2D1HwndRenderTarget接口对象
创建完工厂对象(ID2D1Factory *)后,使用它来创建渲染目标对象(ID2D1HwndRenderTarget*)。
// Obtain the size of the drawing area.
RECT rc;
GetClientRect(hwnd, &rc);
// Create a Direct2D render target
ID2D1HwndRenderTarget* pRT = NULL;
HRESULT hr = pD2DFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(
hwnd,
D2D1::SizeU(
rc.right - rc.left,
rc.bottom - rc.top)
),
&pRT
);
渲染目标对象是一个可以执行绘图操作并创建依赖于设备的绘图资源(如画笔)的设备。不同类型的渲染目标对象渲染到不同的设备。前面的示例使用一个ID2D1HwndRenderTarget,它绘制到屏幕的一部分(窗口)。
如果可能的话,渲染目标对象使用GPU来加速渲染操作和创建绘图资源。否则,渲染目标对象使用CPU来处理绘制指令和创建资源。(你可以在创建渲染目标对象时使用D2D1_RENDER_TARGET_TYPE标志来修改这个行为。)
CreateHwndRenderTarget方法有三个参数。第一个参数是D2D1_RENDER_TARGET_PROPERTIES结构体,指定远程显示选项,是否强制渲染目标使用软件或者硬件加速,以及DPI。本例中的代码使用D2D1::RenderTargetProperties helper函数来接受默认的渲染目标属性。
第二个参数是一个D2D1_HWND_RENDER_TARGET_PROPERTIES结构体,它指定绘制内容的窗口(HWND)、绘制目标的初始大小(以像素为单位)及其presentation options。这个例子使用了D2D1::HwndRenderTargetProperties helper函数来指定HWND和初始大小。它使用默认的表示选项。
第三个参数是接收渲染目标对象引用的指针的地址。
当GPU硬件加速可用时,你创建一个渲染目标对象将在GPU上分配资源。通过一次性创建渲染目标对象并尽可能长时间地保留它,您可以获得更好的性能。你的应用程序应该创建渲染目标对象一次,并在应用程序的生命周期内保留它,直到程序关闭或者收到D2DERR_RECREATE_TARGET错误再释放它。当您收到此错误时,您需要重新创建渲染目标对象(以及它创建的任何资源)。
步骤4:创建一个画刷对象
与工厂对象(ID2D1Factory)一样,渲染目标对象(ID2D1HwndRenderTarget)可以创建绘图资源。在这个例子中,渲染目标对象创建了一个画刷对象(ID2D1SolidColorBrush)。
ID2D1SolidColorBrush* pBlackBrush = NULL;
if (SUCCEEDED(hr))
{
pRT->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::Black),
&pBlackBrush
);
}
画刷是绘制区域的对象,如形状的描边或几何图形的填充。本例中的画刷使用预定义的纯黑色绘制区域。
Direct2D还提供了其他类型的画刷:渐变画刷用于绘制线性和径向渐变,位图画刷用于绘制位图和模式。
一些绘图API(如GDI)提供了用于绘制轮廓的画笔和用于填充形状的画刷。Direct2D没有画笔对象,只有画刷对象,绘制轮廓线和填充形状都是用画刷对象。在绘制轮廓线时,使用画刷对象和 ID2D1StrokeStyle接口对象一起控制线条的形状。
画刷只能由创建它的渲染目标对象和相同资源域中的其他渲染目标对象调用。一般来说,你创建了一个画刷对象以后,应该保留它们直到渲染目标对象的生命周期结束。但是 ID2D1SolidColorBrush画刷对象比较特殊,因为创建它的成本比较低,你可以在每次绘制一帧的时候重新创建一个ID2D1SolidColorBrush对象。你可以使用一个ID2D1SolidColorBrush对象,并且在每次绘制的时候修改它的颜色。
步骤5:绘制矩形
接下来,使用渲染目标绘制矩形。
pRT->BeginDraw();
pRT->DrawRectangle(
D2D1::RectF(
rc.left + 100.0f,
rc.top + 100.0f,
rc.right - 100.0f,
rc.bottom - 100.0f),
pBlackBrush);
HRESULT hr = pRT->EndDraw();
方法DrawRectangle 有2个参数:一个指定绘制的矩形区域,一个是用来绘制轮廓线的画刷。还有2个可选的参数,可以指定矩形的边线宽度、样式、和边线的连接方式以及结束方式。在调用任何绘制方法之前必须先调用 BeginDraw 方法,调用完所有的绘制方法以后,必须调用 EndDraw 方法结束绘制。EndDraw方法返回 HRESULT 类型表示此次绘制是否成功。
步骤6:释放资源
当绘制结束以后或者收到 D2DERR_RECREATE_TARGET错误以后,释放渲染目标对象以及它创建的所有资源。
SafeRelease(pRT);
SafeRelease(pBlackBrush);
当应用程序准备退出之前,释放ID2D1Factory对象:
SafeRelease(pD2DFactory);
创建一个简单的Direct2D应用程序
本文只是简单介绍了一下D2D绘制的基本对象及创建过程,下一遍博文将介绍整个D2D 应用程序的框架和详细代码。