Windows绘制点与线

我们谈论过Windows图形设备接口将图形输出设备的设备驱动程序与计算机连在一起的方式。在理论上,只要提供SetPixel和GetPixel函数,就可以使用图形设备驱动程序绘制一切东西了。其余的一切都可以使用GDI模块中实作的更高阶的例程来处理。例如,画线时,只需GDI呼叫SetPixel数次,并适当地调整x和y坐标。

在实际情况中,也的确可以仅使用SetPixel和GetPixel函数进行您需要的任何绘制。您也可以在这些函数的基础上设计出简洁和构造良好的图形编程系统。唯一的问题是启能效率。如果一个函数通过几次呼叫才能到达SetPixel函数,那么它执行起来会非常慢。如果一个图形系统画线和进行其它复杂的图形操作是在设备驱动程序的层次上,它就会更有效得多,因为设备驱动程序对完成这些操作的程序代码进行了最佳化。此外,一些显示卡包含了图形协处理器,它允许视讯硬件自己绘制图形。

设定图素

即使Windows GDI包含了SetPixel和GetPixel函数,但很少使用它们。尽管如此,由它们开始来研究图形仍是非常方便。

SetPixel函数在指定的x和y坐标以特定的颜色设定图素:

SetPixel (hdc, x, y, crColor) ;

如同在任何绘图函数中一样,第一个参数是设备内容的句柄。第二个和第三个参数指明了坐标位置。通常要获得窗口显示区域的设备内容,并且x和y相对于该显示区域的左上角。最后一个参数是COLORREF型态指定了颜色。如果在函数中指定的颜色视讯显示器不支持,则函数将图素设定为最接近的纯色并从函数传回该值。

GetPixel函数传回指定坐标处的图素颜色:

crColor = GetPixel (hdc, x, y) ;

直线

Windows可以画直线、椭圆线(椭圆圆周上的曲线)和贝塞尔曲线。

  • LineTo 画直线。 
  • Polyline和PolylineTo 画一系列相连的直线。
  • PolyPolyline 画多组相连的线。 
  • Arc 画椭圆线。 
  • PolyBezier和PolyBezierTo 画贝塞尔曲线。 

另外,Windows NT还支持3种画线函数:

  • ArcTo和AngleArc 画椭圆线。 
  • PolyDraw 画一系列相连的线以及贝塞尔曲线。

一些既画线也填入所画图形的封闭区域的函数,这些函数是

  • Rectangle 画矩形。 
  • Ellipse 画椭圆。 
  • RoundRect 画带圆角的矩形。 
  • Pie 画椭圆的一部分,使其看起来像一个扇形。 
  • Chord 画椭圆的一部分,以呈弓形。

设备内容的五个属性影响着用这些函数所画线的外观:目前画笔的位置(仅用于LineTo、PolylineTo、PolyBezierTo和ArcTo )、画笔、背景方式、背景色和绘图模式

画一条直线,必须呼叫两个函数。第一个函数指定了线的开始点,第二个函数指定了线的终点:

MoveToEx (hdc, xBeg, yBeg, NULL) ;        
LineTo (hdc, xEnd, yEnd) ;

MoveToEx实际上不会画线,它只是设定了设备内容的「目前位置」属性。然后LineTo函数从目前的位置到它所指定的点画一条直线。目前位置只是用于其它几个GDI函数的开始点。在内定的设备内容中,目前位置最初设定在点(0,0)。如果在呼叫LineTo之前没有设定目前位置,那么它将从显示区域的左上角开始画线。

如果需要目前位置,就可以通过以下呼叫获得:

GetCurrentPositionEx (hdc, &pt) ;

虽然用两个函数来画一条直线显得有些麻烦,但是在希望画一组相连的直线时,目前画笔位置属性又会变得很有用。例如,您可能想定义一个包含5个点(10个值)的数组,来画一个矩形的边界框:

POINT apt[5] = { 100, 100, 200, 100, 200, 200, 100, 200, 100, 100 } ;

注意,最后一个点与第一个点相同。现在,只需要使用MoveToEx移到第一个点,并对后面的点使用LineTo:

MoveToEx (hdc, apt[0].x, apt[0].y, NULL) ;        
for (i=1; i<5; i++) {   
    LineTo (hdc, apt[i].x, apt[i].y) ;
}

由于LineTo从目前位置画到(但不包括)LineTo函数中给出的点,所以这段程序代码没有在任何坐标处画两次。

当你要将数组中的点连接成线时,使用Polyline函数要简单得多。下面这条叙述画出与上面一段程序代码相同的矩形:

