Direct2D教程

上传人:y****3 文档编号:12830177 上传时间:2020-05-30 格式:DOC 页数:94 大小:1.88MB
返回 下载 相关 举报
Direct2D教程_第1页
第1页 / 共94页
Direct2D教程_第2页
第2页 / 共94页
Direct2D教程_第3页
第3页 / 共94页
点击查看更多>>
资源描述
.Direct2D教程(一)Direct2D已经来了,谁是GDI的终结者?什么是Direct2D一言以蔽之,就是Windows 7平台上的一个2D图形API,可以提供高性能,高质量的2D渲染。大多数人对Direct2D可能都比较陌生,以至于我之前在论坛上提到这个词的时候,有人竟然说你是不是写错了?可能大家比较熟悉的是Direct3D,因为D3D已经是当前游戏界的主流应用。在过去,3D世界是OpenGL与DirectX二分天下,但是由于OpenGL发展十分滞后,而且没有一个像微软这样强大的后台支柱,所以逐渐被DirectX所超越。回过头来说2D,过去Windows上的2D程序还主要基于GDI和GDI+,遗憾的是这两者都是软件实现,而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已经推出有一段时间了,但是不可否认的是,仍然有很多人在使用XP系统。甚至是Win2K。但是新技术的普及是不可阻碍的,相信不久的将来,Direct2D将会成为2D图形世界的主流。D2D的架构Direct2D是基于Direct3D 10.1 API构建的,这意味着Direct2D可以使用硬件加速,下图是Direct2D与Direct3D的一个关系图由上图可以看出,Direct2D还自带了一个软件实现(Software rasterizer),这是因为如果显卡不支持硬件加速,那么Direct2D可以使用软件方式渲染,即使这样,效果还是要优于GDI的。上图中在Direct3D下面还有一个层叫DXGI,其全称是:DirectX Graphics Infrastructure,DXGI从Direct3D 10中才开始存在,主要是为了将一些与3D图形无关的工作从原本的runtime中分离处理,这些工作多是一些底层的操作,比如枚举设备,显示帧缓冲,gamma控制,管理全屏等,在Direct3D 10以前的版本中,这些操作全部集成到了Direct3D的API中,现在分离处理单独成为一个component,就是DXGI。可视效果使用Direct2D可以获得比GDI更加优秀的可视效果,Direct2D支持基于图元的抗锯齿效果,而且完全支持透明和Alpha混合。下图是GDI和Direct2D的一个比较,大家可以看一下,Direct2D的细腻程度可见一斑。可交互性Direct2D与Direct3D,GDI,GDI+都有着良好的交互性,因为他们都支持表面级别的操作,这里的表面在Direct3D中就是surface,而在GDI中则相当于DC(Device Context)。此外Direct2D还能很好的支持DirectWrite,DirectWrite也是DirectX中一个新的Feature,主要用来做文本的绘制。下图是这几者指间的关系期待虽然Direct2D有着明显的优势,但是想取代GDI仍然非短时间可以达到,为了保持向后兼容,微软在短时间内绝不会放弃GDI。如今Direct2D出世已经有一段时间了,GDI还有多少时间?对于Windows平台上的图形世界,DirectX一统江湖的日子还有多远?我们拭目以待!Direct2D教程(二)来看D2D世界中的Hello,World引子任何一门语言的第一个教程几乎都是Hello,world。我们也不例外,但是这里不是教大家打印Hello,world,而是编写一个简单的D2D绘制程序,让大家对Direct2D的程序结构及编程方法有一个基本的认识。下面我们来看如何一步一步绘制一个矩形。基本概念在开始之前,还是先介绍一些基本的概念,有助于大家理解程序,这些概念包括,Brush(画刷),Render target(渲染目标),Geometry(几何图形),它们会贯穿整个教程,所以越早介绍越好,对于有Windows GDI基础的人来说,理解这些概念很容易。没有基础的也没关系,我们可以先了解一下,随着学习的深入,会有更加深刻的认识。BrushBrush-画刷,画刷是绘图的工具,它管理图形的颜色,虚实,画刷可以绘制几何图形,也可以绘制位图。Render targetRender target-渲染目标(姑且这么翻译吧)是绘制的场所,其实这就是一个surface,一个表面,再具体点就是一块内存,可以是显存,也可能是系统内存。所有的绘图操作都在这里完成。Geometry, Bitmap, TextGeometry-几何图形,Bitmap-位图, Text-文本。这三者是要绘制的内容,几何图形包括矩形,圆角矩形,椭圆等,当然Direct2D除了可以绘制几何图形之外,还可绘制位图和文本,D2D没有提供加载位图的接口,所以对位图的加载都是使用WIC来实现的。而对文本的绘制则是通过DirectWrite来实现的,DirectWrite在分类上属于D2D,但是目前已经独立成一个组件了。为了便于理解以上三者之间的关系,大家可以想象一个画家,他在作画的时候,都需要那些东西呢?第一,他需要一支画笔用来绘制,这相当于上面的画刷,第二,他需要一张纸或者一张画布用来承载绘制的东西,这就是上面的Render target,最后,他想画什么呢?山水?花鸟?亦或是人物?这就是绘制的内容,相当于上面的几何图形,位图或者文本。Resource在Direct2D中主要有两种资源,一是设备无关的资源,另一个是设备相关的资源。所谓设备无关,是指该资源不与特定的硬件渲染设备相关联,所以设备无关的资源都分配在CPU中,而设备相关是指该资源与特定的渲染硬件相关联,比如当硬件加速可用时,使用GPU渲染,否则使用CPU渲染。设备无关的资源 ID2D1DrawingStateBlock ID2D1Factory ID2D1Geometry及继承自它的接口 ID2D1GeometrySink和ID2D1SimplifiedGeometrySink ID2D1StrokeStyle除了ID2D1RenderTarget之外,所有使用ID2D1Factory创建的资源都是设备无关的。设备相关的资源 ID2D1Brush及继承自它的接口 ID2D1Layer ID2D1RenderTarget及继承自它的接口一般来说,使用ID2D1RenderTarget创建的资源都是设备相关的。程序框架一个简单的D2D程序大致包含下面三个核心函数。1 创建资源(Create Resources) - 设备无关的资源可以一次性创建,永久使用,而设备相关的资源则需要随着设备改变而相应的改变。2 渲染(Render) - 响应WM_PAINT消息进行绘制。3 清理资源(Cleanup) - DX是基于COM的,所有COM对象在使用完毕时,都要释放。为了便于大家理解,我画了一张图,这个图简单描述了D2D程序的基本渲染流程。需要说明的是,Direct2D的渲染时机与D3D有些不同,D3D是在没有消息处理时进行渲染,而D2D则是响应WM_PAINT消息进行渲染。下面的代码中会有详细的解释。代码添加头文件除了Win32编程需要的头文件(比如Windows.h)之外,任何D2D程序都需要头文件d2d1.h。#include #include 声明全局变量首先我们需要一个ID2D1Factory*类型的对象,也就是D2D工厂接口,这个接口是所有D2D程序的起始点,几乎所有的D2D资源都是由这个接口创建的,其次我们需要一个渲染的场所,也就是Render Target,在D2D中有多种类型的Render Target,这里我们选择ID2D1HwndRenderTarget类型,用来在窗口中进行渲染。最后我们定义一个画刷,用来绘制图形,这里选择固定颜色的画刷,即ID2D1SolidColorBrush。ID2D1Factory* pD2DFactory = NULL ;/ Direct2D factoryID2D1HwndRenderTarget* pRenderTarget = NULL; / Render targetID2D1SolidColorBrush* pBlackBrush = NULL ; / A black brush, reflect the line colorRECT rc ; / Render areaHWND g_Hwnd ; / Window handle创建D2D工厂接下来创建D2D工厂对象,有了这个对象才能创建后续的资源,这个函数有两个参数,第一个参数是工厂的类型,这里只有单线程和多线程两类,如果是单线程的话,意味着D2D不会为所创建的工厂的对象以及由这个对象创建的子对象提供同步机制,也就是说,如果有多个线程访问了这个资源,那么需要自己提供同步机制。如果是多线程类型,那么D2D会为你提供同步机制。第二个参数用来接收创建的工厂。HRESULT hr ;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(),可以用来生成默认的属性,我们这里直接使用这个函数。第二个参数是Hwnd类型的Render target属性,它包含三个参数,第一个是窗口句柄,第二个是Render target的大小,第三个参数是Present选项,这个参数有个默认值,这里我们使用它的默认值。CreateHwndRenderTarget函数的最后一个参数用来接收创建的Render target。/ Create a Direct2D render targethr = pD2DFactory-CreateHwndRenderTarget(D2D1:RenderTargetProperties(),D2D1:HwndRenderTargetProperties(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创建画刷,这里创建一个固定颜色的画刷,第一个参数是画刷的颜色,第二个参数接收创建的画刷,画刷的颜色就是绘制线条所用的颜色,比如这里创建一个红色的画刷,那么后面绘制的矩形就是红色的。/ 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,画刷之类的,该函数包含了上面提到的代码。绘制的代码要放在BeginDraw和EndDraw函数之间,调用Clear函数可以将Render target清除为指定的背景色。DrawRectangle函数用来绘制矩形,它有两个参数,第一个是被绘制的矩形,第二个是绘制所用的画刷。函数EndDraw的返回值标识了渲染是否成功。VOID DrawRectangle()CreateD2DResource(g_Hwnd) ;pRenderTarget-BeginDraw() ;/ Clear background color whitepRenderTarget-Clear(D2D1:ColorF(D2D1:ColorF:White);/ Draw RectanglepRenderTarget-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(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(图形组) Transformed Geometry(变换的图形)各种图形对应的D2D接口如下,所有接口都继承自ID2D1Geometry。 矩形-ID2D1RectangleGeometry 圆角矩形-ID2D1RoundedRectangleGeometry 椭圆-ID2D1EllipseGeometry 路径图形-ID2D1PathGeometry 图形组-ID2D1GeometryGroup 经过变换的图形-ID2D1TransformedGeometry今天我们先来看一下简单几何图形,其他类型的图形将在后续章节中介绍,上面的简单图形有三种,其实还有一种,就是直线,所以D2D目前支持的简单图形有如下四种。 Line - 直线 Rectangle - 矩形 Rounded Rectangle - 圆角矩形 Ellipse - 椭圆下面逐个介绍每种图形的绘制方法,绘制某种图形是通过Render target对象调用相应的绘图函数来完成的,所以下面的代码都假定Render target对象已经创建好了。直线绘制直线使用函数DrawLine,先看一下函数定义,前两个参数分别是直线的起点和终点,第三个参数是画刷。第四个参数是线的宽度,最后一个参数是线的样式,比如实线,虚线或是其他风格的线,你可以使用D2D提供的样式,也可以自己定义样式。由于最后两个参数有默认值,所以我们通常只提供前三个参数即可。最后两个参数在下面的绘图函数中都有出现。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, 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 ID2D1StrokeStyle *strokeStyle = NULL);下面的代码绘制一个矩形,该矩形的左上角坐标是(100, 100),右下角坐标是(500, 500)。pRenderTarget-DrawRectangle( D2D1:RectF(100.f, 100.f, 500.f, 500.f), pBlackBrush);圆角矩形D2D支持圆角矩形,先看看什么是圆角矩形。可见圆角矩形就是常规矩形的改版,将尖角用圆弧代替即可。所以一个圆角矩形可以由一个普通矩形加上两个半径构造而成,这两个半径分别表示椭圆的X轴和Y轴,而这个椭圆则用来取代矩形的尖角。圆角矩形的定义如下:第一个参数是一个常规矩形,上面已经介绍过,后两个参数分别是椭圆的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 *strokeStyle = 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轴半径。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: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, ID2D1Brush* 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, FLOAT 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,所以通常情况下我们无需指定这个参数,但如果想让线条宽一些,那么可以设置成10.0,100.0等等,那么这个宽度是如何增长的呢?会影响图形的大小么?通过实际绘图发现,这个宽度是向外增长的,也就是说线条的宽度不会影响图形的填充区域,比如一个10 x 10的矩形,当线条宽度是1.0时,它的填充面积是100,当线条宽度是10时,它的填充面积还是100。请看下图,黑色是线条颜色,黄色是填充色,可见无论线条多宽,黄色区域的面积始终不变。所以strokeWidth并不影响图形的填充区域。Happy Coding!= THE END =Direct2D教程(四)Path Geometry概述Direct2D支持以下几种类型的几何图形,上一篇介绍了简单几何图形,这篇介绍Path geometry。Simple Geometry(简单几何图形) 矩形 圆角矩形 椭圆Path Geometry(路径图形)Composite Geometry(复合图形) Geometry Group(图形组) Transformed Geometry(变换的图形)Path geometry,说白了,就是以路径来描述图形,由于翻译过来比较别扭,所以下文中出现该词的地方全部使用英文。Path geometry可以用来创建复杂的几何图形,因为无论多么复杂的图形都可以由一些基本的几何图元来表示,Path geometry中可以使用的基本图元包括圆弧,曲线和直线。先来看一个例子。下面这幅图是一座小山,它的轮廓就是由几条直线组成的。再来看一个例子,下面是一个太阳,它由三种图元组成,光芒部分由五条曲线表示,太阳本身由一条弧线和一条直线表示。上面的小山和太阳,都由多个基本图元构成,它们都属于Path geometry,多个Path geometry可以构成更加复杂的图形。使用path geometry的步骤创建Path geometry在D2D中,Path geometry使用接口ID2D1PathGeometry来表示,创建Path geometry使用函数ID2D1Factory:CreatePathGeometry。/ Create 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); / 获取Sink对象if (SUCCEEDED(hr)/添加图形pSink-Close() ; / 关闭Sink对象使用ID2D1GeometrySink添加图形添加图形都的代码必须放在ID2D1GeometrySink的BeginFigure函数和EndFigure函数之间。可以添加的图形有直线,弧线,二次贝塞尔曲线和三次贝塞尔曲线。BeginFigure函数有两个参数,第一个参数表示图形的起始点,第二个参数表示图形是填充的还是中空的(只有轮廓,无填充色)。D2D1_FIGURE_BEGIN_FILLED表示填充,D2D1_FIGURE_BEGIN_HOLLOW表示中空。pSink-BeginFigure(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) ID2D1GeometrySink *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中的一个例子。分析一下这幅图,一共有四个独立的图形,两座小山,一个太阳,一条小溪。小山全部由直线构成。太阳由五条曲线和一条弧线构成。小溪由两条曲线构成。我们将每个图形作为一个独立的path geometry,单独创建。创建小山上面说了,小山都由直线构成,所以我们只需给出小山的顶点,在这些顶点之间连线即可,连线可以使用sink对象的AddLines函数,定义如下,该函数有两个参数,第一个是顶点数组,第二个是数组长度。virtualvoid AddLines( in const D2D1_POINT_2F *points, UINT pointsCount) =0;创建小山的代码如下,这里只给出左边的小山,右边的小山创建方法相同,只是多了几个顶点而已。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 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_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 sweepDirection; D2D1_ARC_SIZE arcSize;可能有人会问,既然是描述两点之间的弧,为什么参数中只有终点呢?起点在哪里?因为像弧线,贝塞尔曲线这类图形,通常是为了和其他图形进行拼接的,所以起点就是上次绘制的终点。如果是第一次绘制,也就是没有点可接,那么起点就有BeginFigure函数的第一个参数指定。virtualvoid AddBezier( in const D2D1_BEZIER_SEGMENT *bezier) =0;D2D1_BEZIER_SEGMENT的定义如下,注意这里实际上省略了一个参数,就是曲线的起点。因为这个函数默认起点就是该曲线要添加到的点。所以这里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:Point2F(299, 182), D2D1_FIGURE_BEGIN_HOLLOW );pSink-AddBezier( D2D1:BezierSegment( D2D1:Point2F(299, 182), D2D1:Point2F(294, 176), D2D1:Point2F(285, 178) );pSink-AddBezier( D2D1:BezierSegment( D2D1:Point2F(276, 179), D2D1:Point2F(272, 173), D2D1:Point2F(272, 173) );pSink-EndFigure(D2D1_FIGURE_END_OPEN);创建小溪小溪的两个边缘对应两条曲线,每条曲线又由两条贝塞尔曲线构成。上面已经提到如何绘制贝塞尔曲线,代码略。绘制场景上面的代码是创建场景,下面开始绘制。绘制分两步完成,第一步先画出图形的轮廓,第二步进行颜色填充。以绘制小山为例,代码如下。VOID DrawPathGeometry()CreateD2DResource(g_Hwnd) ; g_pRenderTarget-BeginDraw() ; g_pRenderTarget-Clear(D2D1:ColorF(D2D1:ColorF:White);/ 轮廓-黑色 g_pSceneBrush-SetColor(D2D1:ColorF(D2D1:ColorF:Black, 1.f); g_pRenderTarget-DrawGeometry(g_pLeftMountainGeometry, g_pSceneBrush, 1.f);/ 填充-绿色 g_pSceneBrush-SetColor(D2D1:ColorF(D2D1:ColorF:OliveDrab, 1.f); g_pRenderTarget-FillGeometry(g_pLeftMountainGeometry, g_pSceneBrush); HRESULT hr = g_pRenderTarget-EndDraw() ;if (FAILED(hr) MessageBox(NULL, Draw failed!, Error, 0) ;return ; Happy Coding!= THE END =Direct2D教程(五)复合图形概述Direct2D支持以下几种类型的几何图形。Simple Geometry(简单几何图形) 矩形 圆角矩形 椭圆Path Geometry(路径图形)Composite Geometry(复合图形) Geometry Group(图形组) Transformed Geometry(变换的图形)上一篇介绍了Path geometry,这篇介绍复合图形。复合图形也可以叫做合成图形,包含两种,一种是图形组,即由多个图形组成的一组图形,另一种是经过变换的图形,D2D支持的变换有四种,平移,旋转,缩放和倾斜。图形组由于图形组是一组图形的集合,所以如果对图形组进行操作,会影响到其中每一个图形,这对批量操作图形是很方便的。图形组在D2D中用接口ID2D1GeometryGroup来表示。创建图形组使用函数ID2D1Factory:CreateGeometryGroup,它的定义如下virtual HRESULT CreateGeometryGroup( D2D1_FILL_MODE fillMode, in ID2D1Geometry *geometries, UINT geometriesCount, out ID2D1GeometryGroup *geometryGroup) =0;第一个参数表示填充模式,可以是D2D1_FILL_MODE_ALTERNATE或者D2D1_FILL_MODE_WINDING,关于这两者的详细说明,后面有述。第二个参数是一个类型为ID2D1Geometry的数组,这个数组中包含了所有将被放入图形组的图形,第三个参数是数组中元素的个数,最后一个参数用来接收创建后的ID2D1GeometryGroup。 使用GeometryGroup的步骤如下。创建图形组中的所有图形这里我们创建四个同心圆,为了简化程序,只给出第一个圆的创建代码。创建其他的圆只需更改一下半径即可。const D2D1_ELLIPSE ellipse1 = D2D1:Ellipse( D2D1:Point2F(105.0f, 105.0f), 25.0f, 25.0f);hr = pD2DFactory-CreateEllipseGeometry( ellipse1, &pEllipseGeometry1);创建图形组先将所有的图形放到一个数组里面ID2D1Geometry *ppGeometries = pEllipseGeometry1, pEllipseGeometry2, pEllipseGeometry3, pEllipseGeometry4;然后使用CreateGeometryGroup来创建图形组。hr = pD2DFactory-CreateGeometryGroup( D2D1_FILL_MODE_ALTERNATE, ppGeometries, ARRAYSIZE(ppGeometries), &pGeoGroup_AlternateFill);使用图形组进行绘制对图形组的绘制,就是对组中所有的图形进行绘制。pRenderTarget-FillGeometry(pGeoGroup_AlternateFill, pFillBrush);pRenderTarget-DrawGeometry(pGeoGroup_AlternateFill, pStrokeBrush, 1.0f);D2D1_FILL_MODE_ALTERNATE和D2D1_FILL_MODE_WINDING这两个模式的区别见下图,两者线条的颜色都是一样的,只是填充色一个是交替模式,一个是统一模式。变换图形在2D中变换图形主要有两种方式,一是对图形所在的Render target进行变换,二是对图形本身进行变换。这两者是有区别的,对render target进行变换将影响到render target上所有的图形,而对图形本身进行变换则只影响图形自己,而且这种方式只改变图形的填充,而不改变其轮廓(也即line width)。这里我们主要介绍如何对图形本身进行变换。函数CreateTransformedGeometry可以创建一个变换后的图形。该函数定义如下virtual HRESULT CreateTransformedGeometry( in ID2D1Geometry *sourceGeometry, in, optional const D2D1_MATRIX_3X2_F *transform, out ID2D1TransformedGeometry *transformedGeometry) =0;第一个参数sourceGeometry表示待变换的图形,第二个参数表示变换用的矩阵,最后一个参数用来存放变换后的结果。举个例子,下面的代码将pPathGeometry沿X轴和Y轴分别平移100个单位距离。平移后的图形保存在pTransformedGeometry中。hr = pD2DFactory-CreateTransformedGeometry( pPathGeometry, D2D1:Matrix3x2F:Translation(100, 100), &pTransformedGeometry);前面说了对图形本身进行变换不影响线条的宽度,而对render target进行变换则会影响,来看一下两者的区别,我们以缩放变换为例。变换render target/ Transform the render target, then draw the rectangle geometry again.m_pRenderTarget-SetTransform( D2D1:Matrix3x2F:Scale( D2D1:SizeF(3.f, 3.f), D2D1:Point2F(175.f, 175.f);m_pRenderTarget-DrawGeometry(m_pRectangleGeometry, m_pBlackBrush, 1);效果图如下,注意,矩形的线条变宽了。变换图形本身hr = m_pD2DFactory-CreateTransformedGeometry( m_pRectangleGeometry, D2D1:Matrix3x2F:Scale( D2D1:SizeF(3.f, 3.f), D2D1:Point2F(175.f, 175.f), &m_pTransformedGeometry);/ Replace the previous render target transform.m_pRenderTarget-SetTransform(D2D1:Matrix3x2F:Identity();/ Draw the transformed geometry.m_pRenderTarget-DrawGeometry(m_pTransformedGeometry, m_pBlackBrush, 1);效果图如下,注意,矩形的线条保持原样。Happy Coding!= THE END =Direct2D教程(六)图形也能做运算概述大家都学过集合运算,给定两个集合,可以求他们的并集,交集,差集等。其实图形之间也能做运算,今天就带大家开始图形运算之旅,讲讲如何合并图形。在D2D中有四种方法合并图形,分别是并(UNION),交(INTERSECT),差(EXCLUDE)和异或(XOR)。那么图形之间是如何合并的呢?两个图形之间进行Xor的结果是什么呢?为了便于理解,我先把效果图贴上来。下面图片中第一副图示两个圆的原始图,第二副图是UNION的结果,取两个圆所有的部分,但是公共部分只保留一份。第三幅图是INTERSECT的结果,取两个圆的公共部分。第四幅图是XOR的结果,取两个圆公共部分以外的部分。最后一幅图是EXCLUDE的结果,这个相当于集合的减法运算,在一个圆中去除两个圆的公共部分。合并图形的基本步骤图形的合并要经历以下几个步骤,合并后的图形是存放在path geometry中的。 创建待合并的图形 创建path geometry 获取path geometry中的sink对象 调用CombineWi
展开阅读全文
相关资源
相关搜索

当前位置:首页 > 临时分类 > 职业技能


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!