云计算百科
云计算领域专业知识百科平台

第5章,[Win32 章节] :练习程序,LittleFontPic

专栏导航

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

回到目录

下一篇:第5章,[Win32 章节] :练习程序,BigFontPic

本专栏课件

关于本专栏课件的获取方法,请参考下述课节。

参考课节:MFC笔记:本专栏课件

本节前言

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

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

经过之前的几个课节的讲解,我们已经是将 GDI 画笔和画刷,以及其他的许多的基础 GDI 知识给讲完了。学到这种程度,我们可以画一些个图形了。

从本节课开始,我们来领着大家,一起来学习一些个绘制图形的代码,从中来学习 GDI 绘图的逻辑,学习 GDI 画笔、画刷等等的 GDI 对象的使用方法与流程,学习 GDI 对象的创建、选入设备环境、选出设备环境、删除等等的流程,并由此来体验 Windows 编程的专业性。

我本人的 PhotoShop 学得一般,有些个图画,我觉得很难画出来。然而,有了 Windows 编程,我倒是可以通过编程的方式,来绘制出一些个图形来。

我想,用 Windows 程序设计的知识来绘制图表,应该会对各位也能有一定的帮助的。

本节内容,是我的原创内容,而非佩措尔德原教材的内容。

一.    佩措尔德原教材插图分析

在之前的某个课节,我们在课节中给出了下述的图片。

图1

这一课节的链接如下。

参考课节:第4章,[标签 Win32] :文本尺寸的度量

接下来,我来将佩措尔德原教材的图片内容给截图,然后贴在下面。

图2,佩措尔德教材原图

佩措尔德原教材中的插图,我没有通过截图的方式,将其放置在参考课节中,而是通过编程的方式,将它的基本的东西绘制出来,然后通过 PhotoShop 进行补充绘制。绘制好了以后,放在了课节里面。

下面的图片,是我用编程的方式,绘制出来的基本图。

图3

在编程之前,我是自己用铅笔,结合着观察,去给佩措尔德的原教材的插图查数,查出了原插图中,一行里面,有多少个模拟像素方格,一列里面,有多少个模拟像素方格。

查询的结果是,佩措尔德原教材的插图中,一行有 15 个像素方格,一列有 16 个方格。

想要将其绘制出来,我需要绘制一个方格矩阵,一个 16 行、15 列的方格矩阵。

在这个方格矩阵中,有的方格需要涂上颜色,有的则是不需要涂颜色。涂色的部分,可以统一地采用同一种颜色。这样一来,整个的方格矩阵,我们可以将其分成两个部分,一个是不涂色的部分,我们可以将其标记为 0;另一个部分是涂色的部分,我们可以将其标记为 1 。

我们可以建立一个二维数组,它的一维的个数,对应着方格矩阵的列数,15 列;它的二维的个数,对应着方格矩阵的行数,16 行。

这个二维数组的名字如下。

BOOL Square[16][15];

Square[3][10] 代表着原插图中 3 号行、10 号列的模拟像素方格,由于数组元素的索引从 0 开始,所以呢,它实际上对应着原插图中的第 4 行、第 11 列的模拟像素方格。其值,代表着这个模拟像素方格是否涂色,1 则涂色,0 则不涂色。

同理,Square[6][7] 代表着原插图中 6 号行、7 号列的模拟像素方格,由于数组元素的索引从 0 开始,所以呢,它实际上对应着原插图中的第 7 行、第 8 列的模拟像素方格。其值,代表着这个模拟像素方格是否涂色,1 则涂色,0 则不涂色。

这样一来,我们就通过二维数组,对模拟像素方格矩阵的涂色与否,进行了建模。

去编程的时候,我们的专栏中给出的图片,为了让大家较为清楚地看出原插图的行列,我们用虚线,对框架进行了绘制。用虚线去绘制,而不是用实线去绘制,所以,我们需要自定义画笔,需要使用画笔句柄 hPen 。

对于模拟像素方格矩阵的 16 行、15 列的框架,我们采用 MoveToEx 和 LineTo 函数,通过画线的方式,将其绘制了出来。如图1 所示,我们在绘制框架的时候,线条的颜色是红色。由此,我们还需要自行创建画刷,并将画刷的颜色设置为红色。

在具体绘制的时候,我们是先用 MoveToEx 和 LineTo 函数,来将模拟像素方格矩阵的框架给绘制了出来。

框架画完了以后,我们接着去给方格矩阵涂色。

