当前位置: 首页 > 文档资料 > Gdip 编程基础 >

1.7 笔和刷

优质
小牛编辑
132浏览
2023-12-01

本节介绍 GDI+的两类绘图工具——笔和刷,它们与 GDI 的相比新增加了许多功能。

1.7.1 笔

与 GDI 中的一样,GDI+中的笔(pen)也是画线状图的工具,但是功能更加强大。例 如:透明笔、图案笔、自定义虚线风格、线帽、笔的缩放和旋转、笔的连接点属性等。

GDI+中的笔对应于 Pen 类,被定义在 GdiplusPen.h 头文件中。 笔的构造方法主要有两个:

Pen(const Color &color, REAL width = 1.0); // 单色笔
Pen(const Brush *brush, REAL width = 1.0); // 纹理图案笔

其中,最常用的是第一个,它构造一个颜色为 color,宽度为 width(默认为 1)的单色笔。 如果颜色的α 值<255,则所创建的笔就是带透明度的笔。

(1)笔对齐

当笔宽大于 1 时,默认情况下,是以笔的中心与绘图坐标对齐。但是,也可以采用 Pen类的方法:

Status SetAlignment(PenAlignment penAlignment);

设置为内对齐,其输入参数取枚举类型 PenAlignment 的符号常量:

typedef enum {
    PenAlignmentCenter = 0, // 中心对齐(默认值)
    PenAlignmentInset = 1 // 内对齐
} PenAlignment;

例如(输出结果如图 14-19 所示):

Graphics graph(pDC-&gt;m_hDC); 
Rect rect(20, 20, 300, 200);
Pen pen(Color::Green, 30), redPen(Color::Red); 
graph.DrawEllipse(&pen, rect); 
graph.DrawRectangle(&redPen, rect); 
pen.SetAlignment(PenAlignmentInset); 
graph.TranslateTransform(340, 0); 
graph.DrawEllipse(&pen, rect); 
graph.DrawRectangle(&redPen, rect);

image

a) 中心对齐(默认值) b) 内对齐

图 14-19 笔对齐

(2)图案笔

笔类 Pen 的第二个构造方法,是从刷子来创建笔,如果是单色的实心刷,则相当于第一 个笔构造方法。如果刷子为条纹(影线)或纹理(图像)等图案刷,则该构造函数所创见的 就是对应的图案笔。

例如(条纹笔画椭圆,参见图 14-20):

HatchBrush hBrush(HatchStyleCross, Color::Green, Color::Red); // 创建十字线条纹刷 
Pen hPen(&**hBrush**, 40); // 创建宽度为 40 像素的条纹笔 
graph.DrawEllipse(&hPen, 20, 20, 400, 250); // 画椭圆

image

图 14-20 条纹笔椭圆

image

图 14-21 纹理笔椭圆

又例如(纹理笔画椭圆,参见图 14-21):

Image img(L"张东健.bmp"); // 创建图像对象,并装入图像文件
TextureBrush tBrush(&img); // 创建纹理刷
Pen tPen(&**tBrush**, 80); // 创建宽度为 80 像素的纹理笔 
Graphics graph(GetDC()-&gt;m_hDC); // 创建图形对象 
graph.DrawEllipse(&tPen, 40, 40, 640, 400); // 画椭圆

(3)线型

与 GDI 一样,对 GDI+中的笔,也可以设置线型。所用的方法为:

Status SetDashStyle(DashStyle dashStyle);

其中的输入参数,为虚线风格枚举 DashStyle:(GdiplusEnums.h)

enum DashStyle {
    DashStyleSolid, // 0 实线 (默认值)
    DashStyleDash, // 1 虚线
    DashStyleDot, // 2 点线:
    DashStyleDashDot, // 3 虚点线:
    DashStyleDashDotDot, // 4 虚点点线 
    DashStyleCustom // 5 自定义虚线
};

可以用 Pen 类的另一个方法来获取笔的线型:

DashStyle GetDashStyle() const;

GDI+中的线型,大多数与 GDI 中的相同,区别主要有两点:

  • GDI 中的非实线线型,对宽度>1 的笔无效;而 GDI+的笔对任意非零宽度的笔都是有效的。

  • GDI+中新增了一种风格——自定义虚线风格。 具体的自定义虚线风格,由 Pen 类的设置虚线图案的方法

