书接上回,我们先来看一个例子吧,我们画了一个矩形和一个圆:
caseWM_PAINT:
HDChDC;
PAINTSTRUCTps;
hDC=BeginPaint(hwnd,&ps);
Rectangle(hDC,50,50,200,200);
Ellipse(hDC,100,100,300,300);
Ellipse(hDC,400,100,600,300);
Rectangle(hDC,350,50,500,200);
EndPaint(hwnd,&ps);
break;
你会感到奇怪,这两个函数画出的图形怎么会相互遮盖呢?或许你猜出了原因了,这两个函数不是单纯地画“边框”,其内部还被白色的背景填充着,让它们可以相互遮挡而不是“透明”的。像前面讲过的Rectangle、Ellipse、RoundRect、Chord、Pie和本回要讲的Polygon、PolyPolygon这些画带边界框的填充图形函数都是这样的。
认识画刷
在Windows中图形是以当前设备描述表选中的画刷来填充的。默认情况下,使用现有的白色画刷,这意味着图形内部将画为白色。Windows预定义了六种现有画刷:WHITE_BRUSH、LTGRAY_BRUSH、GRAY_BRUSH、DKGRAY_BRUSH、BLACK_BRUSH和NULL_BRUSH (也叫HOLLOW_BRUSH),大家看名字就应该知道它们代表什么颜色了吧。你可以将任何一种现有画刷选入你的设备描述表中,就和你选择一种画笔一样。
Windows用HBRUSH定义画刷句柄,可以像这样定义一个画刷句柄变量:
HBRUSH hBrush;
你可以通过调用GetStockObject来取得GRAY_BRUSH的句柄:
hBrush = (HBRUSH) GetStockObject (GRAY_BRUSH) ;//这里需要强制类型转换
你可以调用SelectObject将它选进设备描述表:
SelectObject (hdc, hBrush) ;
现在,如果你要用上面的提到的任意一个函数画图,则图形内部将为灰色。
如果您想画一个没有边界框的图形,可以将NULL_PEN选进设备描述表:
SelectObject (hdc,(HBRUSH) GetStockObject (NULL_PEN)) ;
上一回我曾提到过NULL_PEN有啥用?我估计这可能就是它的一个用途吧。
如果您想画出图形的边界框,但不填入内部,则将NULL_BRUSH选进设备内容:
SelectObject (hdc, (HBRUSH) GetStockobject (NULL_BRUSH) ;
大家看到此,应该想出画不互相遮盖的图形了吧:
case WM_PAINT:HDC hDC;PAINTSTRUCT ps;hDC=BeginPaint(hwnd,&ps);SelectObject(hDC, (HBRUSH) GetStockObject (NULL_BRUSH) ;
Rectangle(hDC,50,50,200,200);
Ellipse(hDC,100,100,300,300);
Ellipse(hDC,400,100,600,300);
Rectangle(hDC,350,50,500,200);
EndPaint(hwnd,&ps);break;
再认识画刷
我们除了可以使用以上的现有画刷,还可以创建自己的画刷,就如同上回我们创建自己的画笔一样。而且过程也是类似的:
首先创建逻辑画刷,这里Windows为我们提供了几个函数可供使用;
然后使用SelectObject把画刷选进设备描述表;
绘图函数绘图;
最后在释放设备描述表(或者在选择了另一种画刷到设备内容中)之后,就可以调用DeleteObject来删除画刷了。
接着我们就来看一下这几个创建画刷的函数吧。
下面是建立逻辑画刷的第一个函数:
hBrush = CreateSolidBrush (crColor) ;
函数中crColor为COLORREF类型,指定画刷颜色。
你还可以使用由水平、垂直或者倾斜的线组成的“影线标记(hatch marks)”来创建画刷,这种风格的画刷对着色条形图的内部和在绘图仪上进行绘图最有用。创建影线画刷的函数为:
hBrush = CreateHatchBrush (iHatchStyle, crColor) ;
iHatchStyle参数描述影线标记的外观。下图显示了六种可用的影线标记风格。
CreateHatchBrush中的crColor参数是影线的色彩。
你还可以使用这个函数创建逻辑画刷:
hBrush = CreateBrushIndirect (&logbrush) ;
变量logbrush是一个类型为LOGBRUSH(“逻辑画刷”)的结构,该结构的三个字段如表5-4所示,lbStyle字段的值确定了Windows如何解释其它两个字段的值:
lbStyle (UINT) |
lbColor (COLORREF) |
lbHatch (LONG) |
BS_SOLID |
画刷的色彩 |
忽略 |
BS_HOLLOW |
忽略 |
忽略 |
BS_HATCHED |
影线的色彩 |
影线画刷风格 |
BS_PATTERN |
忽略 |
位图的句柄 |
BS_DIBPATTERNPT |
忽略 |
指向DIB的指标 |
上一回我们用SelectObject将逻辑画笔选进设备描述表,用DeleteObject删除画笔,用GetObject来取得逻辑画笔的信息。对于画刷,同样能使用这三个函数。
一旦你取得到了画刷句柄,就可以使用SelectObject将该画刷选进设备描述表:
SelectObject (hdc, hBrush) ;
然后,你可以使用DeleteObject函数删除所建立的画刷:
DeleteObject (hBrush) ;
但是,不要删除目前选进设备描述表内的画刷。
如果您需要取得画刷的信息,可以调用GetObject:
GetObject (hBrush, sizeof (LOGBRUSH), (LPVOID) &logbrush) ;
其中,logbrush是一个类型为LOGBRUSH的结构。
Polygon函数和多边形填充
我们好像还有两个函数没有讲,趁我还没有忘记,赶紧把它讲完吧。
Polygon函数也是一个画带边界框的填充图形函数,它的调用与Polyline函数相似:
Polygon (hdc, apt, iCount) ;
其中,apt参数是POINT结构的一个数组,iCount是点的数目。如果该数组中的最后一个点与第一个点不同,则Windows将会再加一条线,将最后一个点与第一个点连起来(在Polyline函数中,Windows不会这么做)。
PolyPolygon函数如下所示:
PolyPolygon (hdc, apt, aiCounts, iPolyCount) ;
该函数绘制多个多边形。最后一个参数给出了所画的多边形的个数。对于每个多边形,aiCounts数组给出了多边形的端点数。apt数组具有全部多边形的所有点。除返回值以外,PolyPolygon在功能上与下面的代码相同:
for (i = 0, iAccum = 0 ; i < iPolyCount ; i++)
{
Polygon (hdc, apt + iAccum, aiCounts[i]) ;
iAccum += aiCounts[i] ;
}
对于Polygon和PolyPolygon函数,Windows使用定义在设备描述表中的当前画刷来填充这个带边界的区域。至于填充内部的方式,则取决于多边形填充方式,你可以用SetPolyFillMode函数来设定:
SetPolyFillMode (hdc, iMode) ;
默认情况下,多边形填入方式是ALTERNATE,但是您可以将它设定为WINDING。
对于ALTERNATE方式,您可以设想从一个无穷大的封闭区域内部的点画线,只有假想的线穿过了奇数条边界线时,才填充封闭区域,当然若为偶数,则不填充该区域。这就是星的角被填充而中心没被填充的原因。
对于WINDING方式:方法一样,如为奇数,填充该区域;如为偶数则要根据边框线的方向来判断:如果穿过的边框线在不同方向的边框线数目相等,则不填充,如不等,则填充。还是来分析个例子吧,比如要绘制下图,线上的箭头指出了画线的方向。
用这两种方式填充的效果如下图(左侧是ALTERNATE方式,右侧是WINDING方式):
两种方式都会填充三个封闭的L形区域,号码从1到3。号码为4和5的两个小内部区域,在ALTERNATE方式下不会被填充。但是,在WINDING方式下,号码为5的区域会被填充,因为从区域内必须穿过两条相同方向的线才能到达图形外部。号码为4的区域不会被填充,因为必须穿过两条方向相反的线。
经过这两节绘图课后,大家应该对Windows中常用的绘图函数有所了解了,发挥你的想象力,去画一些漂亮的图形吧!