第5章,[Win32 章节] :Polygon 函数和多边形填充模式

专栏导航

上一篇:第5章,Win32 章节 :绘制填充区域

回到目录

下一篇:第5章,Win32 章节 :用画刷填充内部

本节前言

对于本节所讲解的知识,有可能,你会需要时不时地参考本专栏的其它文章。真的遇到了需要参考之前的文章的知识点,请你自行查阅。

我呢,也会提到一部分的参考课节。但是呢,你不应该依赖于我的主动提及。最好呢,你自己能够多去了解和查看本专栏目录。

多边形填充区域,这算是 一个很有趣的东西了。

我在写作本节的时候,我特地用 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 程序中的比例缩放技巧。

本节结束。

专栏导航

上一篇:第5章,Win32 章节 :绘制填充区域

回到目录

下一篇:第5章,Win32 章节 :用画刷填充内部

相关推荐
wuminyu19 分钟前
Java FFM处理网络读写事件源码剖析
java·linux·c语言·jvm·c++
百事牛科技37 分钟前
文档不想再加密了?Word打开密码移除的两种方法
windows·word
一木 之林1 小时前
C/C++ 面向对象编程(OOP)(进阶)
java·c语言·c++
千谦阙听2 小时前
C++篇:类和对象(上)
开发语言·c++
LONGZETECH2 小时前
新能源汽车动力电池实训教学痛点与虚拟仿真技术解决方案
c语言·3d·unity·架构·汽车·汽车教学软件
_Narcissus_3 小时前
分治&递归
数据结构·c++·笔记·算法·leetcode·递归·分治
软件资深者3 小时前
千千静听典藏修复版 使用教程:经典本地音乐播放器回归,歌词同步、皮肤皮肤自由换,音乐播放器怀旧新手 5 分钟上手(2026)
windows
牛油果子哥q3 小时前
C++ Socket精讲:TCP/UDP套接字、客户端服务端实现、字节序、粘包拆包、简单HTTP服务、网络踩坑与面试全解
网络·c++·tcp/ip
程与留4 小时前
12_常用控件速查(下):QSpinBox、QSlider、QProgressBar、QCheckBox
c++·qt