Status SetDashPattern(const REAL *dashArray, INT count);

来设置,其中的实数数组 dashArray 含若干个正实数(单位为像素),按线、空、线、空、„„ 的交叉方式排列;参数 count 为数组中实数的个数(须>0)。

例如(参见图 14-22):

Graphics graph(pDC->m_hDC);
Pen pen(Color::Black, 8); // 创建宽 8 个像素的黑色笔(画虚线用)
// 线 5、空 2、线 15、空 4(像素)
REAL dashVals[4] = {5.0f, 2.0f, 15.0f, 4.0f};
FontFamily fontFamily(L"Times New Roman"); // 创建字体族对象
// 创建 5 号字大小的 Times New Roman 字体
Font font(&fontFamily, 10.5);
// 创建绿色的实心刷(写字符串用)
SolidBrush brush(Color(0, 128, 0));
// 笔的虚线风格枚举常量的名称字符串数组
CString strs[] = {L"DashStyleSolid", L"DashStyleDash", L"DashStyleDot", L"DashStyleDashDot", L"DashStyleDashDotDot", L"DashStyleCustom"};
for (int i = 0; i &lt;= 5; i++) { // 绘制各种风格的虚线及其名称串
    pen.**SetDashStyle**((DashStyle)i); // 设置笔的虚线风格
    // 设置自定义虚线图案
    if (i == 5) pen.SetDashPattern(dashVals, 4);
    // 画虚线
    graph.DrawLine(&pen, 10, 10 + i * 20, 400, 10 + i * 20);
    // 绘制虚线风格枚举常量名称字符串
    graph.DrawString(strs[i], -1, &font,
        PointF(410, 2 + i * 20), &brush);
}

image

图 14-22 虚线风格

还可以用 Pen 类的另一个方法来获取笔的自定义虚线图案数据:

INT GetDashPatternCount(VOID); // 获取虚线数组中实数的个数
Status GetDashPattern(REAL *dashArray, INT count); // 获取虚线数组

(4)线帽

线帽(line cap)是指线条两端的外观,默认为正方形,也可以用 Pen 类的下列方法来 设置不同的线端形状:

Status SetStartCap(LineCap startCap); // 设置起点的线帽
Status SetEndCap(LineCap endCap); // 设置终点的线帽
// 设置起点、终点和虚线的线帽
Status SetLineCap(LineCap startCap, LineCap endCap, DashCap dashCap);

其中的线帽枚举 LineCap 为(GdiplusEnums.h):

typedef enum {
    LineCapFlat = 0, // 平线,直线起点位于平线的中点(默认值)
    LineCapSquare = 1, // 方形,高度=线宽,直线起点位于正方形中心 
    LineCapRound = 2, // 圆形,直径=线宽,直线起点位于圆心 
    LineCapTriangle = 3, // 三角,高度=线宽,直线起点位于其底边中点 
    LineCapNoAnchor = 0x10, // 无锚,同平线
    LineCapSquareAnchor = 0x11, // 方形锚,高度&gt;线宽,直线起点位于正方形中心 LineCapRoundAnchor = 0x12, // 圆形锚,直径&gt;线宽,直线起点位于圆心 LineCapDiamondAnchor = 0x13, // 菱形锚,高度&gt;线宽,直线起点位于菱形中心 LineCapArrowAnchor = 0x14, // 箭头锚,高度&gt;线宽,直线起点位于箭头的尖点 LineCapCustom = 0xff // 自定义线帽
} LineCap;

自定义线帽,需要用到 GDI+专门为此定义的类 CustomLineCap。其构造函数为:

CustomLineCap(const GraphicsPath *fillPath, const GraphicsPath *strokePath, LineCap baseCap, REAL baseInset);

其中要用到图形路径类 GraphicsPath,该类中有图形各种添加图形方法,只是把 Graphics 类

绘图方法名中的 Draw 改成 Add 即可。例如:AddLine、AddRectangle 和 AddPolygon 等。使 用时,可以先创建一个空路径,然后调用这些添加图形方法若干次,就可以生成路径了。