Polyline (hdc, apt, 5) ;

最后一个参数是点的数目。我们还可以使用(sizeof (apt) / sizeof (POINT))来表示这个值。Polyline与一个MoveToEx函数后面加几个LineTo函数的效果相同,但是,Polyline既不使用也不改变目前位置。PolylineTo有些不同,这个函数使用目前位置作为开始点,并将目前位置设定为最后一根线的终点。下面的程序代码画出与上面所示一样的矩形:

MoveToEx (hdc, apt[0].x, apt[0].y, NULL) ;        
PolylineTo (hdc, apt + 1, 4) ;

你可以对几条线使用Polyline和PolylineTo,这些函数在绘制复杂曲线最有用了。您使用由几百甚至几千条线组成的极短线段,把它们连在一起就像一条曲线一样。

#include <math.h>        
#define  NUM     1000        
#define TWOPI    (2 * 3.14159)

 case   WM_PAINT:

     MoveToEx (hdc, 0, cyClient / 2, NULL) ;       
     LineTo   (hdc, cxClient, cyClient / 2) ;
     for (i = 0 ; i < NUM ; i++) {   
         apt[i].x = i * cxClient / NUM ;
         apt[i].y = (int) (cyClient / 2 * (1 - sin (TWOPI * i / NUM))) ;
     }
     Polyline (hdc, apt, NUM) ;
    
    return 0 ;

边界框函数

下面我想讨论的是Arc函数,它绘制椭圆曲线。然而,如果不先讨论一下Ellipse函数,那么Arc函数将难以理解;而如果不先讨论Rectangle函数,那么Ellipse函数又将难以理解;而如果讨论Ellipse和Rectangle函数,那么我又会讨论RoundRect、Chord和Pie函数。

问题在于,Rectangle、Ellipse、RoundRect、Chord和Pie函数严格来说不是画线函数。没错,这些函数是在画线,但它们同时又填入画刷填入一个封闭区域。这个画刷内定为白色,因此当您第一次使用这些函数时,您可能不会注意到它们不只是画线。

上面提到的函数有一个共同特性,即它们都是依据一个矩形边界框来绘图的。您定义一个包含该对象的框,即「边界框(bounding box)」;Windows就在这个框内画出该物件。

这些函数中最简单的就是画一个矩形:

Rectangle (hdc, xLeft, yTop, xRight, yBottom) ;

点(xLeft, yTop)是矩形的左上角,(xRight, yBottom)是矩形的右下角。用函数Rectangle画出的图形如图5-6所示,矩形的边总是平行于显示器的水平和垂直边。

以前写过图形程序的程序写作者熟悉图素偏差的问题。有些图形系统画出的图形包含右坐标和底坐标,而有些则只画到(而不包含)右坐标和底坐标。Windows采用后一种方法,不过有一种更简单的方法来思考这个问题。

考虑下面的函数呼叫:

Rectangle (hdc, 1, 1, 5, 4) ;

面我们提到,Windows在边界框内画图。可以将显示器想象成一个网格,其中,每个图素都在一个网格单元内。边界框画在网格上,然后在边界框内画矩形,下面说明了图形画出来时的样子:

我以前提到过,Rectangle严格地说不是画线函数,GDI也填入封闭区域。然而,因为内定用白色填入区域,因此GDI填入区域并不明显。

知道了如何画矩形,也就知道了如何画椭圆,因为它们使用的参数都是相同的

Ellipse (hdc, xLeft, yTop, xRight, yBottom) ;

用Ellipse函数画出的图形如图所示

画圆角矩形的函数使用与函数Rectangle及Ellipse函数相同的边界框,还包含另外两个参数:

RoundRect (hdc, xLeft, yTop, xRight, yBottom,       
           xCornerEllipse, yCornerEllipse) ;

用这个函数画出的图形所示。

Windows使用一个小椭圆来画圆角,这个椭圆的宽为xCornerEllipse,高为yCornerEllipse。可以想象这个小椭圆分为了四个部分,一个象限一个,每个刚好用在矩形的一个角上。xCornerEllipse和yCornerEllipse的值越大,角就越明显。如果xCornerEllipse等于xLeft与xRight的差,且yCornerEllipse等于yTop与yBottom的差,那么RoundRect函数将画出一个椭圆。

在绘制图所示的圆角矩形时,用了下面的公式来计算角上椭圆的尺寸。

xCornerEllipse = (xRight - xLeft) / 4 ;        
yCornerEllipse = (yBottom- yTop) / 4 ;

