zoukankan      html  css  js  c++  java
  • 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 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函数,定义如下,该函数有两个参数,第一个是顶点数组,第二个是数组长度。

    virtualvoid AddLines(

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

    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(

    [inconst 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 ==

    源文档 <http://www.cnblogs.com/graphics/archive/2011/05/27/2057759.html

  • 相关阅读:
    彻底理解ThreadLocal(转)
    javascript中神奇的(+)加操作符
    quartz集群调度机制调研及源码分析---转载
    quartz源码解析--转
    通过Spring @PostConstruct 和 @PreDestroy 方法 实现初始化和销毁bean之前进行的操作
    LeetCode 917. Reverse Only Letters (仅仅反转字母)
    LeetCode 893. Groups of Special-Equivalent Strings (特殊等价字符串组)
    LeetCode 824. Goat Latin (山羊拉丁文)
    LeetCode 443. String Compression (压缩字符串)
    LeetCode 387. First Unique Character in a String (字符串中的第一个唯一字符)
  • 原文地址:https://www.cnblogs.com/hhdllhflower/p/2711713.html
Copyright © 2011-2022 走看看