image

例如(各类线帽,参见图 14-23 和图 14-24):

image

箭头线帽 构造箭头线帽头尾所使用的坐标系

图 14-23 自定义线帽

// 自定义箭头线帽
GraphicsPath startPath, endPath; // 创建起点和终点路径对象
startPath.AddRectangle(Rect(-10, -5, 20, 10)); // 起点矩形
Point polygonPoints[4] = {Point(0, -20), Point(10, 0),
Point(0, -10), Point(-10, 0)};
endPath.AddPolygon(polygonPoints, 4); // 终点箭头 
CustomLineCap startCap(NULL, &startPath); // 创建起点线帽 
CustomLineCap endCap(NULL, &endPath); // 创建终点线帽
// 定义笔
Pen pen(Color::Black, 20); // 画带线帽粗线的黑笔
Pen redPen(Color::Red); // 画不带线帽细线的红笔
// 中英文线帽字符串数组
CString cstrs[] = {L"平线帽", L"方线帽", L"圆线帽", L"三角线帽",
L"无锚线帽", L"方锚线帽", L"圆锚线帽", L"菱锚线帽", L"箭锚线帽", L"定制线帽"};
CString estrs[] = {L"LineCapFlat", L"LineCapSquare",
    L"LineCapRound", L"LineCapTriangle", L"LineCapNoAnchor", L"LineCapSquareAnchor", 
    L"LineCapRoundAnchor", L"LineCapDiamondAnchor", L"LineCapArrowAnchor", 
    L"LineCapCustom"};
// 创建字体
FontFamily fontFamily(L"Times New Roman"); // 对应中文的"宋体" 
Font font(&fontFamily, 10.5); // 五号字
// 绘制各种线帽
Graphics graph(pDC-&gt;m_hDC);
for (int i = 0; i &lt;= 9; i++) { 
    // 画线循环
    LineCap lc = (LineCap)(i &lt; 4 ? i : i + 12); // 线帽常量(整数) 
    if(i &lt; 9) 
        pen.SetLineCap(lc, lc, DashCapFlat); // 标准线帽 
    else 
    { 
        // 自定义线帽(i = 9)
        pen.SetCustomStartCap(&startCap); // 设置自定义的起点线帽 
        pen.SetCustomEndCap(&endCap); // 设置自定义的终点线帽 
        pen.SetWidth(3.0f); // 重新设置线宽为 3 个像素
    }
    int y = 20 + i * 40; // 计算直线的垂直坐标 
    graph.DrawLine(&pen, 100, y, 400, y); // 画带线帽的粗线 
    graph.DrawLine(&redPen, 100, y, 400, y); // 画不带线帽的细线
    // 绘制中英文线帽字符串
    graph.DrawString(cstrs[i], -1, &font,
    PointF(15.0f, y - 8.0f), &brush); 
    graph.DrawString(estrs[i], -1, &font,
    PointF(425.0f, y - 8.0f), &brush);
}

image

图 14-24 线帽的种类 图 14-25 箭头线帽的旋转直线簇 又例如(旋转箭头线帽,参见图 14-25):

// startCap 和 endCap 的创建同上例,需包含头文件 &lt;math.h&gt;
Pen pen(Color::DarkGreen, 2); 
pen.SetCustomStartCap(&startCap); 
pen.SetCustomEndCap(&endCap);
double radian = 3.14159265358979323846 / 180.0;
for (int i = 0; i &lt; 360; i += 10) 
    graph.DrawLine(&pen, 220, 220, 220 + (INT) (200 *
        cos(i * radian)), 220 + (INT) (200 * sin(i * radian)));

方法 SetLineCap 的最后一个输入参数 DashCap dashCap,用于设置虚线内部各线段端点 的形状。其取值是枚举类型(GdiplusEnums.h):

typedef enum {
    DashCapFlat = 0, // 平线(默认值) 
    DashCapRound = 2, // 圆形 
    DashCapTriangle = 3 // 三角
} DashCap;

可见,只有三种选择:平、圆和三角。之所以枚举常量所对应的值不连续,是因为要同 LineCap 枚举的对应常量一致。