如前所述,我们已经是通过二维数组 Square[6][7],来对原插图中的模拟像素方格矩阵进行了建模。原插图的任何一个像素方格,它都要与二维数组中的元素一一对应。并且呢,如果原插图中的某一位置涂色或不涂色,也都要与二维数组的对应元素的值相对应,不得有误。原插图中的某一个模拟像素方格不涂色,则要在二维数组中将某一元素标记为 0; 原插图中的某一个模拟像素方格涂色,则要在二维数组中将某一元素标记为 1。 

对于二维数组 Square[6][7],我们是在声明的时候,直接对其进行了初始化。这样,在给模拟像素方格矩阵涂色的时候,我们是通过两层的 for 循环,来遍历 Square[6][7] 的每一个元素,根据元素值的 0 和 1,来决定是否对某一个模拟像素方格涂色。

在给模拟像素方格涂色的时候,如图1 所示,我们所涂的颜色,是蓝色。这个呢,就需要另外一个画刷了。

同一个程序中,我们的确是可以使用两个画刷句柄变量,来建立两个自定义画刷。不过,我们实际上只使用了一个画刷句柄。在程序中,我们声明的画刷句柄有两个,一个是 hBrush,另一个是 hOldBrush 。但是呢,用来表示自定义画刷的变量,实际上只有一个,那就是 hBrush 。另一个画刷句柄变量 hOldBrush,它承担着辅助职能。

关于 hOldBrush,我是在将 hBrush 画刷选入设备环境之时,用 hOldBrush 来保存旧画刷,然后在完成了一段绘制任务以后,用 hOldBrush 来将 hBrush 画刷给选出设备环境。自始至终,hOldBrush 只是在承担着辅助职能,而非用来表示某一个具体的画刷。

在绘制模拟像素方格矩阵框架的时候,我们使用的是红色的自定义画刷,这个画刷的句柄保存在变量 hBrush 之中。绘制完了框架以后,我们又将这个红色的自定义画刷给选出了设备环境,并将这个红色画刷给删除了。然后呢,我们新建了一个自定义画刷,这次的画刷是一个蓝色的画刷,我们将其又保存在了 hBrush 之中。然后呢,将其选入设备环境,然后就是在两层的 for 循环之中,根据 Square[6][7] 的值,来对模拟像素方格矩阵中的方格进行涂色。涂色过程完结之后,我们又将蓝色画刷给选出设备环境,然后将蓝色画刷给删除。

在给模拟像素方格矩阵涂色的时候,我考虑到,如果让涂色的部分与像素方格的边界重合,则会造成模拟像素方格矩阵的框架与涂色部分重合与混淆的问题。为此,我在给某个模拟像素方格涂色的时候,特意令涂色的部分,比矩阵框架的方格外框,向内缩减了少许像素的大小。内缩的像素数,也许,仍然不是特别地清晰,但是,还算是能够辨识。

另外,在绘制模拟像素方格矩阵框架的时候,我用的是 MoveToEx 和 LineTo 函数,使用的线条是红色的虚线,为此,我使用了自定义画笔,并将其设置为红色、PS_DASHDOT 风格。而在涂色的时候,我使用了 Rectangle 函数,涂色时,不需要绘制边框,因此,我使用了 NULL_PEN 备用画笔。在画笔方面,我也是使用了两个变量,一个用来表示实际使用的画笔,它由 hPen 变量来表示。另一个是辅助画笔变量,它由 hPenOld 变量来表示。

hPen 与 hPenOld 的使用方法,与 hBrush、hOldBrush 的使用方法类似。都是说,实际想要使用某一画笔之时,或者是创建一个自定义画笔,或者是获取系统备用画笔,并将这个画笔呢,用 hPen 来保存,然后将其选入设备环境。选入设备环境之时,用 hPenOld 来保存旧画笔。接下来是进行绘制任务,完成了一段绘制任务以后,通过 hPenOld 来将 hPen 给选出设备环境。然后呢,要看 hPen 表示的画笔,是系统备用画笔还是自定义画笔,如果是系统备用画笔,则无须删除它所代表的 GDI 对象。如果是自定义画笔,则还需要删除 hPen 所对应的 GDI 对象。

以上,就是我对于本节要展示的程序代码的整体思路的大致分析了。

有可能,此时,你还不能够完全理解上面的整体分析的含义。那么,大致看看就好。等你对本课节的代码,完整地阅读了以后,再来对照着看看本分节的整体思路分析,多看几遍,我想,你应该能看懂。

在下一分节,我们来展示代码。

