第二章 2绘制线条简单图形

线条和形状
同Windows其它版本相比,Windows CE提供相当少的功能的领域之一就是基本线条绘制和形状绘制功能。用来创建复杂环形的Chord, Arc, 和Pie函数被去掉了。大部分使用"当前点[current point]"概念的函数也被去掉了。除了MoveToEx, LineTo和GetCurrentPositionEx外,处理当前点的其它GDI函数都不被Windows CE支持。因此想用 ArcTo、PolyBezierTo等函数来绘制一系列连接的直线和曲线是不可能了。不过即使在缺少很多图形函数的情况下,Windows CE依然提供了绘制直线和形状所需要的基本函数。

线条
简单调用Polyline就可以绘制一个或者多个线条了。函数原型如下:

BOOL Polyline (HDC hdc, const POINT *lppt, int cPoints);
第2个参数是指向POINT结构数组的指针,该结构定义如下:
typedef struct tagPOINT {
    LONG x;
    LONG y;
} POINT

每个X、Y结合起来描述一个从屏幕左上角开始的像素。第三个参数是数组里的point结构的数量。因此绘制一个从(0,0)到(50,100)的直线,代码看起来如下:
POINTS pts[2];
  
pts[0].x = 0;
pts[0].y = 0;
pts[1].x = 50;
pts[1].y = 100;
PolyLine (hdc, &pts, 2);

绘制该直线的另外一个方法是使用MoveToEx和LineTo函数。它们的原型如下:
BOOL WINAPI MoveToEx (HDC hdc, int X, int Y, LPPOINT lpPoint);
BOOL WINAPI LineTo (HDC hdc, int X, int Y);

要使用这两个函数绘制线条,首先要调用MoveToEx将当前点移动到线条的起始坐标处,接下来用终点坐标调用LineTo。调用代码如下:
MoveToEx (hdc, 0, 0, NULL);
LineTo (hdc, 50, 100);

要查询当前点,可调用函数GetCurrentPositionEx,原型如下:
WINGDIAPI BOOL WINAPI GetCurrentPositionEx (HDC hdc, LPPOINT pPoint);

和前面绘制文本的例子一样,这些代码片段对设备描述表的状态做了大量假设。例如,绘制的(0, 0)和(50, 100)之间的线条是什么样子?宽度和颜色是什么以及是实心线条吗?所有版本的Windows,包括Windows CE在内,都允许指定这些参数。

 

画笔
画笔(pen)是用于指定线条外观和形状轮廓的工具。画笔是另一个GDI对象,像本章里描述的其它GDI对象一样,画笔要被创建、选进设备描述表,使用、取消选择,最后被销毁。 同其它备用GDI对象一样,可以使用GetStockObject来检索备用画笔。该函数原型如下:
HGDIOBJ GetStockObject (int fnObject);
所有版本的Windows都提供三种备用画笔,每个1像素宽。这些备用画笔有3种颜色:白色、黑色和NULL。当您使用GetStockObject时,该函数分别使用参数WHITE_PEN, BLACK_PEN和NULL_PEN来检索这些画笔中的一个。与应用程序创建的标准图形对象不同,备用对象不应该被应用程序删除。相反,当画笔不再需要的时候,应用程序只应简单地将画笔从设备描述表中取消选择即可。

要在Windows下创建自定义画笔,可以使用以下两个函数。第一个是:
HPEN CreatePen (int fnPenStyle, int nWidth, COLORREF crColor);
fnPenStyle规定要绘制的线条的外观。例如,使用PS_DASH标志可以创建一个虚线。Windows CE只支持PS_SOLID、PS_DASH和PS_NULL这三个风格标志。nWidth参数规定画笔的宽度。最后,crColor规定画笔的颜色。crColor的参数类型是COLORREF,可以使用RGB宏来构造该类型。RGB宏定义如下:

COLORREF RGB (BYTE bRed, BYTE bGreen, BYTE bBlue);
因此要创建一个红色实心线条,代码看起来像这样:
hPen = CreatePen (PS_SOLID, 1, RGB (0xff, 0, 0));

另一种画笔创建函数如下:
HPEN CreatePenIndirect (const LOGPEN *lplgpn);
其中逻辑画笔结构LOGPEN定义如下:
typedef struct tagLOGPEN {
    UINT lopnStyle;
    POINT lopnWidth;
    COLORREF lopnColor;
} LOGPEN;
CreatePenIndirect用不同的形式为Windows提供了同样的参数。用CreatePenIndirect创建同样是1像素宽的红色画笔,代码如下:
LOGPEN lp;
HPEN hPen;
lp.lopnStyle = PS_SOLID;
lp.lopnWidth.x = 1;
lp.lopnWidth.y = 1;
lp.lopnColor = RGB (0xff, 0, 0);
  