注意,虚线帽的设置,只影响其虚线内部的线段,不会影响整条虚线的头尾形状,它们 是由 SetLineCap 方法的前两个参数来分别设置的。例如(参见图 14-26):

Graphics graph(pDC-&gt;m_hDC);
Pen pen(Color::Black, 10);
pen.SetLineCap(LineCapFlat, LineCapFlat, **DashCapFlat**);
//pen.SetLineCap(LineCapFlat, LineCapFlat, **DashCapRound**);
//pen.SetLineCap(LineCapFlat, LineCapFlat, **DashCapTriangle**); 
REAL dashVals[4] = {5.0f, 2.0f, 15.0f, 4.0f};
for (int i = 0; i &lt;= 5; i++) 
{
    pen.SetDashStyle((DashStyle)i);
    if (i == 5) 
        pen.SetDashPattern(dashVals, 4);
    graph.DrawLine(&pen, 10, 10 + i * 20, 400, 10 + i * 20);
}

image

DashCapRound 圆虚线帽

image

DashCapFlat 平虚线帽

image

DashCapTriangle 三角虚线帽

图 14-26 虚线帽

(5)线连接

笔的线连接(join)属性,也是 GDI+新增的功能。可以使用 Pen 类的方法:

Status SetLineJoin( LineJoin lineJoin);

来设置笔的线连接属性。其中输入参数为枚举类型 LineJoin:

enum LineJoin {
    LineJoinMiter = 0, // 斜接(默认值)
    LineJoinBevel = 1, // 斜截
    LineJoinRound = 2, // 圆角
    LineJoinMiterClipped = 3 // 斜剪
};

例如(参见图 14-27):

Graphics graph(pDC-&gt;m_hDC); 
Pen pen(Color::DarkGreen, 40); 
for (int i = 0; i &lt; 4; i++) 
{
    pen.SetLineJoin((LineJoin)i);
    graph.DrawRectangle(&pen, 40 + i * 150, 40, 100, 100);
}

image

LineJoinMiter LineJoinBevel LineJoinRound LineJoinMiterClipped

斜接 斜截 圆角 斜剪

图 14-27 线连接

从该例还看不出斜剪与斜接有什么区别,因为斜剪 LineJoinMiterClipped 主要针对交角 很小,相交部分很长的情形。在斜剪线连接方式下,可以调用 Pen 类的方法

Status SetMiterLimit(REAL miterLimit);

来设置相交部分的最大限制长度,默认是 10.0(相对于线宽的比值)。

对 LineJoinMiterClipped 方式的线连接,如果 miterLimit < 相交部分的长度,则会截断 至线头(同斜截方式,相当于 miterLimit = 1.0);如果 miterLimit >= 相交部分的长度,则绘 制完整的相交部分。

但是对 LineJoinMiter 方式的线连接,如果 miterLimit < 相交部分的长度,则会截断至

miterLimit 所指定比例的长度;如果 miterLimit >= 相交部分的长度,则绘制完整的相交部分。 例如(参见图 14-28):

Graphics graph(pDC-&gt;m_hDC);
Pen redPen(Color::Red); // 画细线的红笔
Pen pen(Color::DarkGreen, 40.0f); // 画粗线的绿色笔
Point points[] = {Point(20, 100), Point(400, 130),
Point(20, 160)}; // 点数组
pen.SetLineJoin(LineJoinMiter); // 斜接
//pen.SetLineJoin(LineJoinBevel); // 斜截
//pen.SetLineJoin(LineJoinRound); // 圆角
//pen.SetLineJoin(LineJoinMiterClipped); // 斜剪
//pen.SetMiterLimit(20.0f); // 设置斜接限长 
graph.DrawLines(&pen, points, 3); // 画粗线 
graph.DrawLines(&redPen, points, 3); // 画细线

image

图 14-28 小交角线连接 图 14-29 不同斜接限长下的斜接线连接 如果不断修改斜接线连接 LineJoinMiter 方式下的线长限制(0.0f~13.0f),则可得到不同

截断长度的斜交角。例如(参见图 14-29):

pen.SetLineJoin(LineJoinMiter); // 斜接 
pen.SetMiterLimit(1.0f/*~13.0f*/); // 设置斜接限长

1.7.2 刷

