专栏导航
本节前言
对于本节所讲解的知识,有可能,你会需要时不时地参考本专栏的其它文章。真的遇到了需要参考之前的文章的知识点,请你自行查阅。
我呢,也会提到一部分的参考课节。但是呢,你不应该依赖于我的主动提及。最好呢,你自己能够多去了解和查看本专栏目录。
多边形填充区域,这算是 一个很有趣的东西了。
我在写作本节的时候,我特地用 VS2019 画了两个课件中的图片。
我们开始吧。
本节内容,来自于对佩措尔德教材的大段抄录。
一. 本节内容
前面已讨论过 5 个区域填充函数,Polygon 是第六个画带边框线并填充图形的函数,这个函数的调用类似于 Polyline 函数的调用。
Polygon (hdc, apt, iCount);
其中,参数 apt 是一个 POINT 结构的数组,iCount 是点的个数。如果数组中最后一个点与第一个点不同,则 Windows 会再加一条线连接最后一个点与第一个点。(在 Polyline 函数中不会这么做。)
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(螺旋)。这两种方式的区别如下图所示。
图1
首先,ALTERNATE 和 WINDING 模式的区别看起来似乎相当简单。对于 ALTERNATE 模式,你可以想象从某个封闭区域中的一个点向无穷远处画一条射线。只有该射线穿越奇数条边框线时,封闭区域才会被填充。这就是为什么该五角星的角会被填充,而中心未被填充的原因。
这个五角星的例子让使用 WINDING 模式看上去比实际情况更简单。在绘制一个多边形时,WINDING 模式在大多数情况下会填充所有封闭区域,但是也有例外。
在 WINDING 模式下,要确定一个区域是否应该被填充,同样可以设想从区域内的一个点画一条伸向无穷远的射线。如果射线穿过奇数条边框线,则区域被填充,这和 ALTERNATE 模式相同。如果射线穿过偶数条边框线,情况比较复杂,还要考虑到边框线的绘制方向:在被穿越的偶数条边框线中,不同方向的边框线(相对于射线的方向)的数目如果相等,则区域不会被填充;不同方向的边框线(相对于射线的方向)的数目如果不相等,则区域会被填充。
我们来看看下面的图。
图2
在图2 中,线上的箭头表示画线的方向。 WINDING 模式和 ALTERNATE 模式都会填充三个 L 型区域,号码 1 到 3 。两个更小的内部区域,号码为 4 和 5,在 ALTERNATE 模式下就不被填充个。而在 WINDING 模式下,号码 5 的区域会被填充,这是因为从区域的内部到达图形的外部必须穿过两条相同方向的线。号码为 4 的区域不会被填充,因为射线必须穿越两条边框线,但是这两条边框线得绘制方向相反。
我们在下一分节,展示与图2 相关的程序,我们会用多边形函数 Polygon 来绘制图2 所示的图的边框线和填充其内部区域,填充模式既有 ALTERNATE,也有 WINDING 。
二. 本节程序,AltWind
代码如下。
/*-----------------------------------------------
ALTWIND.C -- Alternate and Winding Fill Modes
(c) Charles Petzold, 1998
水饺编程改编,2026
-----------------------------------------------*/
#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("注册窗口类失败!"),
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 填充模式。
本节的程序,使用了一个 Windows 编程中的常用技巧,这个技巧,就是比例缩放技巧。
这种比例缩放代码,如下面的截图所示。
图3
这里的比例缩放技巧,它是 Windows 编程中常用的一种技巧,是一个重难点知识。对于本节程序中所涉及的比例缩放技巧,我暂时不想讲。以后,我找个机会,再来讲解它吧。
程序的编译和运行方法,如果你忘了,可以参照下述的两篇文章。
参考课节:使用 VS2010 编写 C语言程序
参考课节:用 VS2019 编写C语言程序
运行结果如下图所示。
图4
结束语
本节代码中,涉及了比例缩放技巧。
这个技巧,算是学习 Windows 编程的时候,需要去克服的一个重难点了。
在我写作的当前,我暂时没有去展开讲解。
以后有机会,我会单独设置一个课节,来讲解 AltWind 程序中的比例缩放技巧。
本节结束。