二.    本节程序代码

本节程序,我是用 VS2019 来编译的。解决方案与项目的名称,为 LittleFontPic 。代码文件包含有两个,一个是头文件 FontPic.h,另一个是源文件 WinMain.cpp 。

接下来,我们来展示代码。

(一)头文件 FontPic.h

代码见下面的代码块。

#ifndef _FONTPIC_H_
#define _FONTPIC_H_

BOOL Square[16][15] =
{
0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1,
0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1,
0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1,
1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1
};

#endif //

(二)源文件 WinMain.cpp

代码如下。

/*———————————————————
小字体点阵图画
水饺编程,2026
———————————————————*/

#include <windows.h>
#include "FontPic.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("LittleFontPic");
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("小字体点阵图画"),
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 int xStart, xEnd, yStart, yEnd, nLength;
HDC hdc;
int i, j, x, y;
PAINTSTRUCT ps;
static COLORREF clrLine, clrSquare;
HPEN hPen, hPenOld;
HBRUSH hBrush, hOldBrush;
RECT rect;

switch (message)
{
case WM_CREATE:
nLength = 27;
xStart = 200;
xEnd = xStart + 15 * nLength;
yStart = 60;
yEnd = yStart + 16 * nLength;

clrLine = RGB(255, 0, 0);
clrSquare = RGB(0, 0, 255);

return 0;

case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);

// 创建并选入画笔
hPen = CreatePen(PS_DASHDOT, 0, clrLine);
hPenOld = (HPEN)SelectObject(hdc, hPen);

// 绘制点线方格,17个横线,16个竖线。
for (i = 0, y = yStart; i < 17; i++)
{
MoveToEx(hdc, xStart, y, NULL);
LineTo(hdc, xEnd, y);
y += nLength;
}

for (i = 0, x = xStart; i < 16; i++)
{
MoveToEx(hdc, x, yStart, NULL);
LineTo(hdc, x, yEnd);
x += nLength;
}

// 创建画刷并选入设备环境
hBrush = CreateSolidBrush(clrSquare);
hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);

// 将画笔选出设备环境,并销毁,然后新建一个备用画笔 NULL_PEN。
SelectObject(hdc, hPenOld);
DeleteObject(hPen);
hPen = (HPEN)GetStockObject(NULL_PEN);
hPenOld = (HPEN)SelectObject(hdc, hPen);

for (i = 0; i < 16; i++)
for (j = 0; j < 15; j++)
if (Square[i][j])
{
rect.left = xStart + j * nLength + 2;
rect.right = xStart + (j + 1) * nLength – 1;
rect.top = yStart + i * nLength + 2;
rect.bottom = yStart + (i + 1) * nLength – 1;
Rectangle(hdc, rect.left, rect.top,
rect.right, rect.bottom);
}

// 将画刷选出设备环境并销毁
SelectObject(hdc, hOldBrush);
DeleteObject(hBrush);

// 将画笔选出设备环境,由于此时 hPen 是备用
// 画笔,因而不能销毁。
SelectObject(hdc, hPenOld);

EndPaint(hwnd, &ps);
return 0;

case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}

(三)部分代码的讲解

(1)强制类型转换

在调用 GetStockObject 和 SelectObject 函数的时候,如果我们需要将它们的函数返回值给保存在某一个变量里面,我们都对其进行了强制类型转换。这是因为,这俩函数的返回值,是 GDI 对象的通用类型,HGDIOBJ,而不是某一个具体的类型,比如画笔句柄类型 HPEN,或者画刷句柄类型 HBRUSH,等等。由于这俩函数返回的,都是大类型 HGDIOBJ,所以呢,我们都使用强制类型转换,来处理了返回值,比如下述的几个代码。

图4

图5

这种强制类型转换,这算是 GDI 绘图常见的流程。初学者,可能还不太熟悉。但是呢,随着你对本专栏的阅读经验的增长,这种强制类型转换,你会熟悉的。

(2)选入设备环境和选出设备环境

图6

这是创建画笔并将其选入设备环境的代码。

为啥创建了画笔之后要将其选入设备环境呢?因为,只有将其选入了设备环境以后,接下来,在使用 GDI 函数绘图的时候,才会使用这个画笔。GDI 函数在绘图的时候,无论是使用 MoveToEx、LineTo 函数对,还是使用 Rectangle、Ellipse 和 RoundRect 等等的绘图函数,在绘制线条的时候,都用的是选入设备环境之中的那个画笔所绘制的线条。