与 GDI 中的一样,GDI+中的刷(brush)也是画填充图的工具,GDI+中也有与 GDI 相 对应的实心刷(单色刷)、条纹刷(影线刷)和纹理刷(图像刷)。不过,GDI+又新增加了 功能强大的线性渐变刷和路径渐变刷,而且还为所有这些刷各自建立了对应的类,基类是Brush(功能少)。

图 14-30 是 GDI+中各种刷类的层次结构图, 所有刷类都被定义在头文件 Gdiplus Brush.h 中。

(1)刷基类 Brush

Brush 是所有 GDI+具体刷类的基类,Brush 类没有自己的公用构造函数,属于非实例化

类(用户不能创建 Brush 类的对象和实例),只是定义了三个公用的方法(接口):

Brush *Clone( VOID) const; // 克隆,用于复制 Brush 及其派生类对象 Status 
GetLastStatus(VOID); // 获取最后状态,返回刷对象最近的错误状态
BrushType GetType(VOID); // 获取类型,返回当前(派生)刷的类型枚举常量

下面是 BrushType 枚举类型的定义(GdiplusEnums.h):

typedef enum {
    BrushTypeSolidColor = 0, // 实心单色刷 
    BrushTypeHatchFill = 1, // 影线条纹填充刷 
    BrushTypeTextureFill = 2, // 图像纹理填充刷 
    BrushTypePathGradient = 3, // 路径渐变刷 
    BrushTypeLinearGradient = 4 // 线性渐变刷
} BrushType;

(2)实心刷类 SolidBrush

GDI+中,实心的单色刷对应于 SolidBrush 类,它只有一个构造函数:

SolidBrush(const Color &color);

输入参数为颜色对象的引用。

在前面的例子中已经多次使用了 SolidBrush 类,下面再举一个画正叶曲线的例子,下面 是正叶曲线的极坐标方程及其到直角坐标系的转换公式:

image

其中,l 为叶片长度、n 为叶片数目。

因为 GDI+并没有画正叶曲线的专门函数,所以需要用多边形、样条曲线或图形路径来 刻画它。可以使用填充多边形、填充封闭曲线和填充图形路径等方式来进行绘制,下面的代码使用的是填充闭基样条曲线,输出结果如图 14-31 所示。

  • 绘制单个正叶曲线的函数代码:

    #include <math.h>
    void DrawLeaves(Graphics &graph, const Color col, Point &O,
        int l, int n) {
        double radian = 3.14159265358979323846 / 180.0; 
        int m = n < 5 ? 21 : 11;
        int N = m * n;
        double da = 360.0 / N; PointF *ps = new PointF[N];
        for (int i = 0; i &lt; N; i++) {
            double r = abs(l * cos(radian * (n * i * da)/ 2.0)), 
                x = r * cos(i * da * radian),
                y = r * sin(i * da * radian);
            ps[i].X = REAL(O.X + x);
            ps[i].Y = REAL(O.Y + y);
        }
        graph.FillClosedCurve(&SolidBrush(col), ps, N);
    }
    
  • 绘制系列彩色正叶曲线的调用序列:

Graphics graph(pDC-&gt;m_hDC);
Color cols[] = {Color::Aqua, Color::Aquamarine, Color::DarkBlue, Color::DarkKhaki, 
    Color::DeepPink, Color::BlueViolet, Color::Brown, Color::BurlyWood, Color::CadetBlue, 
    Color::Chartreuse, Color::Turquoise, Color::Coral, Color::CornflowerBlue, 
    Color::Crimson, Color::DarkCyan};
bool color = true; // false; 
for (int i = 0; i &lt; 15; i++)
    DrawLeaves(graph, color ? cols[i] : Color::Green,
        Point(100 + 200* (i % 5), 100 + 200 * (i / 5)), 100, i + 1);

image

图 14-31 彩色正叶曲线系列

(3)条纹刷类 HatchBrush

条纹是一种重复填充的小方形图案,一般为横线、竖线、斜线和小方块等构成。GDI+ 中,条纹刷(hatch brush 影线刷/阴影刷)对应于 HatchBrush 类,它也只有一个构造函数:

HatchBrush(HatchStyle hatchStyle, const Color &foreColor, const Color &backColor = Color());

