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

图形设备描述表

作者在书中对其阐释:

图形设备描述表

画笔和画刷

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

画笔和画刷

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

使用画笔

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

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

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

1
HGDIOBJ WINAPI GetStockObject(int i);

Windows中存储对象类型如下表:

Windows中存储对象类型表1

Windows中存储对象类型表2

CreatePen函数用于创建用户自定义的画笔,原型如下:

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

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

画笔样式值

使用画刷

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

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

CreateSolidBrush函数原型如下:

1
HBRUSH WINAPI CreateSolidBrush(COLORREF color);

CreateHatchBrush函数原型如下:

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

画刷样式可选值如下表:

画刷样式值

选取对象

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

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

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

1
BOOL WINAPI DeleteObject(HGDIOBJ ho);

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

简单的图元绘制

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

绘制点

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

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

默认模式下,窗口的坐标系为上下颠倒的第一象限笛卡尔坐标系,笛卡尔坐标系和Windows窗口坐标系对比图如下:

笛卡尔坐标系和Windows坐标系

GetWindowDCGetDC都可以用来获取窗口的HDC,区别在于:GetWindowDC获取的HDC覆盖了整个窗口,包含标题栏、菜单、滚动条等内容;而GetDC获取到的内容仅指定窗口用户区的设备环境;

绘制线段

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

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

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

1
2
3
4
5
typedef struct tagPOINT
{
LONG x;
LONG y;
} POINT, *PPOINT, *NPPOINT, *LPPOINT;

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

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

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

1
2
3
4
5
MoveToEx(hdc, 20, 10, NULL);

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

绘制矩形

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

  • Rectangle函数:使用当前的画笔和画刷绘制矩形,并且会自动填充,原型如下:
    1
    2
    3
    4
    5
    BOOL WINAPI Rectangle(
    HDC hdc,
    int left, int top,
    int right, int bottom
    );
    传递到Rectangle函数的坐标为矩形边框,也就是说如果当前画笔线性为NULL的话,会得到一个在四个方向各短一个像素的实心矩形;
  • FillRect函数:不使用边界画笔绘制一个填充矩形,包括左上角定点,但不包括右下角定点,原型如下:
    1
    2
    3
    4
    5
    int WINAPI FillRect(
    HDC hDC, // 设备上下文句柄
    CONST RECT *lprc, // 矩形结构体指针
    HBRUSH hbr // 画刷句柄
    );
  • FrameRect函数:只使用画刷绘制仅有边界的中空矩形,原型如下:
    1
    2
    3
    4
    5
    int WINAPI FrameRect(
    HDC hDC, // 设备上下文句柄
    CONST RECT *lprc, // 矩形结构体指针
    HBRUSH hbr // 画刷句柄
    );

绘制圆

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

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

绘制多边形

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

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

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

文本和字体

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

系统内置TrueType字体

作者

Voidmatrix

发布于

2022-02-04

更新于

2022-02-05

许可协议

评论