如果呢,你在程序中,没有单独地创建画笔并将其选入设备环境,那么,你在绘图的时候,将会采用默认画笔,BLACK_PEN,这是系统的一个备用画笔。

在图6 代码区域的下面,紧接着的是绘制像素方格矩阵框架的函数,如下图所示,

图7

我们要绘制的模拟像素方格矩阵,它是 16 行、15 列的方格矩阵,所以呢,绘制它需要 17 个横线、16 个竖线。

图7 中的两个 for 循环执行完了以后,绘制模拟像素方格矩阵这一任务也就完成了。完成了以后,实际上,图6 中所创建的 PS_DASHDOT、RGB(255, 0, 0) 所对应的红色风格的线条也就完成了它的使命。接下来,我们需要对模拟像素方格矩阵进行涂色了。而在涂色的时候,我们用的函数是 Rectangle,而这一函数所用的线条是 NULL_PEN,也就是无边框。

由于图7 之后的画图任务,需要使用 NULL_PEN 风格的画笔,因而,图6 中的自定义画笔就需要被选出设备环境了。为此,我们来看下一段代码。

图8

图6 中,我们创建了自定义画笔,并将画笔句柄保存在了 hPen 里面。由于它是自定义画笔,所以呢,我们需要在调用 SelectObject 来将 hPen 选出设备环境以后,用 DeleteObject 函数将 hPen 所代表的 GDI 画笔对象给删除掉。

为啥要删除掉?如果不予以删除,那么,在我们只用 hPen 一个画笔句柄变量来保存画笔的情况下,图8 中的 108 行代码中对 hPen 的重新赋值,将会导致图6 中创建的自定义画笔的句柄丢失,丢失了以后,那个 PS_DASHDOT、红色风格的自定义画笔对象,就会驻留在内存中,白白地占着地方。内存资源很宝贵,如果我们创建的程序是一个大程序,里面所使用 GDI 对象会有好多。如果各个都使用完了以后不及时删除,它们就会白白地占用着庞大的内存。随着程序的运行,你的内存慢慢地就不够用了。使用完了某个 GDI 对象以后,及时将其选出设备环境并删除,这是对内存资源的节约利用,就像我们平时生活中的节约用水和用电一样,用完了水龙头要及时关上,用完了电也要及时关上。

关于选出设备环境,其实它算是一种规范性的流程了。在本节程序中,以及以后我们会见到的许多的 GDI 绘图任务中,用完了某一个 GDI 对象,我们是需要将其及时地选出设备环境的。对于选出设备环境的 GDI 对象,如果是自定义的 GDI 对象,还要及时将其删除,以节约利用内存资源。如果是备用对象,则不可以将其删除。

在图7 中的两个 for 循环所执行的绘制模拟像素方格矩阵的任务完成了以后,我们接下来要去进行的是对模拟像素方格矩阵中的某些个模拟像素方格进行涂色。涂色的时候,我们要用到的函数是 Rectangle 。想要用它来给模拟像素方格涂色,我们还需要使用 GDI 画刷。涂什么颜色,就用什么颜色画刷。

为此,我们在下述截图所示的代码中,创建并选入了自定义画刷。

图9

接下来呢,是用两层 for 循环代码,来对模拟像素方格进行涂色,代码截图如下所示。

图10

代码本身不难,不过,你可能会对每个小矩形的左上加2、右下减1 有所疑惑,这一点,我们在后面的某个小节来讲解。此处,暂且不表。

图10 所示的涂色代码完成了以后,图8 的108、109 行代码中获取系统备用对象并选入设备环境的画笔,我们就用完了。同时,图9 的 102、103 行所创建并选入设备环境的画刷,我们也用完了。

既然用完了,那就需要将其选出设备环境。由于 hPen 画笔是备用对象,因此在选出设备环境后,无须删除它,也不能删除它。而画刷 hBrush 是自定义对象,因此在选出设备环境后,需要删除它。

将用完了的 GDI 画笔和画刷选出设备环境及删除对象的代码,如下图所示。

图11

在这里,对于删除 GDI 对象,我们没有像佩措尔德原教材中所讲解的那样,在 WM_DESTROY 中去执行 DeleteObject 函数来执行删除任务,而是在用完了某个 GDI 对象之后,及时删除清理。这种用法,其实,我也是跟佩措尔德学的。在后续的许多的原教材的代码中,佩措尔德先生也是说,用完了某个 GDI 自定义对象之后,及时删除它,而不是非得等到 WM_DESTROY 消息处理的时候才来执行 DeleteObject 函数来执行删除任务。