其中:第一个参数为条纹类型,第二个参数为前景色(条纹色),第三个参数为背景色(空隙色)。

GDI+中一共有 53 种条纹风格,而 GDI 中只有前 6 种。条纹风格枚举 HatchStyle 也被定 义在头文件 GdiplusEnums.h 中:

enum HatchStyle {
    HatchStyleHor izontal, // 0:横线
    HatchStyleVertical, // 1:竖线
    HatchStyleForwardDiagonal, // 2:正斜线
    HatchStyleBackwardDiagonal, // 3:反斜线
    HatchStyleCross, // 4:十字线
    HatchStyleDiagonalCross, // 5:斜十字线
    HatchStyle05Percent, // 6:5%
    HatchStyle10Percent, // 7:10%
    ...
    HatchStyleSphere, // 47:球面
    HatchStyleSmallGrid, // 48:小网格
    HatchStyleSmallChecker Board, // 49:小跳棋盘
    HatchStyleLargeCheckerBoard, // 50:大跳棋盘
    HatchStyleOutlinedDiamond, // 51:斜纲线
    HatchStyleSolidDiamond, // 52:实菱形 HatchStyleTotal, // = 53(0 ~ 52):条纹风格总数 HatchStyleLargeGrid = HatchStyleCross, // 4:大网格
    HatchStyleMin = HatchStyleHorizontal, // 0:条纹风格最小值 HatchStyleMax = HatchStyleTotal - 1, // 52:条纹风格最大值
};

例如(参见图 14-32):

Graphics graph(pDC-&gt;m_hDC); Pen pen(Color::Black);
SolidBrush textBrush(Color::Red);
FontFamily fontFamily(L"Times New Roman"); 
Font font(&fontFamily, 18);
CString str;
StringFormat sfmt; // 文本格式 
sfmt.SetAlignment(StringAlignmentCenter); // 水平对齐 
sfmt.SetLineAlignment(StringAlignmentCenter); // 垂直对齐
int w = 50, h = 50, s = 5;
for (int i = 0; i &lt; 53; i++) { // 主循环
    HatchBrush brush(HatchStyle(i), Color::Black, Color::White); 
    RectF rect(REAL(s + (i % 10) * (w + s)),
    REAL(s + (i / 10) * (h + s)), REAL(w), REAL(h));
    graph.FillRectangle(&brush, rect); // 画条纹块 
    str.Format(L"%d", i); // 绘制数字编号的文本串: 
    graph.DrawString(str, str.GetLength(), &font, rect,
        &sfmt, &textBrush);
}

image

图 14-32 条纹刷的条纹风格

与 GDI 一样,在 GDI+中也可以调整条纹刷和图像刷的起点。这需要使用图像类 Graphics 的方法 SetRenderingOrigin 来设置渲染原点为(x, y)(默认为(0, 0)):

Status SetRenderingOrigin(INT x, INT y);

(4)纹理刷类 TextureBrush

纹理刷(texture brush)就是图像刷,它将刷中所装入的图像,在目标区域中进行平铺, 可达到纹理效果。GDI 中也有图像刷,但仅限于使用位图资源和(非常费事才能使用)BMP 文件。在 GDI+中,纹理刷所对应的是 TextureBrush 类,它有 7 个构造函数,最常用的为:

TextureBrush(Image* image, WrapMode wrapMode = WrapModeTile);

其中,第一个参数是图像对象的指针,第二个参数是排列方式的枚举常量(GdiplusEnums.h):

typedef enum {
    WrapModeTile = 0, // 平铺(瓦)(默认值)
    WrapModeTileFlipX = 1, // 平铺且 X 向翻转(相邻列左右翻转) 
    WrapModeTileFlipY = 2, // 平铺且 Y 向翻转(相邻行上下翻转) 
    WrapModeTileFlipXY = 3, // 平铺且 XY 向翻转(相邻行列左右上下翻转) 
    WrapModeClamp = 4 // 不平铺(不重复,夹住)
} WrapMode;

还可以用纹理刷类的下面两个方法来设置和获取刷的排列方式:

Status SetWrapMode(WrapMode wrapMode); WrapMode GetWrapMode() const;

