Direct2D教程(四)Path Geometry

摘要:
概述Direct2D支持以下几种类型的几何图形,上一篇介绍了简单几何图形,这篇介绍Pathgeometry。上面的小山和太阳,都由多个基本图元构成,它们都属于Pathgeometry,多个Pathgeometry可以构成更加复杂的图形。使用pathgeometry的步骤创建Pathgeometry在D2D中,Pathgeometry使用接口ID2D1PathGeometry来表示,创建Pathgeometry使用函数ID2D1Factory::CreatePathGeometry。//CreatepathgeometryID2D1PathGeometry*pathGeometry=NULL;hr=g_pD2DFactory-˃CreatePathGeometry;if{//...}获取ID2D1GeometrySink对象真正负责创建图形的函数是ID2D1GeometrySink对象,使用Pathgeometry的Open方法可以获取该对象,使用完毕之后,调用Close函数关闭之。我们将每个图形作为一个独立的pathgeometry,单独创建。
概述

Direct2D支持以下几种类型的几何图形,上一篇介绍了简单几何图形,这篇介绍Path geometry。
Simple Geometry(简单几何图形)

  • 矩形
  • 圆角矩形
  • 椭圆

Path Geometry(路径图形)
Composite Geometry(复合图形)

  • Geometry Group(图形组)
  • Transformed Geometry(变换的图形)

Path geometry,说白了,就是以路径来描述图形,由于翻译过来比较别扭,所以下文中出现该词的地方全部使用英文。Path geometry可以用来创建复杂的几何图形,因为无论多么复杂的图形都可以由一些基本的几何图元来表示,Path geometry中可以使用的基本图元包括圆弧,曲线和直线。先来看一个例子。下面这幅图是一座小山,它的轮廓就是由几条直线组成的。

Direct2D教程(四)Path Geometry第1张

再来看一个例子,下面是一个太阳,它由三种图元组成,光芒部分由五条曲线表示,太阳本身由一条弧线和一条直线表示。

Direct2D教程(四)Path Geometry第2张

上面的小山和太阳,都由多个基本图元构成,它们都属于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中的一个例子。

Direct2D教程(四)Path Geometry第3张

分析一下这幅图,一共有四个独立的图形,两座小山,一个太阳,一条小溪。小山全部由直线构成。太阳由五条曲线和一条弧线构成。小溪由两条曲线构成。我们将每个图形作为一个独立的path geometry,单独创建。

创建小山

上面说了,小山都由直线构成,所以我们只需给出小山的顶点,在这些顶点之间连线即可,连线可以使用sink对象的AddLines函数,定义如下,该函数有两个参数,第一个是顶点数组,第二个是数组长度。

virtualvoidAddLines(
[
in] constD2D1_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 points[5] ={
            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,用来描述圆弧的属性。

virtualvoidAddArc(
[
in] D2D1_ARC_SEGMENT *arc
)
=0;

D2D1_ARC_SEGMENT的定义如下,第一个参数表示弧的终点,第二个参数size表示弧的x轴和y轴,第三个参数表示弧的旋转角度,按顺时针方向计算,第四个参数指出了扫描方向,顺时针或者逆时针。最后一个参数指出了弧的大小是否超过180度。

structD2D1_ARC_SEGMENT {
D2D1_POINT_2F point;
D2D1_SIZE_F size;
FLOAT rotationAngle;
D2D1_SWEEP_DIRECTION sweepDirection;
D2D1_ARC_SIZE arcSize;
};

可能有人会问,既然是描述两点之间的弧,为什么参数中只有终点呢?起点在哪里?因为像弧线,贝塞尔曲线这类图形,通常是为了和其他图形进行拼接的,所以起点就是上次绘制的终点。如果是第一次绘制,也就是没有点可接,那么起点就有BeginFigure函数的第一个参数指定。

virtualvoidAddBezier(
[
in] constD2D1_BEZIER_SEGMENT *bezier
)
=0;

D2D1_BEZIER_SEGMENT的定义如下,注意这里实际上省略了一个参数,就是曲线的起点。因为这个函数默认起点就是该曲线要添加到的点。所以这里point1和point2是控制点,而point3是曲线的终点。

structD2D1_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教程(四)Path Geometry》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇正则表达式-1Elastic search 基本使用下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

随便看看

PCL点云分割(2)

点云的分割是我想做的机器人手臂捕捉的一个非常重要的部分,因此首先要了解,如果我使用点云库来处理我用kinect获得的点云数据,这个例程也是由我自己慢慢修改程序并结合官方API的解释来实现的。如果我直接更改源程序,由于数据类型、头文件和其他原因,其中的许多细节可能无法编译,我们将很难找出错误。首先,让我们看看我自己设定的场景。然后我使用Kinect获取数据并观...

浅谈 SQL 注入(注入篇)

1、 SQL注入1.1简介什么是SQL注入?它不过滤用户可以严格控制或没有限制的参数,以便用户可以将传入的参数和SQL语句组合成SQL语句,然后将其传输到web服务器。最后,它被传输到数据库以执行添加、删除、修改和查询等操作。基于此,用户可以获取数据库数据或提高其销毁数据库数据的权限。...

【JVM】元空间详解 Metaspace

nocs。JpgNoKlassisMetaspaceNoKlassinMetaspaces专用于存储其他与klass相关的内容,如方法、常量池等。它可以由多个不连续的存储器组成。在元空间GC之后,还将调整阈值。默认情况下,MaxMetaspaceSize基本上是无限的,因为大多数元空间都是在本地内存中分配的,但它仍然受到本地内存大小的限制。为了防止元空间的无...

如何在Android模拟器上安装apk文件

如本实例的“mishop_2.0.20130911_1.1.1.apk”3.执行控制台命令,进行安装。切换到D盘,输入D:,然后点击Enter,即切换到D盘,输入cd,找到platform-tools的文件地址,即adb.exe的文件路径。,粘贴在控制台中。...

Matlab自定义函数的五种方法 [转]

子函数lfg2只能被主函数和主函数中的其他子函数调用。特点是,它是基于Matlab的数值运算内核的,所以它的运算速度较快,程序效率更高。...

mac 安装xcode命令行工具

重印:https://segmentfault.com/a/1190000018045211?utm_source=tag-Newest1.启动终端,输入命令:xcode select--install,然后一直单击install。2.安装成功后,输入命令:gcc-v以检查是否成功。如果在第一步中报告了错误,提示为:xcode select:error:co...