hPen = CreatePenIndirect (&lp);

Windows CE不支持复杂画笔,比如宽度超过1像素的虚线。要确定支持什么,我们熟悉的GetDeviceCaps就派上用场了,给它的第2个参数取LINECAPS即可。具体可以参考Windows CE文档。

 

形状
线条很有用,不过Windows还提供了绘制形状的函数,包括填充和非填充的形状。Windows CE提供了Windows程序员大部分常见的函数。

Rectangle, RoundRect, Ellipse和Polygon都支持。

画刷
在讨论矩形和椭圆形之前,需要先讲述另一个曾经简要提到过的GDI对象--画刷(brush)。画刷通常是一个8*8像素的位图,用于填充形状。
Windows也用它来填充客户窗口地背景。Windows CE提供许多备用画刷,并提供从应用程序定义的图案创建画刷的能力。许多纯色备用画刷可以使用GetStockObject来检索。在众多可用画刷中,有一个是用于四色灰度级显示器的每个灰度的,四种灰度是:白色、浅灰色、深灰色和黑色。

要创建纯色画刷,可以调用以下函数:
HBRUSH CreateSolidBrush (COLORREF crColor);
crColor规定了画刷的颜色。颜色可以使用RGB宏来指定。
Windows CE下用Win32函数 CreateDIBPatternBrushPt创建自定义图案的画刷。函数原型如下:
HBRUSH CreateDIBPatternBrushPt (const void *lpPackedDIB, UINT iUsage);
第一个参数指向紧凑格式的DIB。这意味着指针指向一个缓冲区,包含有BITMAPINFO结构,并且紧随其后的是位图的位数据。您应该还记得BITMAPINFO结构实际上由BITMAPINFOHEADER结构及紧随其后的RGBQUAD格式的调色板构成,所以该缓冲区包含了创建DIB所需要的每个信息,即位图信息、调色板、位图的位数据。如果第二个参数设置为DIB_RGB_COLORS,则调色板在每个入口都包含有RGBQUAD值。对于每像素8位的位图来说,可以设置DIB_PAL_COLORS标志,但Windows CE会忽略位图的颜色表。

在Windows CE下CreateDIBPatternBrushPt更加重要,因为Windows CE下不支持阴影画刷,而其它版本的Windows使用CreateHatchBrush函数来支持阴影画刷。阴影画刷是由水平线条、垂直线条或斜线构成的画刷。这些画刷在灰度级显示器上特别有用,因为您可以使用不同阴影图案来突出图表的不同区域。不过,您可以使用CreateDIBPatternBrushPt和适当的位图图案来复制出这些画刷。本章后面的示例代码演示了在Windows CE下创建阴影画刷的方法。

默认情况下,画刷原点在窗口左上角。这并不总是你所希望的。例如,一个条形图使用阴影画刷填充从(100,100)到(125,220)的矩形。因为该矩形不能被8(画刷通常是8*8像素的正方形)整除,所以就用一个不怎么美观的局部画刷来填充条形图的左上角。为了避免这种情况,您可以移动画刷的原点,这样可以使用与形状的边角正确对齐的画刷来绘制各个形状了。用来完成这一调整的函数如下:BOOL SetBrushOrgEx (HDC hdc, int nXOrg, int nYOrg, LPPOINT lppt);
nXOrg和nYOrg允许将原点设置为0到7之间,这样就可以将原点定位在画刷8*8范围内任何一点。lppt填充的是画刷先前的版本,这样可以在必要的时候恢复先前的原点。