例如(参见图 14-33):

Graphics graph(pDC-&gt;m_hDC); 
Image img(L"张东健.bmp");
TextureBrush brush(&img, WrapModeTile/*FlipXY*/);
//TextureBrush brush(&img, **WrapModeClamp**); 
RECT rect;
GetClientRect(&rect);
graph.FillRectangle(&brush, RectF(0.0f, 0.0f, REAL(rect.right), REAL(rect.bottom)));

image

平铺(WrapModeTile)

image

平铺且 X 向翻转(WrapModeTileFlipX)

image

平铺且 Y 向翻转(WrapModeTileFlipY)

image

平铺且 XY 向翻转(WrapModeTileFlipXY)

image

不平铺(WrapModeClamp)

图 14-33 纹理刷排列方式

纹理刷类 TextureBrush 中,还有几个方法,可以对刷中的图像进行平移(translate)、旋 转(rotate)和缩放(scale)等变换(transform)(这是 GDI 里所没有的功能):

Status TranslateTransform(REAL dx, REAL dy, MatrixOrder order = MatrixOrderPrepend); 
Status RotateTransform(REAL angle, MatrixOrder order = MatrixOrderPrepend) ;
Status ScaleTransform(REAL sx, REAL sy, MatrixOrder order = MatrixOrderPrepend);

例如(参见图 14-34):

Graphics graph(pDC-&gt;m_hDC); Image img(L"张东健.bmp"); 
TextureBrush brush(&img);
//brush.TranslateTransform(30, 30); // 平移(30, 30)
brush.RotateTransform(30); // 旋转 30 度
//brush.ScaleTransform(3, 1); // 水平放大 3 倍
//brush.ScaleTransform(1, 3); // 垂直放大 3 倍
RECT rect; 
GetClientRect(&rect);
graph.FillRectangle(&brush, RectF(0.0f, 0.0f, REAL(rect.right), REAL(rect.bottom)));

image

平移(30, 30)

image

旋转 30 度

image

水平放大 3 倍

image

垂直放大 3 倍

图 14-34 纹理刷变换

(5)线性渐变刷类 LinearGradientBrush

线性渐变刷(linear gradient brush 线性梯度刷)使用逐渐变化的颜色填充目标区域。是 GDI+新增的功能。线性渐变刷所对应的类为 LinearGradientBrush,它有 6 个构造函数,前 3 个是整数版,后 3 个是对应的浮点数版。下面是 3 个整数版的构造函数:

LinearGradientBrush(const Point& point1, const Point& point2, const Color& color1, const Color& color2);
LinearGradientBrush(const Rect& rec t, const Color& color1, const Color& color2, LinearGradientMode mode);
LinearGradientBrush(const Rect& rect, const Color& color1, const Color& color2, REAL angle, BOOL is AngleScalable = FALSE);

在这三种构造函数中,第一个是点到点、第二个是矩形与渐变模式、第三个是是矩形与旋转角度。限于篇幅,这里只介绍其中点到点的整数版构造函数的具体使用方法。

1)点到点渐变

点到点的渐变是指刷子所填充的颜色,沿着点 point1 到点 point2 的直线,从颜色 color1 连续变化到 color2。若 p1 和 p2 点的 y 值相等,则为水平方向的渐变;若 p1 和 p2 点的 x 值 相等,则为垂直方向的渐变;p1 和 p2 点的 x 和 y 值都不相等,则为斜对角方向的渐变。

例如(参见图 14-35):

Graphics graph(pDC->m_hDC);
Point p1(10, 10), p2(110, 10), p3(10, 110), p4(230, 10), p5(330, 110);
Size size(100, 100);
Color col1(255, 0, 0), col2(0, 0, 255);
LinearGradientBrush hbrush(p1, p2, col1, col2);
graph.FillRectangle(&hbrush, Rect(p1, size)); 
LinearGradientBrush vbrush(p1, p3, col1, col2); 
graph.FillRectangle(&vbrush, Rect(Point(120, 10), size)); 
LinearGradientBrush dbrush(p4, p5, col1, col2); 
graph.FillRectangle(&dbrush, Rect(Point(230, 10), size));

image

