《Windows游戏编程大师技巧》学习笔记(六)
WM_PAINT 消息
在之前的代码中,我们对于WM_PAINT
消息的处理方式如下:
1 | PAINTSTRUCT ps; |
如下图所示,当一个窗口被移动、改变大小或被其他窗口遮盖时,WN_PAINT
消息便被发送了:
在调用BeginPaint
函数后,需要重新绘图的区域坐标便被保存在了ps
的rcPaint
字段中,rcPaint
字段的定义如下:
1 | typedef struct tagRECT |
如上述,使用BeginPaint
函数只能获取窗口需要重绘的部分,所以如果需要获取窗口的更多信息(如图形设备上下文),则需要将BeginPaint...EndPaint
函数对替换为GetDC...ReleaseDC
函数对,相关原型如下:
1 | HDC GetDC(HWND hWnd); |
所以之前对于WM_PAINT
消息的处理代码就可以变为:
1 | HDC gdc = NULL; |
如果只是这样简单地替换,会有新的问题:
BeginPaint...EndPaint
会向Windows发出一个消息,指示窗口内容已恢复(甚至在没有进行任何图形调用的情况下),所以Windows不会继续发出WM_PAINT
消息;如果如上文所述简单替换,那么为了使窗口有效,WM_PAINT
会一直不停地传递下去;
要使需要重画的窗口区域有效,并通知Windows已经恢复了该窗口,可以使用专用函数ValidateRect
,函数原型如下:
1 | BOOL WINAPI ValidateRect( |
在大多数情况下,有效区域是整个窗口,所以上文中的代码段,可以改成如下所示:
1 | PAINTSTRUCT ps; |
GetClientRect
函数可以获取用户矩形区域的坐标,由于窗口可以任意移动,一个窗口拥有两套坐标系:Windows坐标系和用户坐标系;Windows坐标系相对于屏幕,而用户坐标系相对于窗口左上角(0, 0),如下图所示:
另外,如果想手动地使整个窗口无效,以确保BeginPaint
函数设置的rcPaint
字段为整个窗口区域,那么可以调用InvalidateRect
函数,函数原型如下:
1 | BOOL WINAPI InvalidateRect( |
函数中第二个参数所设置的窗口无效区域,会和原本的无效区域形成并集,当我们传递NULL
时,函数会将整个窗口作为无效区域进行设置;
所以,前述代码也可以继续使用BeginPaint...EndPaint
对进行实现,只不过需要修改为如下方式:
1 | PAINTSTRUCT ps; |
视频显示基础和色彩
与图形有关的概念和术语:
有两种方式用于在显存中表示颜色,分为直接方式和间接方式:直接方式又叫RGB模式,调色板模式以间接模式工作;
RGB模式下,用代表红绿蓝三原色的16位、24位或32位数据来表示屏幕上的每个像素颜色,显而易见,16位或32位的数据不能被平均分为三份,所以在这种情况下,每个色彩通道会占据不同的位数,如下图所示:
调色板是一个有256项的表,每一项都是一个单字节值(0~255),但是实际上每一个输入项都是由三个8位的红绿蓝项构成的,从本质上说它是一个24位RGB全彩描述符;
颜色查找表的工作原理如下:当从8位颜色模式的屏幕上读取到一个像素时,嘉定其值为26,则26就用作颜色表的一个索引;然后将对应于26号索引地址的色彩描述符的24位RGB值取出,显示实际的颜色;
也就是说,通过这种办法,在同一时间屏幕上可以有256种不同的色彩,但是他们是来自于16.7百万色或24位RGB值;
查找过程如下图:
基本文本显示
使用GDI进行文本显示有两个常用函数:TextOut
和DrawText
,在非Unicode环境下二者的定义如下:
1 | BOOL WINAPI TextOutA( |
TextOut
函数参数意义明显,此处不再进行解释;DrawText
函数的第四个参数为包裹文本的矩形,这就意味着所有用此函数进行显示的文字都会在显示前被这个矩形进行裁剪;第五个参数为文本显示的样式,如可以使用DT_LEFT
调整文本为左对齐,更多的标志见Win32SDK - DrawTextExA;
可以通过SetTextColor
和SetBkColor
函数分别设置文本的前景色和背景色,二者函数原型如下:
1 | COLORREF WINAPI SetTextColor( |
如上两个函数调用结束后,随后所有的文本显示的前景色和背景色均使用设置的颜色;函数的返回值均为设置前的颜色,所以我们可以保存其返回值,在使用新的颜色渲染结束后恢复原先的颜色;COLORREF
结构体的定义如下(在WinSDK-10.0中此结构体直接被定义为了DWORD
,以下定义为作者在书中对旧版本的记录,二者程序意义相同):
1 | typedef struct tagCOLORREF |
COLORREF
在内存中表示为0x00bbggrr
,此处使用小端模式,创建一个有效的颜色定义,可以使用RGB
宏,如下所示:
1 | COLORREF red = RGB(255, 0, 0); |
PALETTEENTRY
与COLORREF
相似,同样可以用来描述颜色,其定义如下:
1 | typedef struct tagPALETTEENTRY { |
对于peFlags
成员,可以有如下取值:
由于PALETTEENTRY
与COLORREF
二者只有最后一个字节的解释不同,所以在多数情况下它们是可以互换的;
默认状态下输出的文本背景色是由SetBkColor
进行指定,如果需要设置背景色透明,则可以通过SetBkMode
进行设置,函数原型如下:
1 | int WINAPI SetBkMode( |
第二个参数使用TRANSPARENT
表示使用透明背景,使用OPAQUE
恢复为纯色不透明背景;
与前景色和背景色设置函数相同,SetBkMode
的返回值依然是设置前的旧值,可以保存以便恢复;
上述介绍的函数中,TextOut
函数的运行速度相对于DrawText
来说较快,并且背景不透明的文本渲染速度较快,向淡色背景上输出文本时可以不用设置文本背景色透明;
综上,一个可能的文本输出代码如下:
1 | int old_tmode; |
于是,很有年代感的文本就出现啦:
《Windows游戏编程大师技巧》学习笔记(六)