四、Windows图形处理—绘制填入区域(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。两种方式的区别参见图5-15所示。
首先,ALTERNATE和WINDING方式之间的区别很容易察觉。对于ALTERNATE方式,您可以设想从一个无穷大的封闭区域内部的点画线,只有假想的线穿过了奇数条边界线时,才填入封闭区域。这就是填入了星的角而中心没被填入的原因。
五角星的例子使得WINDING方式看起来比实际上更简单一些。在绘制单个的多边形时,大多数情况下,WINDING方式会填入所有封闭的区域。但是也有例外。
在WINDING方式下要确定一个封闭区域是否被填入,您仍旧可以设想从那个无穷大的区域画线。如果假想的线穿过了奇数条边界线,区域就被填入,这和ALTERNATE方式一样。如果假想的线穿过了偶数条边界线,则区域可能被填入也可能不被填入。如果一个方向(相对于假想线)的边界线数与另一个方向的边界线数不相等,就填入区域。
例如,考虑图5-16中的物体。在线的箭头指出了画线的方向。两种方式都会填入三个封闭的L形区域,号码从1到3。号码为4和5的两个小内部区域,在ALTERNATE方式下不会被填入。但是,在WINDING方式下,号码为5的区域会被填入,因为从区域内必须穿过两条相同方向的线才能到达图形外部。号码为4的区域不会被填入,因为必须穿过两条方向相反的线。
如果您怀疑Windows没有这么聪明,那么程序5-5 ALTWIND会展示给您看。
程序5-5 ALTWIND ALTWIND.C /*------------------------------------------------------------------- ALTWIND.C -- Alternate and Winding Fill Modes (c) Charles Petzold, 1998 -------------------------------------------------------------------*/ #include <windows.h> LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("AltWind") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc= WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground= (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName= NULL ; wndclass.lpszClassName= szAppName ; if (!RegisterClass (&wndclass)) { MessageBox ( NULL, TEXT ("Program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Alternate and Winding Fill Modes"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static POINT aptFigure [10] = {10,70, 50,70, 50,10, 90,10, 90,50, 30,50, 30,90, 70,90, 70,30, 10,30 }; static int cxClient, cyClient ; HDC hdc ; int i ; PAINTSTRUCT ps ; POINT apt[10] ; switch (message) { case WM_SIZE: cxClient = LOWORD (lParam) ; cyClient = HIWORD (lParam) ; return 0 ; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; SelectObject (hdc, GetStockObject (GRAY_BRUSH)) ; for (i = 0 ; i < 10 ; i++) { apt[i].x = cxClient * aptFigure[i].x / 200 ; apt[i].y = cyClient * aptFigure[i].y / 100 ; } SetPolyFillMode (hdc, ALTERNATE) ; Polygon (hdc, apt, 10) ; for (i = 0 ; i < 10 ; i++) { apt[i].x += cxClient / 2 ; } SetPolyFillMode (hdc, WINDING) ; Polygon (hdc, apt, 10) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
图形的坐标(划分为100×100个单位)储存在aptFigure数组中。这些坐标是依据显示区域的宽度和高度划分的。程序显示图形两次,一次使用ALTERNATE填入方式,另一次使用WINDING方式。结果见图5-17。