水平渐变 垂直渐变 对角渐变

图 14-35 线性渐变刷

其实,线性渐变刷默认是按 WrapModeTile 平铺方式重复排列的(原点是 point1),例如(参见图 14-36 a)):

Graphics graph(pDC-&gt;m_hDC);
Point p1(10, 10), p2(110, 10), p3(10, 110);
Color col1(255, 0, 0), col2(0, 0, 255); 
LinearGradientBrush hbrush(p1, p2, col1, col2);
//hbrush.SetWrapMode(WrapModeTileFlipX); 
graph.FillRectangle(&hbrush, Rect(p1, Size(400, 200))); 
LinearGradientBrush vbrush(p1, p3, col1, col2);
//vbrush.SetWrapMode(WrapModeTileFlipX); 
graph.FillRectangle(&vbrush, Rect(Point(420, 10), Size(200, 410)));
LinearGradientBrush dbrush(p1, Point(110, 100), col1, col2);
//dbrush.SetWrapMode(WrapModeTileFlipX); 
graph.FillRectangle(&dbrush, Rect(Point(10, 220), Size(400, 200)));

你也可以将上面代码中的注释符“//”去掉,利用线性渐变刷类的方法

Status SetWrapMode(WrapMode wrapMode);

来设置画刷的排列方式为 WrapModeTileFlipX 平铺并水平翻转,参见图 14-36 b)。

image

图 14-37 参数的含义

image

a) 平铺重复排列 b) 加水平翻转

图 14-36 按平铺重复排列的线性渐变刷

下面是一个利用水平线性渐变刷来画阴阳八卦中的阴阳鱼例子(参见图 14-37 和图 14-38):

LinearGradientBrush R2BBrush(Point(0, 10), Point(200, 10), Color(255, 0, 0), Color(0, 0, 255));
LinearGradientBrush B2YBrush(Point(0, 10), Point(200, 10),
Color(0, 0, 255), Color(255, 255, 0));
Pen bluePen(Color(255, 0, 0, 255));
Rect circleRect(0, 0, 200, 200);
Rect leftRect(0, 50, 100, 100);
Rect rightRect(100, 50, 100, 100); Graphics graph(pDC-&gt;m_hDC);
graph.FillPie(&R2BBrush, circleRect, 0.0f, 180.0f);
graph.FillPie(&B2YBrush, circleRect, 180.0f, 180.0f); 
graph.FillPie(&R2BBrush, leftRect, 180.0f, 180.0f); 
graph.FillPie(&B2YBrush, rightRect, 0.0f, 180.0f); int r = 10;
graph.FillEllipse(&SolidBrush(Color(0, 255, 0)), 50 - r, 100 - r, 2 * r, 2 * r);
graph.FillEllipse(&SolidBrush(Color(255, 0, 255)), 150 - r, 100 - r, 2 * r, 2 * r);

image

image

image

image

图 14-38 绘制阴阳鱼的分步输出结果

2)多色渐变 线性渐变刷还有很多其他功能,例如可利用刷的方法:

Status SetInterpolationColors(const Color *presetColors, const REAL *blendPositions, INT count);

来设置多色渐变。其中,presetColors 为多色数组、blendPositions 为以百分比表示的对应混色点的位置(首、尾值必须为 0.0f 和 1.0f,中间的值应该按递增序排列)、count 为颜色和混 色点位的数目。例如(参见图 14-39):

Color cols[] = {Color::Red, Color::Orange, Color::Yellow, Color::Green, Color::Cyan, Color::Blue, Color::Purple, Color::Magenta};
REAL bps[] = {0.0f, 0.15f, 0.3f, 0.45f, 0.6f, 0.75f, 0.875f, 1.0f};
LinearGradientBrush brush(Point(10, 10), Point(810, 10), Color::Black, Color::White);
brush.SetInterpolationColors(cols, bps, 8);
graph.FillRectangle(&brush, Rect(10, 10, 800, 100));

image

图 14-39 多色渐变 另外,也可以像纹理刷和条纹刷一样,设置线性渐变刷的渲染原点等。 路径渐变刷的内容,安排到下一章的第 15.1.2 小节中,在介绍过路径的基本概念和使用方法之后再来讲解。