图标资源
以.ico
为后缀的图片,icon 图标资源。一个图标文件中可以有多个不同分辨率大小的图标,以适应不同场景下的使用。
想要使用图标资源,第一步添加图标资源,第二步加载图标资源,第三步设置窗口类。
1 | HICON LoadIcon( |
添加图标资源:
添加图标资源后可以自己绘制图标:从图中可以看到有不同大小的图标(256x256像素、48x48像素等),有支持彩色的图标,也有黑白图标。
代码中添加资源头文件,并将图标赋值给窗口类的成员变量。
1 |
|
编译运行改变窗口图标:
在Visual Studio中编辑图标并不怎么方便,在这里可以将提前制作好的图标放在项目目录中,修改.rc
文件中对于图标资源的描述,修改为自己提前制作好的图标,然后在Visual Studio中重新加载被修改的 .rc
文件,再次编译运行。
光标资源
光标资源就是鼠标光标,这个光标在不同情况下是不同的:有的时候是箭头光标,有的时候是一个小手,有的时候是一闪一闪的竖杠,等等;在游戏中光标的形状也各不相同。一个光标就是一个图片,也是需要自己提前绘制,然后用代码进行应用。
光标资源使用步骤:
- 第一步添加光标资源,光标的默认大小是32x32像素,每个光标有一个HotSpot热点,也就是点击时生效的点(因为一个光标图片有很多像素,但是生效的一个点只有一个点,这个点的位置可以自己设置,例如箭头光标的热点就在箭头的尖上)。
- 第二步加载资源:
1
2
3
4HCURSOR LoadCursor(
HINSTANCE hInstance, //handle to application instance
LPCTSTR lpCursorName //name or resource identifier
); // hInstance 可以为 NULL, 获取系统默认的Cursor - 第三步设置光标:
- 注册窗口类时设置。
- 或 使用 SetCursor() 设置光标。
添加光标资源:
绘制光标资源:
使用图中的 Set Hot Spot Tool
来设置光标热点。
设置光标:
1 |
|
无论是 LoadCursor()
函数还是 LoadIcon()
函数,他们的作用都是通过窗口实例句柄和资源的ID来在内存中找到这个资源。窗口实例句柄 hInstance
的作用就是在内存中找到一块保存窗口各种信息的内存,这些资源被编译后会编译到 .exe
里面,当程序运行的时候,程序就会被加载到内存,其中就包括了各种资源。
自己创建的光标只在客户区起作用,在标题栏就会自动变成默认光标。
使用 SetCursor() 函数可以在程序运行的过程中修改光标:
1 | HCURSOR SetCursor( |
SetCursor()函数必须放在 WM_SETCURSOR
消息下进行调用。
WM_SETCURSOR 消息
只有光标有移动的情况,这个消息就会不断地出现。专职用法:改光标。
附带参数:
- wParam :当前使用的光标句柄。
- lParam :
- LOWORD :当前区域的代码(Hit-Test code)HTCLIENT (客户区)/ HTCAPTION(标题栏、边框)。
- HIWORD :没太大用处。
再绘制一个光标 IDC_CURSOR2
,在程序运行时进行切换:
1 | //添加全局变量 hInstance |
1 | //修改如下 |
字符串资源
字符串资源存在的目的就是方便代码中各种字符串的修改,例如语言的切换(中英文切换等)。
具体来说,字符串资源的作用包括:
中心化管理文本:将应用程序中使用的所有文本信息集中管理,方便统一修改和维护。
多语言支持:通过为不同语言单独创建字符串资源文件,可以实现应用程序的多语言支持,使应用能够在不同语言环境下运行并显示相应的文本。
提高可维护性:由于文本信息集中管理,修改或更新某个文本只需在字符串资源中进行修改,而不需要在代码中逐个替换,提高了代码的可维护性。
便于国际化和本地化:通过使用字符串资源,可以轻松地将应用程序本地化为不同的语言和区域设置,满足不同用户群体的需求。
节省内存空间:采用字符串资源可以在编译时对字符串进行优化,并且可以节省内存空间。
- 添加字符串资源:添加字符串表,在表中增加字符串。
- 加载字符串资源:
1
2
3
4
5
6int LoadString(
HINSTANCE hInstance, // handle to resource module
UINT uID, //字符串ID
LPTSTR lpBuffer, //存放字符串BUFF
int nBufferMax //字符串BUFF长度
); //成功返回字符串长度,失败返回0
添加字符串资源:
添加字符串资源:
使用字符串资源:
1 | char wTitle[20] = { 0 }; |
在程序中尽量多使用字符串资源(string table),这样可以方便文本管理,如果需要大面积的修改文本,也会方便很多。
加速键资源
加速键,就当快捷键理解。例如记事本的快捷键:
添加资源:资源添加加速键表,增加命令ID对应的加速键。
使用:
1 | //加载加速键表 |
添加加速键资源: Accelerator(加速)
编辑加速键:当加速键的ID和菜单中某一项ID相同时,就实现了加速键和某一个菜单项绑定。加速键和菜单项没有什么对应关系,当它们的ID相同时,就可以说是绑定;当然加速键的ID也可以单独设置,不一定非要和菜单项的ID相同。关于菜单资源参见上一篇文章菜单资源(点击跳转)。
在这里 ID_40001
、ID_40002
、ID_40003
分别对应着菜单项的ID 新建
、打开
、退出
。ID_other
不对应任何菜单项。
当菜单项被点击时,或者加速键被按下时,都可以产生 WM_COMMAND
消息。
在 WM_COMMAND
消息中:
- wParam:
- HIWORD 为 1 表示消息产生自加速键,为 0 表示消息产生自菜单项。
- LOWORD 为命令 ID。
- lParam :0.
TranslateAccelerator()函数的内部执行过程:
1 | //伪代码 |
加速键的使用:
1 | //加速键要放在消息循环中使用。 |
根据 WM_COMMAND
消息的 HIWORD(wParam)
可以区分出消息来自 加速键 被按下 还是 菜单项 被点击:
1 | //消息处理函数中对于 WM_COMMAND 消息的处理: |
运行:
WM_PAINT 消息
WM_PAINT :
- 产生时间:当窗口需要绘制的时候。(窗口第一次显示,窗口被拖拽、大小发生变化等等)
- 附带信息:
- wParam 、lParam :0。
- 专职用法:用于绘图。
ShowWindow()
函数在调用显示窗口的时候会发出一个 WM_PAINT
消息。
GetMessage()
函数在执行过程中也会检查窗口是否需要重新绘制,并发出 WM_PAINT
消息。
InvalidateRect
:需要重新绘制的区域。1
2
3
4
5BOOL InvalidateRect(
HWND hWnd, //窗口句柄
CONST RECT* lpRect, //区域的矩形坐标,参数为空的话表明整个窗口都需要重新绘制
BOOL bErase //重绘前是否需要擦除
);其中 RECT 是一个封装了矩形 上下左右的结构体:
1
2
3
4
5
6
7typedef struct tagRECT
{
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECT, *PRECT, NEAR *NPRECT, FAR *LPRECT;InvalidateRect
该函数一旦调用,GetMessage() 也会发出WM_PAINT
消息。用于通知Windows系统某个窗口或者指定的矩形区域需要重绘。当调用InvalidateRect函数时,系统会发送一个WM_PAINT消息给窗口,提示它需要重新绘制。这在开发Windows应用程序时非常有用,可以在需要更新界面时强制触发重绘操作。
一般情况下,开发者会在需要更新窗口内容的时候调用InvalidateRect函数,然后系统会在适当的时间批量处理所有需要重绘的区域,提高绘制效率。通过使用InvalidateRect函数,开发者可以实现界面的实时更新和响应用户操作,提升用户体验。
绘图
绘图步骤:
- 开始绘图:
1
2
3
4HDC BeginPaint(
HWND hWnd, //绘图窗口
LPPAINTSTRUCT lpPaint //绘图参数的BUFF
); //返回绘图设备句柄 HDC - 正式绘图
- 结束绘图
1
2
3
4BOOL EndPaint(
HWND hWnd, //绘图窗口
CONST PAINTSTRUCT *lpPaint //绘图参数的指针,BeginPaint返回
);
按下鼠标左键进行字符串绘制:使用TextOut()函数,在客户区绘制字符串。
绘图编程
绘图基础
GDI - Windows graphics device interface Windows图形设备接口(Win32提供的绘图API), 是 Windows 操作系统提供的一组函数和数据结构,用于在屏幕上绘制图形、文本和图像。GDI 提供了访问和控制显示设备的功能,包括绘制形状、填充颜色、显示文本、裁剪图像等。开发人员可以使用 GDI 来创建图形用户界面(GUI)和进行图形处理,例如创建窗口、按钮、菜单等界面元素。GDI 在 Windows 中扮演着重要的角色,为应用程序提供了图形处理能力,帮助用户和开发者实现丰富的图形显示效果。
绘图设备 DC(Device Context 设备上下文):
Device Context (设备上下文) 是Windows开发中的重要概念,它指代一种用于绘制图形、文本和图像的抽象对象。
Device Context 封装了与设备相关的绘图操作,允许开发者在绘制到屏幕、打印机或其他输出设备上时使用统一的接口。通过与设备上下文进行交互,开发者可以绘制图形、操纵字体、绘制文本、处理图像和执行其他与绘图相关的操作。
DC可以看成 Windows 的画家,想要绘图就要先找到这个画家,通过 DC 句柄来找到画家。
HDC hdc = BeginPaint(hWnd, &pc);
BeginPaint() 函数用来找到画家,并将返回值赋值给 HDC。BeginPaint() 的第一个参数 hWnd 窗口句柄,告诉画家在哪个窗口画画。
HDC - DC句柄,表示绘图设备。
抓到 HDC 以后,就可以使用各种绘图函数来进行图形绘制。
绘制图形结束后 EndPaint() 释放DC。
颜色:
- 计算机使用 红、绿、蓝 (Red、Green、Blue:RGB) 作为三原色,计算机使用的三原色是物理中的三原色,通过这三种颜色的不同程度的配比可以得到各种各样的颜色,拿放大镜观察屏幕可以看到这三原色构成了显示器的各种颜色。(物理中的三原色要区别于美术中的三原色,美术中的三原色是指黄红蓝,这三种颜料可以配出绝大多数颜色的颜料。)
- R值(红色值):0 ~ 255,(2^8^ = 256,一个字节)。
- G:0 ~ 255。
- B:0 ~ 255。
- 每个点的颜色是3个字节24位保存 0 ~ 2^24^ - 1,总共有 16,777,216 种颜色。
- 在过去的计算机中,使用16位来保存颜色值,低5位Red,第二个5位Green,最高6位Blue。
- 32位表示颜色值:前面的24位用来表示红色(R)、绿色(G)和蓝色(B)的色彩强度,而后面的8位则用来表示 alpha 通道。Alpha 通道通常用来表示颜色的透明度,数值范围从 0 到 255,0 表示完全透明,255 表示完全不透明。通过调整alpha通道的数值,可以控制颜色的透明度,使得它可以适应各种混合和叠加效果。这种32位的颜色表示方式常用于计算机图形和游戏开发中。
颜色的使用:
使用 COLORREF
类型来声明一个颜色值。
1 | typedef DWORD COLORREF; //本质是 DWORD。 |
unsigned long
类型占 4 个字节,正好 32 位,正好可以用来存储颜色值:
声明一个颜色值 COLORREF nColor = 0;
赋值使用 RGB宏:nColor = RGB(0,0,255); //蓝色
获取RGB值:GetRValue/GetGValue/GetBValue。BYTE nRed = GetRValue(nColor);
除了使用这几个函数,也自己自己通过 除法取余运算 来获得各个字节的值。
画点:
1 | //SetPixel 设置指定点的颜色 |
在消息处理函数的 switch 语句中添加对 WM_PAINT 消息的处理:在像素 100,100 的位置处绘制了一个非常小的黑点。
使用 for 循环绘制渐变色: 使用 for 循环绘制点,点成线,线成面,R和G的值在绘制的过程中不断变化,形成渐变色方块,第一个方块中,Blue的分量为255,第二个方块中 Blue的分量为128:
使用 for 循环一个一个点地去绘制,并不是一种高效的绘制方法。
基本图形绘制
线的使用(直线、弧线):
- MoveToEx :指定窗口当前点(窗口当前点默认在(0,0)处,窗口客户区左上角)。
- LineTo :从窗口当前点到指定点绘制一条直线。
1 | MoveToEx(hdc, 0, 260, NULL); //起点 |
封闭图形:能够用画刷填充的图形(Rectangle,Ellipse)。
1 | Rectangle(hdc, 100, 100, 300, 300); //绘制直角矩形 坐标:左上->右下 |
更多的图形的绘制函数可以在网上查找,不赘述。
GDI 绘图对象
画笔
画笔的作用:线的颜色、线型、粗细。
HPEN
画笔句柄。
画笔的使用:
- 创建画笔
HPEN CreatePen( int fnPenStyle, //画笔样式,PS_SOLID,实心线,可以支持多个像素宽,其他线型只能是一个像素宽。 int nWidth, //画笔粗细 COLORREF crColor //画笔颜色 ); //创建成功返回句柄
1
2
3
4
5
6- 将画笔应用到DC中
```cpp
HGDIOBJ SelectObject(
HDC hdc, //绘图设备句柄
HGDIOBJ hgdiobj //GDI绘图对象句柄,画笔句柄
); //返回原来的GDI绘图对象句柄,注意保存原来DC当中的画笔
- 绘图完成后,取出DC画笔,使用SelectObject()函数。
- 释放画笔:
1
2
3BOOL DeleteObject(
HGDIOBJ hObject //GDI绘图对象句柄,画笔句柄
); //只能删除不被DC使用的画笔,所以在释放前,必须将画笔从DC中取出
代码如图所示:
画笔样式也可以是虚线 PS_DASH
,非实线画笔像素宽度必须为 1 像素。HPEN red_pen = CreatePen(PS_DASH, 1, RGB(255, 0, 0));
画刷
画刷的作用给封闭图形填充颜色或图案,封闭图形有 Rectangle
、Ellipse
。
HBRUSH
:画刷句柄,保存画刷。
画刷的使用步骤跟画笔的使用步骤基本一样:
- 创建画刷
- CreateSolidBrush :创建实心画刷。
- CreateHatchBrush :创建纹理画刷。
- 将画刷应用到 DC 中:SelectObject()。
- 绘图
- 将画刷从 DC 中取出。
- 删除画刷:DeleteObject()。
1 | void OnPaint(HWND hWnd) { |
为了让绘制的封闭图形看上去与背景相契合,可以使用透明画刷。透明画刷已经存在于操作系统中,想要使用透明画刷,只需要向操作系统借用即可。
使用 GetStockObject()
函数获取由操作系统维护的画笔、画刷、字体等等。向操作系统借用的画笔、画刷等使用完后,不需要 DeleteObject()
。
向操作系统借用透明画刷:
1 | HGDIOBJ transparent_brush = GetStockObject(NULL_BRUSH); |
位图
- 光栅图形:记录图像中每一点的颜色等信息,光栅图形是由像素(图像的最小单元)组成的图形,它们在网格状的二维数组中描述。在光栅图形中,每个像素都有自己的颜色值,图像的清晰度和细节取决于像素的密度和分辨率。常见的光栅图形格式包括 JPEG、PNG、BMP 等。复杂的图像和真实世界的场景,但在缩放和变换时可能会失真。
- 矢量图形:记录图像算法,绘图指令等,矢量图形使用数学公式描述图形,它由线条、曲线和填充区域等基本几何形状组成。与光栅图形不同,矢量图形以对象的形式存储,因此可以在任意比例下缩放而不失真。常见的矢量图形格式包括 SVG、EPS、AI 等。矢量形适合用于图标、标志、表和大型设计元素,能够保持清晰度并支持无损缩放。
位图句柄:HBITMAP
,位图句柄能在内存中找到一块内存,里面保存着位图图像每个点的颜色值等信息。
位图的使用:
在资源中添加位图资源。
从资源中加载位图
LoadBitmap()
。创建一个与当前 DC 相匹配的 DC (内存 DC)。
HDC CeateCompatibleDC( HDC hdc //当前 DC 句柄,可以为 NULL (使用 屏幕 DC) ); //返回创建好的的 DC 句柄 /* 这里的 内存 DC 是一个在内存中的 DC,可以理解为一个虚拟的 DC */
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17- 使用 SelectObject 将位图的数据放入虚拟的内存DC中。
- 成像(1:1比例):将内存 DC 中的图像显示在窗口上。
- ```cpp
BOOL BitBlt(
HDC hdcDest, //目的DC
int nXDest, //目的DC左上角坐标
int nYDest, //目的右上角坐标
int nWidth, //目的宽度
int nHeight, //目的高度,这四个参数确定成像位置和成像区域
HDC hdcStr, //源DC
int nXSrc, //源左上角坐标
int nYSrc, //源右上角坐标,从虚拟DC的什么位置开始成像
DWORD dwRop //成像方法 SRCCOPY
);
缩放成像:
1
2
3
4
5
6
7
8
9
10
11
12
13BOOL StretchBlt(
HDC hdcDest, //handle to destination DC
int nXOriginDest, //x-coord of destination upper-left corner
int nYOriginDest, //y-coord of destination upper-right corner
int nwidthDest, //width of destination rectangle
int nHeightDest, //height of destination rectangle
HDC hdcSrc, //handle to source DC
int nXOriginSrc, //x-coord of source upper-left corner
int nYOriginSrc, //y-coord of source upper-right corner
int nWidthSrc, //源DC宽
int nHeightSrc, //源DC高
DWORD dwRop //raster operation code
);使用 SelectObject 函数取出位图。
释放位图 DeleteObject。
释放匹配的 DC:DeleteDC。
添加 bitmap :添加 bitmap 成功之后,会显示 bitmap 的绘制界面,也可以修改 Resource.rc 文件中对于 bitmap 资源的描述,修改为从外部导入的 bitmap 文件。
1:1 成像代码:
1 | PAINTSTRUCT ps = { 0 }; |
在窗口中 1:1 显示位图:(该图高1536像素,宽1024像素,所以 1:1 显示无法看到全部图像。)
缩放成像:
1 | StretchBlt(hdc, 5, 5, //窗口位置 |
运行:
从上图的运行结果可以看到,图片缩小后显示,出现了很多的纹理。出现这种情况可能是因为在使用StretchBlt函数时,将图像进行缩放时导致了图像质量下降。StretchBlt函数可以对图像进行拉伸和压缩,但它是基于像素级的操作,因此在缩放时可能会导致图像的锯齿状边缘和失真。
想要在窗口中显示缩小的BMP图像并保持较好的质量,可以考虑使用双线性插值进行图像缩放。双线性插值是一种图像处理算法,它可以在进行图像缩放时减少锯齿状边缘和纹理。
使用 GDI+ 库来实现双线性插值
首先配置项目属性:在 Solution Explorer 栏下,右键项目名,项目属性 properties——>Linker 链接器——>Input——>Additional Dependencies 附加属性后面:添加一个值 gdiplus.lib;
(注意每个值之间用英文版分号 ;
隔开)。
或者在代码第一行添加如下代码:
1 |
1 |
|
运行:消除图片缩小时产生的纹理。
修改图片绘制的透明度为 0.5 :
1 | // 设置透明度 |
运行:
1 | ColorMatrix colorMatrix = { |
这段代码创建了一个 ColorMatrix 对象,其中第四行第四列的值被设置为 0.5,即设置了图像的透明度为 50%。
ColorMatrix 是一个 5x5 的矩阵,用于在 GDI+ 中进行颜色转换。当我们将一个图像绘制到目标上时,可以使用 ColorMatrix 对图像进行颜色调整。它的基本原理是将每个像素的颜色值与 ColorMatrix 进行矩阵乘法运算,得到新的颜色值。
在这个 ColorMatrix 中,每行代表着输出颜色的一个分量(红、绿、蓝、透明度和偏移量),而每列代表输入颜色的一个分量。在这个矩阵中,第四行第四列的值表示了输出的 alpha(透明度)通道,因此将其设置为 0.5 就使得输出的透明度减半,从而实现了图像的半透明效果。
通过调整 ColorMatrix 中的各个元素,我们可以实现对图像的不同颜色分量的调整,包括亮度、对比度、饱和度以及透明度等。这种颜色转换的方式提供了一种非常灵活的方法,可以在绘制图像时对其进行各种颜色效果的处理。
文本绘制
在前文中提到了一个文本的绘制函数 TextOut
(将文字绘制在指定坐标位置),这个函数对于文本的绘制功能比较基础。下面介绍 DrawText()
函数:
1 | int DrawText( |
1 | RECT rc; //矩形结构体 |
- DT_LEFT :水平靠左开始绘制字符串。
- DT_TOP :垂直靠上绘制字符串。
- DT_WORDBREAK :单行超出矩形右侧的文本另起一行绘制。
- DT_NOCLIP :文本太长超出矩形范围时,不剪切字符串(完整显示所有文本)。
- DT_CENTER :水平居中。
- DT_VCENTER :垂直居中。(只能单行居中,与WORDBREAK冲突)
- DT_SINGLELINE :单行绘制。
… …
文字颜色和背景:
文字颜色:SetTextColor。
SetTextColor(hdc, RGB(0, 0, 255)); //蓝色字体 DrawText(hdc, "你好\n hello hello\n Long\n LONG LONG Long\n LONG", strlen("你好\n hello hello\n Long\n LONG LONG Long\n LONG"), &rc, DT_CENTER | DT_VCENTER | DT_NOCLIP);
1
2
3
4
5
- 文字背景:SetBkColor。(默认背景色白色)
- ```cpp
SetBkColor(hdc, RGB(0, 128, 200)); //浅蓝色背景
文字背景模式:SetBkMode (OPAQUE :默认的不透明模式 / TRANSPARENT :透明模式)。
SetBkMode(hdc, TRANSPARENT);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
---
#### 字体
Windows 常用字体为 TrueType 格式的字体文件。
字体名:标识字体类型。
HFONT :字体句柄。
在 Windows 系统盘 ``C:/Windows/Fonts`` 目录下有着各种各样的字体:这些字体文件都是 TrueType 格式的,也就是每个字体文件中保存着每个字的真实点阵字型(位图字体,每个字的真实外观),即使双击打开,也不能查看到每个字,只是一个预览而已。


---
**字体的使用**:
- 创建字体:想要创建一个字体,那么该电脑中就要有该字体的字体文件。
- ```cpp
HFONT CreateFont(
int nHeight, //字体高度
int nWidth, //字体宽度,字体高度给出后,宽度填0即可自动匹配合适的宽度
int nEscapement, //字符串倾斜角度,字符串与水平方向的夹角,以0.1度为单位。(字体站得竖直,但是字符串中所有的字符不在一水平线上)
int nOrientation, //字符旋转角度,以x轴为轴心,向里或向外旋转角度。
int fnWeight, //字体的粗细
DWORD fdwItalic, //斜体,1或0。
DWORD fdwUnderline, //字符下划线
DWORD fdwStrikeOut, //删除线
DWORD fdwCharSet, //字符集
DWORD fdwOutputPrecision, //输出精度,没什么用
DWORD fdwClipPrecision, //剪切精度,没什么用
DWORD fdwQuality, //输出质量,没什么用
DWORD fdwPitchAndFamily, //匹配字体,没什么用
LPCTSTR lpszFace //字体名称
);
应用到字体 DC,(SelectObject)。
绘制文本,(DrawText / TextOut)。
取出字体,(SelectObject)。
删除字体,(DeleteObject)。
1 | SetTextColor(hdc, RGB(0, 128, 200)); //字体颜色浅蓝色 |
字体绘制如下:
请求一个图片的 url 并在窗口中显示
1 |
|