《Windows游戏编程大师技巧》学习笔记(八)

《Windows游戏编程大师技巧》学习笔记(八)

图形设备描述表

作者在书中对其阐释:
图形设备描述表.png

画笔和画刷

  • 画笔:用于画线条和轮廓,具有颜色、粗细和线型;
  • 画刷:用于填充任何封闭对象,具有颜色、样式,甚至本身可以是位图;

画笔和画刷.png

GDI一般只使用一个画笔和一个画刷,即当前的图形设备描述表中每次只有一个画笔或画刷被激活;
所以在绘图前,要先选定一个画笔/画刷,需要注意的是,一旦选定后,直到被修改或程序结束,该画笔/画刷会一直被使用;
GDI关于画笔和画刷句柄的存取位置有限,且创建的画笔和画刷占用系统资源,完成绘图之后务必删除该画笔/画刷;

使用画笔

首先获取画笔对象,使用系统存储的画笔或创建用户自定义的画笔

HPEN black_pen = (HPEN)GetStockObject(BLACK_PEN);
HPEN red_pen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));

GetStockObject函数用于获取Windows中存储的对象,原型如下:

HGDIOBJ WINAPI GetStockObject(int i);

Windows中存储对象类型如下表:
Windows中存储对象类型表1.png
Windows中存储对象类型表2.png
CreatePen函数用于创建用户自定义的画笔,原型如下:

HPEN WINAPI CreatePen(
    int iStyle,     // 画笔样式
    int cWidth,     // 画笔宽度
    COLORREF color  // 画笔颜色
);

画笔样式的可选值如下表:
画笔样式.png

使用画刷

与画笔的使用相似,可以直接获取系统中存储的画刷对象,或创建用户自定义的画刷;
使用GetStockObject函数获取和画笔的使用相同,此处不再赘述;
用户自定义的画刷分为两类:实心的(Solid)和阴影线的(Hatched),创建方式分别如下:

HBRUSH green_brush = CreateSolidBrush(RGB(0, 255, 0));
HBRUSH red_brush = CreateHatchBrush(HS_CROSS, RGB(255, 0, 0));

CreateSolidBrush函数原型如下:

HBRUSH WINAPI CreateSolidBrush(COLORREF color);

CreateHatchBrush函数原型如下:

HBRUSH WINAPI CreateHatchBrush(
    int iHatch,     // 画刷样式
    COLORREF color  // 画刷颜色
);

画刷样式可选值如下表:
画刷样式值.png

选取对象

使用SelectObject函数选中指定的画笔/画刷对象为当前GDI绘图所使用的对象,函数原型如下:

HGDIOBJ WINAPI SelectObject(
    HDC hdc,    // 设备上下文句柄
    HGDIOBJ h   // 对象句柄
);

选中新对象时函数返回旧对象的句柄,可以保存备用;
使用DeleteObject销毁绘图对象,函数原型如下:

BOOL WINAPI DeleteObject(HGDIOBJ ho);

销毁画笔等对象时一定要多加小心,删除正在被选中使用的对象将发生严重的错误

简单的图元绘制

使用画笔和画刷对点、线、平面多边形和圆进行绘制;

绘制点

使用SetPixel函数绘制点,原型如下:

COLORREF WINAPI SetPixel(
    HDC hdc,        // 设备上下文句柄
    int x,          // x 坐标
    int y,          // y 坐标
    COLORREF color  // 颜色
);

默认模式下,窗口的坐标系为上下颠倒的第一象限笛卡尔坐标系,笛卡尔坐标系和Windows窗口坐标系对比图如下:
笛卡尔坐标系和Windows坐标系.png
GetWindowDCGetDC都可以用来获取窗口的HDC,区别在于:GetWindowDC获取的HDC覆盖了整个窗口,包含标题栏、菜单、滚动条等内容;而GetDC获取到的内容仅指定窗口用户区的设备环境;

绘制线段

GDI用一个不可见的小光标,来跟踪将要被绘制的线段当前的起始位置,所以绘制线段需要先设定线段起点,然后移动“光标”到终点,完成绘制
MoveToEx函数可以设置线段起点位置,原型如下:

BOOL WINAPI MoveToEx(
    HDC hdc,        // 设备上下文句柄
    int x,          // 新位置的 x 坐标
    int y,          // 新位置的 y 坐标
    LPPOINT lppt    // 旧位置坐标指针
);

lppt用于保存“光标”在移动前的位置坐标,不需要记录时可以传递NULL,点坐标的结构体定义如下:

typedef struct tagPOINT
{
    LONG  x;
    LONG  y;
} POINT, *PPOINT, *NPPOINT, *LPPOINT;

设定完线段的初始位置后,使用LineTo函数绘制一条线段,原型如下:

BOOL WINAPI LineTo(
    HDC hdc,    // 设备上下文句柄
    int x,      // 线段终点 x 坐标
    int y       // 线段终点 y 坐标
);

如下代码可以绘制三个顶点分别为(20, 10)、(30, 20)、(10, 20)的三角形:

MoveToEx(hdc, 20, 10, NULL);

LineTo(hdc, 30, 20);
LineTo(hdc, 10, 20);
LineTo(hdc, 20, 10);

绘制矩形

有如下三种方式绘制矩形:

  • Rectangle函数:使用当前的画笔和画刷绘制矩形,并且会自动填充,原型如下:
    	BOOL WINAPI Rectangle(
    	HDC hdc,    
    	int left, int top, 
    	int right, int bottom
    	);
    

    传递到Rectangle函数的坐标为矩形边框,也就是说如果当前画笔线性为NULL的话,会得到一个在四个方向各短一个像素的实心矩形;

  • FillRect函数:不使用边界画笔绘制一个填充矩形,包括左上角定点,但不包括右下角定点,原型如下:
    	int WINAPI FillRect(
    		HDC hDC,            // 设备上下文句柄
    		CONST RECT *lprc,   // 矩形结构体指针
    		HBRUSH hbr          // 画刷句柄
    	);
    
  • FrameRect函数:只使用画刷绘制仅有边界的中空矩形,原型如下:
    	int WINAPI FrameRect(
    		HDC hDC,            // 设备上下文句柄
    		CONST RECT *lprc,   // 矩形结构体指针
    		HBRUSH hbr          // 画刷句柄
    	);
    

绘制圆

Ellipse函数用于绘制圆和椭圆,原型如下:

BOOL WINAPI Ellipse(
    HDC hdc,    // 设备上下文句柄
    int left,   // 椭圆左顶点 x 坐标
    int top,    // 椭圆上顶点 y 坐标
    int right,  // 椭圆右顶点 x 坐标
    int bottom  // 椭圆下顶点 y 坐标
);

绘制多边形

Polygon函数用于绘制凸多边形,原型如下:

BOOL WINAPI Polygon(
    HDC hdc,            // 设备上下文句柄
    CONST POINT *apt,   // 多边形顶点数组
    int cpt             // 多边形顶点个数
);

如果传递的多边形顶点并不能完全使用以绘制凸多边形,那么GDI会尽最大可能将其拆分为多个凸多边形进行绘制,但并不能保证绘制质量。

文本和字体

可以使用GetStockObject函数获取Windows内预存的字体对象,具体见前文表格所述;
也可以使用CreateFont函数创建字体对象,使用细节较为复杂,此处不做过多讲述,具体查看Win32文档-CreateFontA function
Windows系统内置的TrueType字体如下表:
系统内置TrueType字体.png