我是靠谱客的博主 传统狗,最近开发中收集的这篇文章主要介绍Direct2D教程(四)Path Geometry概述使用path geometry的步骤Demo,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

概述

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 geometry
ID2D1PathGeometry * 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 geometry
ID2D1PathGeometry * 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函数,定义如下,该函数有两个参数,第一个是顶点数组,第二个是数组长度。

virtual void 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 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,用来描述圆弧的属性。

virtual void 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函数的第一个参数指定。

virtual void 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教程(四)Path Geometry概述使用path geometry的步骤Demo的全部内容,希望文章能够帮你解决Direct2D教程(四)Path Geometry概述使用path geometry的步骤Demo所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(58)

评论列表共有 0 条评论

立即
投稿
返回
顶部