1、Direct2D教程(一)Direct2D已经来了,谁是GDI的终结者?什么是Direct2D一言以蔽之,就是Windows 7平台上的一个2D图形API,可以提供高性能,高质量的2D渲染。大多数人对Direct2D可能都比较陌生,以至于我之前在论坛上提到这个词的时候,有人竟然说你是不是写错了?可能大家比较熟悉的是Direct3D,因为D3D已经是当前游戏界的主流应用。在过去,3D世界是OpenGL与DirectX二分天下,但是由于OpenGL发展十分滞后,而且没有一个像微软这样强大的后台支柱,所以逐渐被DirectX所超越。回过头来说2D,过去Windows上的2D程序还主要基于GDI和GD
2、I+,遗憾的是这两者都是软件实现,而Direct2D则不同,它基于Direct3D,所以能够使用硬件加速功能,能更大程度的发挥硬件特性,创建高性能,高质量的2D图形,这些图形包括几何图形,如矩形,椭圆等。还有位图,文本。而且更方便的是,Direct2D与GDI,GDI+和D3D都是可以交互的。运行需求要开发和使用Direct2D程序,系统必须是Windows 7,Windows Vista SP2, Windows Server 2008 R2或者Windows Server 2008 SP2,这样的系统需求无疑会稍稍阻碍Direct2D前进的步伐,虽然Vista和Windows 7已经推出有
3、一段时间了,但是不可否认的是,仍然有很多人在使用XP系统。甚至是Win2K。但是新技术的普及是不可阻碍的,相信不久的将来,Direct2D将会成为2D图形世界的主流。D2D的架构Direct2D是基于Direct3D 10.1 API构建的,这意味着Direct2D可以使用硬件加速,下图是Direct2D与Direct3D的一个关系图由上图可以看出,Direct2D还自带了一个软件实现(Software rasterizer),这是因为如果显卡不支持硬件加速,那么Direct2D可以使用软件方式渲染,即使这样,效果还是要优于GDI的。上图中在Direct3D下面还有一个层叫DXGI,其全称是:
4、DirectX Graphics Infrastructure,DXGI从Direct3D 10中才开始存在,主要是为了将一些与3D图形无关的工作从原本的runtime中分离处理,这些工作多是一些底层的操作,比如枚举设备,显示帧缓冲,gamma控制,管理全屏等,在Direct3D 10以前的版本中,这些操作全部集成到了Direct3D的API中,现在分离处理单独成为一个component,就是DXGI。可视效果使用Direct2D可以获得比GDI更加优秀的可视效果,Direct2D支持基于图元的抗锯齿效果,而且完全支持透明和Alpha混合。下图是GDI和Direct2D的一个比较,大家可以看一
5、下,Direct2D的细腻程度可见一斑。可交互性Direct2D与Direct3D,GDI,GDI+都有着良好的交互性,因为他们都支持表面级别的操作,这里的表面在Direct3D中就是surface,而在GDI中则相当于DC(Device Context)。此外Direct2D还能很好的支持DirectWrite,DirectWrite也是DirectX中一个新的Feature,主要用来做文本的绘制。下图是这几者指间的关系期待虽然Direct2D有着明显的优势,但是想取代GDI仍然非短时间可以达到,为了保持向后兼容,微软在短时间内绝不会放弃GDI。如今Direct2D出世已经有一段时间了,GD
6、I还有多少时间?对于Windows平台上的图形世界,DirectX一统江湖的日子还有多远?我们拭目以待!Direct2D教程(二)来看D2D世界中的Hello,World引子任何一门语言的第一个教程几乎都是Hello,world。我们也不例外,但是这里不是教大家打印Hello,world,而是编写一个简单的D2D绘制程序,让大家对Direct2D的程序结构及编程方法有一个基本的认识。下面我们来看如何一步一步绘制一个矩形。基本概念在开始之前,还是先介绍一些基本的概念,有助于大家理解程序,这些概念包括,Brush(画刷),Render target(渲染目标),Geometry(几何图形),它们会
7、贯穿整个教程,所以越早介绍越好,对于有Windows GDI基础的人来说,理解这些概念很容易。没有基础的也没关系,我们可以先了解一下,随着学习的深入,会有更加深刻的认识。BrushBrush-画刷,画刷是绘图的工具,它管理图形的颜色,虚实,画刷可以绘制几何图形,也可以绘制位图。Render targetRender target-渲染目标(姑且这么翻译吧)是绘制的场所,其实这就是一个surface,一个表面,再具体点就是一块内存,可以是显存,也可能是系统内存。所有的绘图操作都在这里完成。Geometry, Bitmap, TextGeometry-几何图形,Bitmap-位图, Text-文本
8、。这三者是要绘制的内容,几何图形包括矩形,圆角矩形,椭圆等,当然Direct2D除了可以绘制几何图形之外,还可绘制位图和文本,D2D没有提供加载位图的接口,所以对位图的加载都是使用WIC来实现的。而对文本的绘制则是通过DirectWrite来实现的,DirectWrite在分类上属于D2D,但是目前已经独立成一个组件了。为了便于理解以上三者之间的关系,大家可以想象一个画家,他在作画的时候,都需要那些东西呢?第一,他需要一支画笔用来绘制,这相当于上面的画刷,第二,他需要一张纸或者一张画布用来承载绘制的东西,这就是上面的Render target,最后,他想画什么呢?山水?花鸟?亦或是人物?这就是
9、绘制的内容,相当于上面的几何图形,位图或者文本。Resource在Direct2D中主要有两种资源,一是设备无关的资源,另一个是设备相关的资源。所谓设备无关,是指该资源不与特定的硬件渲染设备相关联,所以设备无关的资源都分配在CPU中,而设备相关是指该资源与特定的渲染硬件相关联,比如当硬件加速可用时,使用GPU渲染,否则使用CPU渲染。设备无关的资源 ID2D1DrawingStateBlock ID2D1Factory ID2D1Geometry及继承自它的接口 ID2D1GeometrySink和ID2D1SimplifiedGeometrySink ID2D1StrokeStyle除了ID
10、2D1RenderTarget之外,所有使用ID2D1Factory创建的资源都是设备无关的。设备相关的资源 ID2D1Brush及继承自它的接口 ID2D1Layer ID2D1RenderTarget及继承自它的接口一般来说,使用ID2D1RenderTarget创建的资源都是设备相关的。程序框架一个简单的D2D程序大致包含下面三个核心函数。1 创建资源(Create Resources) - 设备无关的资源可以一次性创建,永久使用,而设备相关的资源则需要随着设备改变而相应的改变。2 渲染(Render) - 响应WM_PAINT消息进行绘制。3 清理资源(Cleanup) - DX是基于
11、COM的,所有COM对象在使用完毕时,都要释放。为了便于大家理解,我画了一张图,这个图简单描述了D2D程序的基本渲染流程。需要说明的是,Direct2D的渲染时机与D3D有些不同,D3D是在没有消息处理时进行渲染,而D2D则是响应WM_PAINT消息进行渲染。下面的代码中会有详细的解释。代码添加头文件除了Win32编程需要的头文件(比如Windows.h)之外,任何D2D程序都需要头文件d2d1.h。#include #include 声明全局变量首先我们需要一个ID2D1Factory*类型的对象,也就是D2D工厂接口,这个接口是所有D2D程序的起始点,几乎所有的D2D资源都是由这个接口创建
12、的,其次我们需要一个渲染的场所,也就是Render Target,在D2D中有多种类型的Render Target,这里我们选择ID2D1HwndRenderTarget类型,用来在窗口中进行渲染。最后我们定义一个画刷,用来绘制图形,这里选择固定颜色的画刷,即ID2D1SolidColorBrush。ID2D1Factory* pD2DFactory = NULL ;/ Direct2D factoryID2D1HwndRenderTarget* pRenderTarget = NULL; / Render targetID2D1SolidColorBrush* pBlackBrush = N
13、ULL ; / A black brush, reflect the line colorRECT rc ; / Render areaHWND g_Hwnd ; / Window handle创建D2D工厂接下来创建D2D工厂对象,有了这个对象才能创建后续的资源,这个函数有两个参数,第一个参数是工厂的类型,这里只有单线程和多线程两类,如果是单线程的话,意味着D2D不会为所创建的工厂的对象以及由这个对象创建的子对象提供同步机制,也就是说,如果有多个线程访问了这个资源,那么需要自己提供同步机制。如果是多线程类型,那么D2D会为你提供同步机制。第二个参数用来接收创建的工厂。HRESULT hr ;
14、hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory) ;if (FAILED(hr)MessageBox(hWnd, Create D2D factory failed!, Error, 0) ;return ;创建Render target有了工厂对象以后,开始创建RenderTarget,CreateHwndRenderTarget函数有三个参数,第一个参数是Render target属性,包括渲染模式,象素格式,DPI等,D2D提供了一个函数D2D1:RenderTargetProperties()
15、,可以用来生成默认的属性,我们这里直接使用这个函数。第二个参数是Hwnd类型的Render target属性,它包含三个参数,第一个是窗口句柄,第二个是Render target的大小,第三个参数是Present选项,这个参数有个默认值,这里我们使用它的默认值。CreateHwndRenderTarget函数的最后一个参数用来接收创建的Render target。/ Create a Direct2D render targethr = pD2DFactory-CreateHwndRenderTarget(D2D1:RenderTargetProperties(),D2D1:HwndRende
16、rTargetProperties(hWnd, D2D1:SizeU(rc.right - rc.left,rc.bottom - rc.top), &pRenderTarget) ;if (FAILED(hr)MessageBox(hWnd, Create render target failed!, Error, 0) ;return ;创建画刷有了Render target,再使用函数CreateSolidColorBrush创建画刷,这里创建一个固定颜色的画刷,第一个参数是画刷的颜色,第二个参数接收创建的画刷,画刷的颜色就是绘制线条所用的颜色,比如这里创建一个红色的画刷,那么后面绘制的
17、矩形就是红色的。/ Create a brushhr = pRenderTarget-CreateSolidColorBrush(D2D1:ColorF(D2D1:ColorF:Red),&pBlackBrush) ;if (FAILED(hr)MessageBox(hWnd, Create brush failed!, Error, 0) ;return ;绘制矩形万事俱备,只欠渲染!渲染的代码很简单,首先调用CreateD2DResource函数来创建资源,这是个自定义函数,用来创建资源,比如Render target,画刷之类的,该函数包含了上面提到的代码。绘制的代码要放在BeginDr
18、aw和EndDraw函数之间,调用Clear函数可以将Render target清除为指定的背景色。DrawRectangle函数用来绘制矩形,它有两个参数,第一个是被绘制的矩形,第二个是绘制所用的画刷。函数EndDraw的返回值标识了渲染是否成功。VOID DrawRectangle()CreateD2DResource(g_Hwnd) ;pRenderTarget-BeginDraw() ;/ Clear background color whitepRenderTarget-Clear(D2D1:ColorF(D2D1:ColorF:White);/ Draw RectanglepRen
19、derTarget-DrawRectangle(D2D1:RectF(100.f, 100.f, 500.f, 500.f),pBlackBrush);HRESULT hr = pRenderTarget-EndDraw() ;if (FAILED(hr)MessageBox(NULL, Draw failed!, Error, 0) ;return ;清理资源最后,在程序退出时,清理程序资源,每个COM对象都有一个Release方法,用来释放自己,这里我们定义一个宏来释放COM对象。VOID Cleanup()SAFE_RELEASE(pRenderTarget) ;SAFE_RELEASE
20、(pBlackBrush) ;SAFE_RELEASE(pD2DFactory) ;释放COM对象的宏。#define SAFE_RELEASE(P) if(P)P-Release() ; P = NULL ;效果图如下Happy Coding= THE END =Direct2D教程(三)简单几何图形从本章开始,我们介绍D2D几何图形。D2D图形分类Direct2D支持多种类型的几何图形,包括Simple Geometry(简单几何图形) 矩形 圆角矩形 椭圆Path Geometry(路径图形)Composite Geometry(复合图形) Geometry Group(图形组) Tra
21、nsformed Geometry(变换的图形)各种图形对应的D2D接口如下,所有接口都继承自ID2D1Geometry。 矩形-ID2D1RectangleGeometry 圆角矩形-ID2D1RoundedRectangleGeometry 椭圆-ID2D1EllipseGeometry 路径图形-ID2D1PathGeometry 图形组-ID2D1GeometryGroup 经过变换的图形-ID2D1TransformedGeometry今天我们先来看一下简单几何图形,其他类型的图形将在后续章节中介绍,上面的简单图形有三种,其实还有一种,就是直线,所以D2D目前支持的简单图形有如下四种
22、。 Line - 直线 Rectangle - 矩形 Rounded Rectangle - 圆角矩形 Ellipse - 椭圆下面逐个介绍每种图形的绘制方法,绘制某种图形是通过Render target对象调用相应的绘图函数来完成的,所以下面的代码都假定Render target对象已经创建好了。直线绘制直线使用函数DrawLine,先看一下函数定义,前两个参数分别是直线的起点和终点,第三个参数是画刷。第四个参数是线的宽度,最后一个参数是线的样式,比如实线,虚线或是其他风格的线,你可以使用D2D提供的样式,也可以自己定义样式。由于最后两个参数有默认值,所以我们通常只提供前三个参数即可。最后两
23、个参数在下面的绘图函数中都有出现。virtualvoid DrawLine( D2D1_POINT_2F point0, D2D1_POINT_2F point1, in ID2D1Brush *brush, FLOAT strokeWidth = 1.0f, in, optional ID2D1StrokeStyle *strokeStyle = NULL) = 0;下面的代码绘制一条黑色直线,起点是(100, 100),终点是(500, 500)。pRenderTarget-DrawLine( D2D1:Point2F(100.f, 100.f), D2D1:Point2F(500.f,
24、500.f),pBlackBrush);矩形矩形使用如下的结构体表示。struct D2D_RECT_F FLOAT left; FLOAT top; FLOAT right; FLOAT bottom;绘制矩形使用函数DrawRectangle,定义如下。第一个参数是D2D1_RECT_F结构,表示待绘制的矩形,它其他参数前面已经提到过,不再赘述。void DrawRectangle( ref const D2D1_RECT_F &rect, in ID2D1Brush *brush, FLOAT strokeWidth =1.0f, in, optional ID2D1StrokeStyl
25、e *strokeStyle = NULL);下面的代码绘制一个矩形,该矩形的左上角坐标是(100, 100),右下角坐标是(500, 500)。pRenderTarget-DrawRectangle( D2D1:RectF(100.f, 100.f, 500.f, 500.f), pBlackBrush);圆角矩形D2D支持圆角矩形,先看看什么是圆角矩形。可见圆角矩形就是常规矩形的改版,将尖角用圆弧代替即可。所以一个圆角矩形可以由一个普通矩形加上两个半径构造而成,这两个半径分别表示椭圆的X轴和Y轴,而这个椭圆则用来取代矩形的尖角。圆角矩形的定义如下:第一个参数是一个常规矩形,上面已经介绍过,
26、后两个参数分别是椭圆的X轴半径和Y轴半径。D2D1_ROUNDED_RECT RoundedRect( _in const D2D1_RECT_F &rect, FLOAT radiusX, FLOAT radiusY);绘制圆角矩形的函数定义如下,第一个参数是一个圆角矩形,后面的参数不再赘述。void DrawRoundedRectangle( ref const D2D1_ROUNDED_RECT &roundedRect, in ID2D1Brush *brush, FLOAT strokeWidth =1.0f, in, optional ID2D1StrokeStyle *strok
27、eStyle = NULL);下面的代码绘制一个圆角矩形,其左上角坐标是(100, 100),右下角坐标是(500, 500)。圆角的X轴半径是30,Y轴半径是50。D2D1_ROUNDED_RECT roundedRect = D2D1:RoundedRect( D2D1:RectF(100.f, 100.f, 500.f, 500.f), 30.0f, 50.0f);pRenderTarget-DrawRoundedRectangle(roundedRect, pBlackBrush, 1.0f) ;椭圆椭圆由如下所示的结构体定义,第一个参数是椭圆的中心,后两个参数分别是X轴半径和Y轴半径
28、。struct D2D1_ELLIPSE D2D1_POINT_2F point; FLOAT radiusX; FLOAT radiusY;绘制椭圆的函数定义如下void DrawEllipse( ref const D2D1_ELLIPSE &ellipse, in ID2D1Brush *brush, FLOAT strokeWidth =1.0f, in, optional ID2D1StrokeStyle *strokeStyle = NULL);下面的代码绘制一个椭圆,中心在(100, 100),长轴半径100,短轴半径50。D2D1_ELLIPSE ellipse = D2D1:
29、Ellipse(D2D1:Point2F(100.0f, 100.0f), 100.0f, 50.0f) ;pRenderTarget-DrawEllipse(ellipse, pBlackBrush) ;三角形D2D没有提供绘制三角形的函数,不过我们可以使用DrawLine函数自己实现一个,代码很简单,对于给定的三个顶点,在每两个顶点之间调用上面的DrawLine函数即可。void DrawTriangle(D2D1_POINT_2F p0, D2D1_POINT_2F p1, D2D1_POINT_2F p2, ID2D1HwndRenderTarget* pRenderTarget, I
30、D2D1Brush* brush, FLOAT strokeWidth =1.0f, ID2D1StrokeStyle *strokeStyle = NULL) pRenderTarget-DrawLine(p0, p1, brush); pRenderTarget-DrawLine(p1, p2, brush) ; pRenderTarget-DrawLine(p2, p0, brush) ;圆D2D同样没有提供绘制圆的函数,因为圆是特殊的椭圆,可以通过绘制椭圆的函数来实现,只要使X轴半径和Y轴半径相等即可。void DrawCircle(D2D1_POINT_2F center, FLOA
31、T radius, ID2D1HwndRenderTarget* pRenderTarget, ID2D1Brush* brush, FLOAT strokeWidth =1.0f, ID2D1StrokeStyle *strokeStyle = NULL) D2D1_ELLIPSE ellipse = D2D1:Ellipse(center, radius, radius) ; pRenderTarget-DrawEllipse(ellipse, brush) ;线条的宽度每个DrawXXX函数都有一个参数strokeWidth,代表的是线条的宽度,这个函数有个默认值1.0,所以通常情况下我
32、们无需指定这个参数,但如果想让线条宽一些,那么可以设置成10.0,100.0等等,那么这个宽度是如何增长的呢?会影响图形的大小么?通过实际绘图发现,这个宽度是向外增长的,也就是说线条的宽度不会影响图形的填充区域,比如一个10 x 10的矩形,当线条宽度是1.0时,它的填充面积是100,当线条宽度是10时,它的填充面积还是100。请看下图,黑色是线条颜色,黄色是填充色,可见无论线条多宽,黄色区域的面积始终不变。所以strokeWidth并不影响图形的填充区域。Happy Coding!= THE END =Direct2D教程(四)Path Geometry概述Direct2D支持以下几种类型的
33、几何图形,上一篇介绍了简单几何图形,这篇介绍Path geometry。Simple Geometry(简单几何图形) 矩形 圆角矩形 椭圆Path Geometry(路径图形)Composite Geometry(复合图形) Geometry Group(图形组) Transformed Geometry(变换的图形)Path geometry,说白了,就是以路径来描述图形,由于翻译过来比较别扭,所以下文中出现该词的地方全部使用英文。Path geometry可以用来创建复杂的几何图形,因为无论多么复杂的图形都可以由一些基本的几何图元来表示,Path geometry中可以使用的基本图元包括
34、圆弧,曲线和直线。先来看一个例子。下面这幅图是一座小山,它的轮廓就是由几条直线组成的。再来看一个例子,下面是一个太阳,它由三种图元组成,光芒部分由五条曲线表示,太阳本身由一条弧线和一条直线表示。上面的小山和太阳,都由多个基本图元构成,它们都属于Path geometry,多个Path geometry可以构成更加复杂的图形。使用path geometry的步骤创建Path geometry在D2D中,Path geometry使用接口ID2D1PathGeometry来表示,创建Path geometry使用函数ID2D1Factory:CreatePathGeometry。/ Create
35、path geometryID2D1PathGeometry* pathGeometry = NULL ;hr = g_pD2DFactory-CreatePathGeometry(&pathGeometry) ;if (SUCCEEDED(hr)/ .获取ID2D1GeometrySink对象真正负责创建图形的函数是ID2D1GeometrySink对象,使用Path geometry的Open方法可以获取该对象,使用完毕之后,调用Close函数关闭之。ID2D1GeometrySink *pSink = NULL;hr = pathGeometry-Open(&pSink); / 获取Si
36、nk对象if (SUCCEEDED(hr)/添加图形pSink-Close() ; / 关闭Sink对象使用ID2D1GeometrySink添加图形添加图形都的代码必须放在ID2D1GeometrySink的BeginFigure函数和EndFigure函数之间。可以添加的图形有直线,弧线,二次贝塞尔曲线和三次贝塞尔曲线。BeginFigure函数有两个参数,第一个参数表示图形的起始点,第二个参数表示图形是填充的还是中空的(只有轮廓,无填充色)。D2D1_FIGURE_BEGIN_FILLED表示填充,D2D1_FIGURE_BEGIN_HOLLOW表示中空。pSink-BeginFigur
37、e(D2D1:Point2F(346,255), D2D1_FIGURE_BEGIN_FILLED);/ 添加图形pSink-EndFigure(D2D1_FIGURE_END_CLOSED);所以完整的创建代码应该是下面这个样子,主要包括三部分,创建Path geometry,获取Sink对象,添加图形。/ 创建 path geometryID2D1PathGeometry* pathGeometry = NULL ;hr = g_pD2DFactory-CreatePathGeometry(&pathGeometry) ;if (SUCCEEDED(hr) ID2D1GeometrySin
38、k *pSink = NULL; hr = pathGeometry-Open(&pSink); / 获取Sink对象 if (SUCCEEDED(hr) pSink-BeginFigure(D2D1:Point2F(100,100), D2D1_FIGURE_BEGIN_FILLED); / 添加图形 pSink-EndFigure(D2D1_FIGURE_END_CLOSED); pSink-Close() ; / 关闭Sink对象Demo下面通过一个例子详细讲解如何使用Path geometry,最终的效果图如下,这是D2D SDK中的一个例子。分析一下这幅图,一共有四个独立的图形,两座
39、小山,一个太阳,一条小溪。小山全部由直线构成。太阳由五条曲线和一条弧线构成。小溪由两条曲线构成。我们将每个图形作为一个独立的path geometry,单独创建。创建小山上面说了,小山都由直线构成,所以我们只需给出小山的顶点,在这些顶点之间连线即可,连线可以使用sink对象的AddLines函数,定义如下,该函数有两个参数,第一个是顶点数组,第二个是数组长度。virtualvoid AddLines( in const D2D1_POINT_2F *points, UINT pointsCount) =0;创建小山的代码如下,这里只给出左边的小山,右边的小山创建方法相同,只是多了几个顶点而已。
40、hr = g_pD2DFactory-CreatePathGeometry(&g_pLeftMountainGeometry) ;if (SUCCEEDED(hr) ID2D1GeometrySink *pSink = NULL;hr= g_pLeftMountainGeometry-Open(&pSink);if (SUCCEEDED(hr) pSink-SetFillMode(D2D1_FILL_MODE_WINDING);pSink-BeginFigure( D2D1:Point2F(346,255), D2D1_FIGURE_BEGIN_FILLED ); D2D1_POINT_2F
41、points5 = D2D1:Point2F(267, 177), D2D1:Point2F(236, 192), D2D1:Point2F(212, 160), D2D1:Point2F(156, 255), D2D1:Point2F(346, 255), ;pSink-AddLines(points, ARRAYSIZE(points);pSink-EndFigure(D2D1_FIGURE_END_CLOSED); pSink-Close() ; SAFE_RELEASE(pSink) ;创建太阳太阳由圆弧和贝塞尔曲线构成,创建圆弧使用函数AddArc,它的定义如下。只有一个参数D2D1
42、_ARC_SEGMENT,用来描述圆弧的属性。virtualvoid AddArc( in D2D1_ARC_SEGMENT *arc) =0;D2D1_ARC_SEGMENT的定义如下,第一个参数表示弧的终点,第二个参数size表示弧的x轴和y轴,第三个参数表示弧的旋转角度,按顺时针方向计算,第四个参数指出了扫描方向,顺时针或者逆时针。最后一个参数指出了弧的大小是否超过180度。struct D2D1_ARC_SEGMENT D2D1_POINT_2F point; D2D1_SIZE_F size; FLOAT rotationAngle; D2D1_SWEEP_DIRECTION swe
43、epDirection; D2D1_ARC_SIZE arcSize;可能有人会问,既然是描述两点之间的弧,为什么参数中只有终点呢?起点在哪里?因为像弧线,贝塞尔曲线这类图形,通常是为了和其他图形进行拼接的,所以起点就是上次绘制的终点。如果是第一次绘制,也就是没有点可接,那么起点就有BeginFigure函数的第一个参数指定。virtualvoid AddBezier( in const D2D1_BEZIER_SEGMENT *bezier) =0;D2D1_BEZIER_SEGMENT的定义如下,注意这里实际上省略了一个参数,就是曲线的起点。因为这个函数默认起点就是该曲线要添加到的点。所以
44、这里point1和point2是控制点,而point3是曲线的终点。struct D2D1_BEZIER_SEGMENT D2D1_POINT_2F point1; D2D1_POINT_2F point2; D2D1_POINT_2F point3;绘制太阳的代码如下,注意这里我们只创建了一条光芒曲线,由于每一条光芒曲线由两条贝塞尔曲线拼接而成,如果全部绘制出来,需要十次AddBezier函数调用,为了简化代码,省略了其他四条曲线的创建。pSink-BeginFigure( D2D1:Point2F(270, 255), D2D1_FIGURE_BEGIN_FILLED );/ 太阳顶部圆弧pSink-AddArc( D2D1:ArcSegment( D2D1:Point2F(440, 255), / end point D2D1:SizeF(85, 85),0.0f, / rotation angle D2D1_SWEEP_DIRECTION_CLOCKWISE, D2D1_ARC_SIZE_SMALL ); pSink-EndFigure(D2D1_FIGURE_END_CLOSED);/ 太阳光芒曲线pSink-BeginFigure( D2D1:Poi