WIN32汇编语言教程:第07章 图形操作 · 7.2 绘制图形(5)
7.2.3 绘制图形
GDI的图形绘制函数主要有绘制线条和填充区域两大类。绘制线条的函数以当前画笔绘制线条;绘制填充区域的函数以当前画笔绘制边线,并以当前画刷填充中间的区域。
1. 绘制线条
绘制线条的函数有画直线的LineTo,画多条直线的Polyline和PolylineTo,画贝塞儿曲线的PolyBezier和PolyBezierTo,画弧线的Arc和ArcTo。
DC的数据结构中有一个“当前点”,LineTo函数就是从当前点画一条直线到参数中指定的点,并把参数中指定的点设置为新的当前点。画线函数中所有以To结尾的函数都是从当前点开始绘画的,如LineTo,PolylineTo,PolyBezierTo和ArcTo,这些函数在绘画结束后会把绘制的最后一点设置为新的当前点,所以在使用这些函数的时候要考虑到当前点也是参与绘制的坐标一部分。而其余的Polyline,PolyBezier和Arc函数则和当前点没有关系,也不会影响当前点的位置。
如果要设置当前点的位置,可以使用MoveToEx函数:
invoke MoveToEx,hDC,dwX,dwY,lpPoint
dwX和dwY指出了新的当前点的坐标,lpPoint指向一个空的POINT结构,用来返回原来的当前点位置,如果不需要的话,这个参数可以使用NULL。
另一个函数也可以得到当前点的坐标:
invoke GetCurrentPositionEx,hDC,lpPoint
同样,lpPoint指向一个用来返回当前点坐标的POINT结构地址。
如果要绘制一条直线,必须配合使用MoveToEx和LineTo函数,首先由MoveToEx函数设置一个当前点当做起始坐标,然后用LineTo绘画到结束坐标,如Clock.asm中的_DrawLine子程序中就是这样绘制时钟指针的:
invoke MoveToEx,_hDC,@dwX1,@dwY1,NULL invoke LineTo,_hDC,@dwX2,@dwY2
这两句代码绘画一条从@dwX1,@dwY1到@dwX2,@dwY2的直线。
如果要绘制是相连的多条直线,可以使用Polyline或PolylineTo函数:
invoke PolylineTo,hDC,lpPoint,cPoints invoke Polyline,hDC,lpPoint,cPoints
lpPoint指向一个包含一系列POINT结构的缓冲区,由于POINT结构只有X和Y两个字段,所以缓冲区中的数据实际上是x1,y1,x2,y2,x3,y3,...,cPoints参数指出了点的数目,注意:PolylineTo画出的直线是从当前点坐标(x,y)开始,然后到(x1,y1),再到(x2,y2),...,而Polyline函数画出的直线是从(x1,y1)开始的,对于这个函数,如果cPoints参数指定了n个点,那么直线的数量实际上是n−1。当绘制的相连直线很多的时候,用Polyline或PolylineTo比多次使用LineTo的速度要快很多,就像用填充函数比多次使用SetPixel要快一样。
表7.3举例说明了这些画线函数的功能,表中的(x1,y1)或(x2,y2)等表示点1或点2的坐标,(xc,yc)表示当前点的坐标,当前点在图中用c表示。
表7.3 画线函数的功能
函 数 | 说明 | 图 例 |
LineTo(hDC,x,y) | 从当前点到(x,y)点 | |
PolylineTo(hDC,lpPoint,5) | lpPoint指向存放(x1,y1)到(x5,y5)的缓存区,函数画的线条从(xc,yc)到(x1,y1)到(x2,y2)...到(x5,y5),共5条直线 | |
Polyline(hDC,lpPoint,5) | lpPoint指向存放(x1,y1)到x5、y5的缓存区,函数画的线条从(x1,y1)到(x2,y2)...到(x5,y5),共4条直线 | |
PolyBezierTo(hDC,lpPoint,3) | 绘画的Bezier曲线的控制点为(xc,yc)和(x1,y1)和(x2,y2)和(x3,y3) | |
PolyBezier(hDC,lpPoint,4) | 绘画的Bezier曲线的控制点为(x1,y1)和(x2,y2)和(x3,y3)和(x4,y4) | |
ArcTo(hDC,x1,y1,x2,y2,
x3,y3,x4,y4) | 首先画(xc,yc)到起始点的直线,再画起始点到结束点的弧线 | |
Arc(hDC,x1,y1,x2,y2,
x3,y3,x4,y4) | 画起始点到结束点的弧线 |
对于Arc和ArcTo函数,参数(x1,y1)和(x2,y2)定义了一个矩形的对角点,然后在和这个矩形相切的椭圆上面,以椭圆的中心(也就是矩形的中心)画两条假想的直线到(x3,y3)和(x4,y4),这两条直线和椭圆相交的点就是圆弧的起始点和结束点。在默认情况下,圆弧由起始点沿着椭圆从逆时针方向画到结束点。不过绘画方向可以由SetArcDirection函数重新规定:
invoke SetArcDirection,hDC,AD_COUNTERCLOCKWISE ;逆时针方向 invoke SetArcDirection,hDC,AD_CLOCKWISE ;顺时针方向
读者一定注意到了一个问题:在画线的时候,如果当前的画笔是虚线的话,虚线的不连续部分实际上是由白色组成的,当虚线画在非白色的背景上的时候这一点显得特别明显。实际上,可以选择这些不连续部分的颜色,用以下的语句就可以做到这一点:
invoke SetBkColor,hDC,dwColor
调用后不连续的部分就将用dwColor指定的颜色绘画。
但是改变颜色也并不是惟一的选择,GDI允许这部分并不绘画任何颜色,也就是可以是“透明”的,用下面的调用可以将模式在透明和非透明之间切换:
invoke SetBkMode,hDC,OPAQUE ;非透明模式 invoke SetBkMode,hDC,TRANSPARENT ;透明模式
两种模式以及绘画颜色不单影响虚线的空隙部分,同样也影响CreateHatchBrush函数创建的画刷,因为这种画刷使用几种由线条构成的图案,当用这种画刷填充一个区域的时候,线条图案的空隙部分同样受SetBkColor函数和SetBkMode函数的影响。
2. 绘制边界框和填充区域
绘制边界框和填充区域其实是同一件事情。如果当前画笔是NULL_PEN的话,画出来的是没有边线的填充区域;如果当前画刷是NULL_BRUSH的话,那么只有边线而不会填充;如果当前画刷既不是NULL_PEN也不是NULL_BRUSH,那么画出来的图形既有边线也是填充的。
绘制区域的函数有画矩形的Rectangle,画圆角矩形的RoundRect,画多边形的Polygon,画弦的Chord,画圆饼的Pie和画椭圆的Ellipse。这些函数的使用效果见如7.4所示。
表7.4 填充函数的功能
函 数 | 说明 | 图 例 |
Rectangle(hDC,x1,y1,x2,y2) | 画以(x1,y1)和(x2,y2)为对角坐标的填充矩形 | |
RoundRect(hDC,x1,y1,x2,y2,w,h) | 画以(x1,y1)和(x2,y2)为对角坐标的填充矩形,四个角以一个小椭圆来画圆角,小椭圆的宽和高为w和h |
函 数 | 说明 | 图 例 |
Polygon(hDC,lpPoint,5) | lpPoint指向存放(x1,y1)到(x5,y5)的缓存区,函数从(x1,y1)到(x2,y2)...到(x5,y5),再回到(x1,y1),一共画5条直线并填充 | |
Chord(hDC,x1,y1,x2,y2,
x3,y3,x4,y4) | 以和Arc函数同样的方法画弧,然后连接弧的两个端点并填充 | |
Pie(hDC,x1,y1,x2,y2,
x3,y3,x4,y4) | 以和Arc函数同样的方法画弧,然后将弧的两个端点分别和椭圆中心连接并填充 | |
Ellipse(hDC,x1,y1,x2,y2) | 以(x1,y1)和(x2,y2)为对角定义一个矩形,然后画矩形相切的椭圆并填充 |
在这些函数中,Polygon的调用方式和Polyline很相似,只不过如果最后一点和第一点不同的话,函数自动再画一条和起始点相连的直线将整个区域闭合起来。用Polygon绘画的多边形中各条直线可能相交,Windows允许程序自行选择填充的模式,可以是表7.4中Polygon一栏中的上面那个图例(填充全部区域),也可以是下面那个图例(间隔填充区域)。可以用下面的函数切换填充的模式: