本小节主要介绍如何使用Direct2D来绘制几何图形,其中会使用到FillGeometry函数和FillEllipse函数,FillGeometry函数用于填充几何图形的内部区域,而FillEllipse函数则专门用于填充椭圆的内部区域。下面将分别来介绍如何使用FillGeometry函数和FillEllipse函数在应用窗口中绘制简单的三角形和圆形。
1.使用FillGeometry函数绘制三角形
本示例中所要添加的代码比较多,为了让读者能更为清晰地了解如何使用Direct2D绘制几何图形,下面将分为四部分来介绍使用FillGeometry函数绘制三角形的步骤。其中第一部分首先介绍如何新建Direct2D中的资源,第二部分紧接着介绍如何初始化和激活应用窗口,接下来在第三部分中具体来介绍使用FillGeometry函数绘制三角形的方法,最后一部分将介绍如何为项目添加主入口函数,以便于项目能正常运行。
(1)新建Direct2D资源
在使用FillGeometry函数绘制几何图形之前,首先需要新建Direct2D资源,这些资源包括独立于设备的资源和依赖于设备的资源。Direct2D中独立于设备的资源包括ID2D1Factory工厂接口、IDWriteFactory工厂接口、ID2D1Geometry接口和ID2D1GeometrySink接口等,在显示设备发生改变时这些资源不会被重新创建,如显示设备的分辨率发生改变,以及远程桌面连接断开等。下面列举出了Direct2D中常用的独立于设备的资源:
- ID2D1Factory工厂接口,用于新建Direct2D中的资源。
- IDWriteFactory工厂接口,用于创建DirectWrite类的对象。
- ID2D1Geometry接口,绘制弧线、曲线和线段所需的接口。
- ID2D1GeometrySink接口,绘制线段、弧线、三次方和二次方贝塞尔曲线所需的接口。
Direct2D中依赖于设备的资源包括ID2D1Brush接口、ID2D1Layer接口、ID2D1RenderTarget接口和ID2D1DeviceContext接口等,这些资源会在GPU可用的情况下直接映射到GPU上,并且当显示设备发生改变时将会重新创建这些资源。下面列举出了Direct2D中常用的依赖于设备的资源:
- ID2D1SolidColorBrush接口,用于绘制纯色区域的画刷。
- ID2D1Layer接口,实现图层的接口。
- ID2D1RenderTarget接口,用于渲染图形的接口。
- ID2D1DeviceContext接口,继承自ID2D1RenderTarget的接口。
在Visual Staudio 2012中新建一个Visual C++的Windows应用商店的Direct3D应用程序项目,并命名为Direct2DDemo。由于本示例中并没有用到Direct3D应用程序项目默认的绘图代码,读者可以分别右键点击BasicTimer.h、CubeRenderer.cpp、CubeRenderer.h、Direct2DDemo.cpp、Direct2DDemo.h、Direct3DBase.cpp、Direct3DBase.h、SimplePixelShader.hlsl和SimpleVertexShader.hlsl文件,并在弹出的菜单栏中选中"移除",将这些不必要的文件从项目中移除掉,以保证在本示例中所要添加的代码不会和项目中默认的代码产生冲突。
接着打开pch.h头文件,并引用如下的头文件,在使用Direct2D绘制图形的过程中将会用到定义在这些头文件中的类。
#include <wrl.h>
#include <d3d11_1.h>
#include <d2d1_1.h>
#include <d2d1effects.h>
#include <d2d1_1helper.h>
#include <dwrite_1.h>
#include <wincodec.h>
#include <math.h>
#include <agile.h>
引用了上面的头文件以后,接下来在解决方案资源管理器窗口中右键点击项目图标,在弹出的菜单栏中选中"添加", 并在"添加"的子菜单栏中选择"新建项",在出现的"添加新项"窗口中选中"C++ 文件(.cpp)",添加名为"DirectXBase.cpp"的源文件。然后使用同样的方法在"添加新项"窗口中选中"头文件(.h)",添加名为"DirectXBase.h"的头文件。这里新建Direct2D资源的实现代码以及第二部分中用于初始化应用窗口的实现代码将添加在DirectXBase.cpp源文件中,而DirectXBase.h头文件则用于声明在DirectXBase.cpp源文件中使用到的函数和变量。
下面来介绍如何新建Direct2D中独立于设备的资源,首先打开DirectXBase.h头文件,并添加如下代码用于定义一个DirectXBase类。
#include "DirectXHelper.h"
ref class DirectXBase abstract
{
internal:
//声明构造函数DirectXBase
DirectXBase();
};
定义了DirectXBase类以后,接下来在此类中添加如下的代码,用于声明在创建独立于设备的资源的过程中使用到的成员变量和函数。
protected private:
//声明成员变量d2dFactory
Microsoft::WRL::ComPtr<ID2D1Factory1> d2dFactory;
//声明成员变量dwriteFactory
Microsoft::WRL::ComPtr<IDWriteFactory> dwriteFactory;
//声明成员变量wicFactory
Microsoft::WRL::ComPtr<IWICImagingFactory> wicFactory;
internal:
//声明CreateDeviceIndependentResources函数
void CreateDeviceIndependentResources();
在上面的代码中,使用protected private关键字声明三个受保护的私有成员变量,分别为d2dFactory、dwriteFactory和wicFactory,其中d2dFactory为ID2D1Factory1类型的指针,dwriteFactory为IDWriteFactory类型的指针,wicFactory为IWICImagingFactory类型的指针。然后使用internal关键字声明内部访问的CreateDeviceIndependentResources函数,用于新建Direct2D中独立于设备的资源。
声明了上述的成员变量和CreateDeviceIndependentResources函数以后,接下来实现CreateDeviceIndependentResources函数。首先打开DirectXBase.cpp源文件,并添加如下的代码,引用DirectXBase.h头文件和pch.h头文件。
#include "pch.h"
#include "DirectXBase.h"
接下来在DirectXBase.cpp源文件中引用如下的命名空间,新建Direct2D中独立于设备的资源时将会用到定义在这些命名空间中的类。代码如下所示:
using namespace Windows::UI::Core;
using namespace Windows::Foundation;
using namespace Microsoft::WRL;
using namespace Windows::Graphics::Display;
using namespace D2D1;
添加了上述的代码以后,在DirectXBase.cpp源文件中添加CreateDeviceIndependentResources函数的实现代码,具体代码如下所示:
// 新建独立于设备的资源
void DirectXBase::CreateDeviceIndependentResources()
{
//ID2D1Factory的调试级别
D2D1_FACTORY_OPTIONS options;
ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS));
#if defined(_DEBUG)
options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
#endif
//得到ID2D1Factory1类型的对象
DX::ThrowIfFailed(
D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
__uuidof(ID2D1Factory),
&options,
&d2dFactory
)
);
//得到IDWriteFactory1类型的对象
DX::ThrowIfFailed(
DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
&dwriteFactory
)
);
//得到IWICImagingFactory2类型的对象
DX::ThrowIfFailed(
CoCreateInstance(
CLSID_WICImagingFactory,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&wicFactory)
)
);
}
在上面的代码中,首先声明D2D1_FACTORY_OPTIONS结构体的变量options,此结构体变量用于存储ID2D1Factory类的对象的调试级别,并将options结构体变量作为参数传递给ZeroMemory函数,用来为此结构体变量分配内存空间。当程序出现异常时,将options结构体变量的成员debugLevel赋值为D2D1_DEBUG_LEVEL_INFORMATION,设置ID2D1Factory类的对象的调试级别为向调试层发送错误信息、警告和其他有助于提高性能的诊断信息。接着以options结构体变量作为参数调用D2D1CreateFactory函数得到ID2D1Factory1类型的对象,使用d2dFactory指针指向这个对象。接下来调用DWriteCreateFactory函数得到IDWriteFactory1类型的对象,使用dwriteFactory指针指向这个对象。最后调用CoCreateInstance函数得到一个IWICImagingFactory2类型的对象,并使用wicFactory指针指向这个对象。
新建了Direct2D中独立于设备的资源以后,接下来新建Direct2D中依赖于设备的资源。在DirectXBase.h头文件的DirectXBase类中添加如下代码:
protected private:
//声明成员变量d2dDevice
Microsoft::WRL::ComPtr<ID2D1Device> d2dDevice;
//声明成员变量d2dContext
Microsoft::WRL::ComPtr<ID2D1DeviceContext> d2dContext;
//声明成员变量d3dDevice
Microsoft::WRL::ComPtr<ID3D11Device> d3dDevice;
//声明成员变量d3dContext
Microsoft::WRL::ComPtr<ID3D11DeviceContext> d3dContext;
//声明成员变量swapChain
Microsoft::WRL::ComPtr<IDXGISwapChain1> swapChain;
//声明成员变量featureLevel
D3D_FEATURE_LEVEL featureLevel;
internal:
//声明CreateDeviceResources函数
virtual void CreateDeviceResources();
在上面的代码中,使用protected private关键字声明六个受保护的私有成员变量d2dDevice、d2dContext、d3dDevice、d3dContext、swapChain和featureLevel。然后使用internal关键字声明内部访问的CreateDeviceResources函数,用于新建Direct2D中依赖于设备的资源,另外需要注意的是这里使用virtual关键字将CreateDeviceResources函数声明为虚函数,以便于能在DirectXBase类的子类中重写此函数。
添加了上述的代码以后,接下来在DirectXBase.cpp源文件中添加CreateDeviceResources函数的实现代码,具体代码如下所示:
// 以下是依赖设备的资源
void DirectXBase::CreateDeviceResources()
{
// 此标志为与 API 默认设置具有不同颜色渠道顺序的图面添加支持
// 要与 Direct2D 兼容,必须满足此要求
UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
ComPtr<IDXGIDevice> dxgiDevice;
#if defined(_DEBUG)
// 如果项目处于调试生成过程中,请通过带有此标志的 SDK 层启用调试
creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
// DirectX硬件特性级别
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1
};
//得到ID3D11Device和ID3D11DeviceContext类型的对象
DX::ThrowIfFailed(
D3D11CreateDevice(
nullptr,
D3D_DRIVER_TYPE_HARDWARE,
0,
creationFlags,
featureLevels,
ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION,
&d3dDevice,
&featureLevel,
&d3dContext
)
);
DX::ThrowIfFailed(
d3dDevice.As(&dxgiDevice)
);
//得到ID2D1Device类型的对象
DX::ThrowIfFailed(
d2dFactory->CreateDevice(dxgiDevice.Get(), &d2dDevice)
);
DX::ThrowIfFailed(
d2dDevice->CreateDeviceContext(
D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
&d2dContext
)
);
//swapChain赋值为空指针
swapChain = nullptr;
}
在上面的代码中,首先声明UINT类型的变量creationFlags,并将其赋值为D3D11_CREATE_DEVICE_BGRA_SUPPORT用来表示Direct2D可以共享Direct3D中的资源,接着定义D3D_FEATURE_LEVEL类型的数组featureLevels用来储存应用程序支持的DirectX硬件特性级别。然后以creationFlags变量和featureLevels数组作为参数调用D3D11CreateDevice函数,得到ID3D11Device类型的对象和ID3D11DeviceContext类型的对象,使用d3dDevice指针指向ID3D11Device类型的对象,d3dContext指针指向ID3D11DeviceContext类型的对象。
接下来调用d2dFactory指针所指向的对象的CreateDevice函数得到一个ID2D1Device类型的对象,并使用d2dDevice指针指向这个对象。接着调用d2dDevice指针所指向的对象的CreateDeviceContext函数得到一个ID2D1DeviceContext类型的对象,使用d2dContext指针指向这个对象,最后将swapChain指针赋值为空指针。