这是一种简单的方法,但是结果看起来有点不对劲,因为角的弯曲部分在矩形长的一边要大些。要矫正这一问题,可以让xCornerEllipse与yCornerEllipse的值相等。

Arc、Chord和Pie函数都只要相同的参数:

Arc   (hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd) ;        
Chord (hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd) ;        
Pie   (hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd) ;

用Arc函数画出的线如图所示;用Chord和Pie函数画出的线分别如图5-10和5-11所示。Windows用一条假想的线将(xStart, yStart)与椭圆的中心连接,从该线与边界框的交点开始,Windows按反时针方向,沿着椭圆画一条弧。Windows还用另一条假想的线将(xEnd,yEnd)与椭圆的中心连接,在该线与边界框的交点处,Windows停止画弧。

对于Arc函数,这样就结束了。因为弧只是一条椭圆形的线而已,而不是一个填入区域。对于Chord函数,Windows连接弧线的端点。而对于Pie函数,Windows将弧的两个端点与椭圆的中心相连接。弦与扇形图的内部以目前画刷填入。

你可能不太明白在Arc、Chord和Pie函数中开始和结束位置的用法,为什么不简单地在椭圆的周在线指定开始和结束点呢?是的,可以这么做,但是将不得不算出这些点。Windows的方法在不要求这种精确性的条件下,却完成了相同的工作。

贝塞尔曲线

「曲尺」这个词从前指的是一片木头、橡皮或者金属,用来在纸上画曲线。比如说,如果您有一些不同图点,您想要在它们之间画一条曲线(内插或者外插),您首先将这些点描在绘图纸上,然后,将曲尺定在这些点上,并用铅笔沿着曲尺绕着这些点弯曲的方向画曲线。

当然,时至今日,曲尺已经数学公式化了。有很多种不同的曲尺公式,它们各有千秋。贝塞尔曲线是计算机程序设计中用得最广的曲尺公式之一,它是直到最近才加到操作系统层次的图形支持中的。在六十年代Renault汽车公司进行了由手工设计车体(要用到粘土)到计算机辅助设计的转变。他们需要一些数学工具,而Pierm Bezier找到了一套公式,最后显示出这套公式应付这样的工作非常有用。

此后,二维的贝塞尔曲线成了计算机图学中最有用的曲线(在直线和椭圆之后)。在PostScript中,所有曲线都用贝塞尔曲线表示-椭圆线用贝塞尔曲线来逼近。贝塞尔曲线也用于定义PostScript字体的字符轮廓(TrueType使用一种更简单更快速的曲尺公式)。

一条二维的贝塞尔曲线由四个点定义-两个端点和两个控制点。曲线的端点在两个端点上,控制点就好像「磁石」一样把曲线从两个端点间的直线处拉走。

在32位的Windows版本之前,你必须利用Polyline来自己建立贝塞尔曲线,并且还需要知道下面的贝塞尔曲线的参数方程。起点是( x0,y0),终点是( x3,y3),两个控制点是(x1,y1)和(x2,y2),随着t的值从0到1的变化,就可以画出曲线:

x(t) = (1 - t)3 x0 + 3t (1 - t)2 x1 + 3t2 (1 - t) x2 + t3 x3       
y(t) = (1 - t)3 y0 + 3t (1 - t)2 y1 + 3t2 (1 - t) y2 + t3 y3

现在不需要知道这些公式。要画一条或多条连接的贝塞尔曲线,只需呼叫:

PolyBezier (hdc, apt, iCount) ;

PolyBezierTo (hdc, apt, iCount) ;

两种情况下,apt都是POINT结构的数组。对PolyBezier,前四个点(按照顺序)给出贝塞尔曲线的起点、第一个控制点、第二个控制点和终点。此后的每一条贝塞尔曲线只需给出三个点,因为后一条贝塞尔曲线的起点就是前一条贝塞尔曲线的终点,如此类推。iCount参数等于1加上您所绘制的这些首尾相接曲线条数的三倍。

PolyBezierTo函数使用目前点作为第一个起点,第一条以及后续的贝塞尔曲线都只需要给出三个点。当函数传回时,目前点设定为最后一个终点。

一点提示:在画一系列相连的贝塞尔曲线时,只有当第一条贝塞尔曲线的第二个控制点、第一条贝塞尔曲线的终点(也就是第二条曲线的起点)和第二条贝塞尔曲线的第一个控制点线性相关时,也就是说这三个点在同一条直线上时,曲线在连接点处才是光滑的。

发布了25 篇原创文章 · 获赞 8 · 访问量 420

猜你喜欢

转载自blog.csdn.net/cix1314/article/details/103626286