矩形
矩形函数绘制一个填充矩形或者一个中空矩形。该函数定义如下:
BOOL Rectangle (HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
该函数使用当前选择的画笔绘制矩形外框,使用当前画刷填充内部。要绘制中空矩形,需要在调用Rectangle之前把空画刷选择进设备描述表中。

理解绘制边框的实际像素是很重要的。假定我们要在(0,0)处绘制一个5*7的矩形,函数调用如下:
Rectangle(0,0,5,7);
注意观察矩形右边界实际上是如何绘制到第4列的,底部边缘是如何绘制到第6行的。这是标准的Windows惯例。矩形在Rectangle函数指定的右边界和底边界以内进行绘制。如果选择的画笔宽度超过1个像素,右边界和底边界以边框矩形居中进行绘制。(Windows其它版本允许使用PS_INSIDEFRAME画笔风格忽略画笔宽度,强制在框架内绘制矩形)

圆和椭圆
可以用Ellipse函数绘制圆和椭圆,该函数原型如下:
BOOL Ellipse (HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
使用传入的矩形作为边界矩形(bounding rectangle)来绘制椭圆,如图2-7所示。对于Rectangle函数,使用当前画刷来填充椭圆内部,使用当前画笔来绘制椭圆外框。
圆角矩形
RoundRect函数绘制一个圆角矩形,原型如下:
BOOL RoundRect (HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect, int nWidth, int nHeight);
最后两个参数给出了用于圆角的椭圆的宽度和高度,如图2-8所示。指定椭圆的高度和宽度可以让程序绘制出完全一样的均匀的圆角。缩短椭圆的高度,可以使矩形的两侧更平,而缩短椭圆的宽度可以使矩形的顶部和底部更平。

多边形
最后,Polygon绘制了一个多边形,函数原型如下:BOOL Polygon (HDC hdc, const POINT *lpPoints, int nCount);
第2个参数是一个指向Point结构数组的指针,该数组定义了描述多边形的各个点。从最终形状上看,会被点数多一条边,因为函数自动把最后一个点和第一个点连接起来,绘制出多边形的最后一条边。

填充函数
前面提到的函数都是使用画刷和画笔的组合在设备描述表上绘制形状的。只填充区域而不涉及绘制形状轮廓的画笔的函数也是有的。这些函数中的第一个就如下所示:
int FillRect (HDC hDC, CONST RECT* lprc, HBRUSH hbr);
FillRect的参数设备描述表句柄、需要填充的矩形以及用来填充矩形的画刷。要在矩形区域里绘制一个纯色或者图案,使用FillRect函数会是一个快捷方便的方式。
虽然FillRect很方便,但GradientFill可能更棒一些。GradientFill函数填充一个矩形区域,使用一个颜色从一边开始绘制,并逐渐平滑过度到另外一个颜色直到另外一边。

 

GradientFill函数原型如下:
BOOL GradientFill (HDC hdc, PTRIVERTEX pVertex, ULONG dwNumVertex, PVOID pMesh, ULONG dwNumMesh, ULONG dwMode);
第一个参数依旧是设备描述表。pVertex是指向TRIVERTEX结构数组的指针,dwNumVertex是TRIVERTEX数组中入口的数量。TRIVERTEX结构定义如下:
struct _TRIVERTEX {   
    LONG        x;
    Long        y;
    COLOR16     Red;
    COLOR16     Green;
    COLOR16     Blue;
    COLOR16     Alpha;s
} TRIVERTEX;
TRIVERTEX结构的各个域描述了设备描述表里的一个点和一个RGB颜色。这些点应该是要填充的矩形的左上角和右下角。pMesh是指向GRADIENT_RECT结构的指针,该结构定义如下:
struct _GRADIENT_RECT
{
    ULONG UpperLeft;
    ULONG LowerRight;
} GRADIENT_RECT;
GRADIENT_RECT结构简单指出TRIVERTEX结构中的哪些入口是描述左上角或者是右下角的。最后,dwNumMesh参数包含GRADIENT_RECT结构的数量。dwMode结构包含标志位,指出是从左到右(GRADIENT_FILL_RECT_H)填充还是从上到下(GRADIENT_FILL_RECT_V)填充。GradientFill函数实际上比表面上看到的要更复杂,因为在桌面系统里,它还执行三角形填充,这种填充方式在Windows CE下不支持。下面是创建图2-9的窗口的代码片段:
TRIVERTEX vert[2];
GRADIENT_RECT gRect;
  
vert [0] .x       =  prect->left;
vert [0] .y       =  prect->top;
vert [0] .Red     =  0x0000;
vert [0] .Green   =  0x0000;
vert [0] .Blue    =  0xff00;
vert [0] .Alpha   =  0x0000;
  
vert [1] .x       =  prect->right;
vert [1] .y       =  prect->bottom;
vert [1] .Red     =  0x0000;
vert [1] .Green   =  0xff00;
vert [1] .Blue    =  0x0000;
vert [1] .Alpha   =  0x0000;
  
gRect.UpperLeft = 0;
gRect.LowerRight = 1;
  
GradientFill(hdc,vert,2,&gRect,1,GRADIENT_FILL_RECT_H);

你可能感兴趣的:(简单)