对于这种,用完了以后立即清理的编程思路,大家需要去体会和熟悉。你不见得能够深刻领悟其含义,但是,要会用。知道佩措尔德是这么用的,广大的 Windows 程序员是这么用的,我们也跟着这么用,就好了。

(3)涂色的时候,对 rect 的尺寸的设置

我们重新将图10 的代码截图贴在下面。

图10 副本

在图10 中,j 是模拟像素方格矩阵中,每一个竖线的编号。在整个的模拟像素方格矩阵框架中,最左侧的竖线,我们将其视为 0 号,再往右一个是 1 号竖线,再往右一个是 2 号竖线等等。

而 i 是模拟像素方格矩阵中,每一个横线的编程的编号。在整个的模拟像素方格矩阵框架中,最上侧的横线,我们将其视为 0 号,再往下一个是 1 号横线,再往下一个是 2 号横线等等。

i 和 j 也同时用来在 Square[i][j] 数组元素中,用来表示某一个模拟像素方格。Square[i][j] 用来表示 i 号行、j 号列的像素方格,模拟像素方格的行列号都是从 0 开始编号。这一点,我们在本篇文章的第一分节中有讲述,忘了的,请参考第一分节的相关内容。

对于某一个模拟像素方格,如果它是 3 号列,则这个方格的左侧的竖线是 3 号竖线,右侧的竖线是 4 号竖线。对于一个属于 j 号列的模拟像素方格,它的左侧和右侧的竖线的编号分别是 j 号竖线和 (j+1) 号竖线。

对于某一个模拟像素方格,如果它是 6 号行,则这个方格的上侧的横线是 6 号横线,下侧的横线是 7 号横线。对于一个属于 i 号行的模拟像素方格,它的上侧和下侧的横线的编号分别是 i 号横线和 (i+1) 号横线。

如图10 所示,我们在设置 rect 的 left、top、right、bottom 四个字段的时候,分别进行了左上加2、右下减1 的设定。为什么这样呢?

再用画刷填充模拟像素方格的时候,我希望,涂色的部分与四周的用 MoveToEx、LineTo 函数对绘制的模拟像素方格矩阵框架之间,能够空出来一点像素,而不是说涂色的部分将模拟像素方格四周的框架给完全覆盖掉或是紧挨着。

左上加2,、右下减1,是我自己试出来的一个结果,你也可以自己将其调整为别的值,比如左上加3、右下减2,或者是其它的你觉得看着合适的值。

(4)小结

到了这里,代码部分,我相信,我已经是大致讲完了。

还有不懂的,你可以评论或者是私信联系我。

三.    代码编译

程序的编译和运行方法,如果你忘了,可以参照下述的两篇文章。

参考课节:使用 VS2010 编写 C语言程序

参考课节:用 VS2019 编写C语言程序

至于程序的运行结果,它差不多就是图3 啦。

我们再次将图3 给贴在下面,它差不多就是本节程序的运行结果。

图3 副本,程序运行结果

为啥是差不多呢?

因为,我一开始在用编程的方式来绘制图3 的时候,我给模拟像素方格涂色时所用的函数,是在以后的课节中会讲到的 FillRect 函数。而此时,我在给大家讲解 GDI 绘图代码时,我给模拟像素方格涂色时所用的函数,是应用了 NULL_PEN 备用画笔的 Rectangle 函数。

也就是,我们在绘制图3 时实际使用的函数,是 FillRect 函数。而本节代码中,我们为了不超纲,为了早一点给大家演示 GDI 绘图代码,我们临时将 FillRect 函数替换为应用了 NULL_PEN 备用画笔的 Rectangle 函数。

应用了 NULL_PEN 备用画笔的 Rectangle 函数的行为与 FillRect 函数的行为略有不同,所以,在你那里,实际运行结果会与图3 略有不同。此时,请不要深抠这点细节。

结束语

本节内容,字数不少,我也觉得有些累了。

务必学好本节内容。

下一节,我将展示 BigFontPic 项目代码,它与本节代码的逻辑很相似,只是少数的几个变量值,我设置成了其它的样子。那个代码,我将其作为一个思考题,请大家自行分析其逻辑。

专栏导航

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

回到目录

下一篇:第5章,[Win32 章节] :练习程序,BigFontPic

赞(0)
未经允许不得转载:网硕互联帮助中心 » 第5章,[Win32 章节] :练习程序,LittleFontPic
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!