专栏导航
本节前言
对于本节所讲解的知识,有可能,你会需要时不时地参考本专栏的其它文章。真的遇到了需要参考之前的文章的知识点,请你自行查阅。
我呢,也会提到一部分的参考课节。但是呢,你不应该依赖于我的主动提及。最好呢,你自己能够多去了解和查看本专栏目录。
在上一节,我们讲解了如何来使用系统备用画笔。本节,我们来讲解如何来操作自定义画笔。
本节内容,来自于对佩措尔德教材的大段抄录。
一. 本节内容
尽管使用备用对象中的画笔非常方便,但只能使用实心的黑色画笔、实心的白色画笔或者没有画笔三种情况。如果想要获得更丰富的效果,则必须创建自己的画笔。
这里是创建画笔的一般过程:调用 CreatePen 或者 CreatePenIndirect 函数创建一个 "逻辑画笔",它只是说明你想要得到一个什么样的画笔。这些函数会返回一个逻辑画笔的句柄。然后需要调用 SelectObject 函数将画笔选入设备环境中。接着,就可以使用这个新的画笔来绘制线条。一次只能有一支画笔被选入设备环境。释放设备环境之后(或者将其他画笔选入设备环境之后),需要调用 DeleteObject 函数来删除你创建的逻辑画笔。此后,画笔的句柄不再有效。
逻辑画笔是一个 "GDI 对象",一个程序可以创建 6 种 GDI 对象,它是其中之一。其他五种分别是画刷、位图、区域、字体和调色板。除了调色板之外,所有这些对象都通过 SelectObject 函数选入设备环境。
下面三条规则控制画笔等 GDI 对象的使用。
- 最终应当删除你所创建的所有 GDI 对象。
- 当 GDI 对象被选入一个有效的设备环境时,不要删除它。
- 不要删除备用对象。
这些规则都是有道理的。但是有时候有点棘手。下面我们将通过例子来帮助理解这些规则。
CreatePen 函数的一般语法如下。
hPen = CreatePen (iPenStyle, iWidth, crColor);
其中,参数 iPenStyle 决定画笔绘制的是实线、虚线或者点线。这个参数可以使用定义在 WinGDI.h 头文件中的下列标识符。下面的图也显示了每种样式产生的线条。
图1
对于 PS_SOLID、PS_NULL 和 PS_INSIDEFRAME 样式,参数 iWidth 表示画笔的宽度。当 iWidth 值为 0 时,Windows 会把画笔的宽度设定为 1 个像素。这其实是一个很重要的知识点。以后,我们还会学习映射模式的知识。在这里,我们要求大家先有一个印象,无论处于何种映射模式之下,当 iWidth 值为 0 时,Windows 都会把画笔的宽度设定为 1 个像素。
至于何为映射模式,我们以后会学习到的。此处,我们要求大家有个印象。
备用画笔总是一个像素宽。如果指定使用虚线或点线样式,同时把画笔宽度设置为大于 1 个像素,那么 Windows 会使用实心的画笔来代替。
CreatePen 的参数 crColor 是一个 COLORREF 值,它用来指定画笔的颜色。对所有的除了 PS_INSIDEFRAME 之外的画笔样式,当将画笔选入到设备环境时,Windows 将该颜色转换为设备所能表示的最接近的纯色。PS_INSIDEFRAME 画笔样式是唯一能够使用抖动色的画笔样式,并且只有当画笔宽度大于 1 时才如此。
PS_INSIDEFRAME 画笔样式用于填充区域的函数时有另外一个奇特之处。当使用非 PS_INSIDEFRAME 样式的画笔样式时,如果用于绘制轮廓的画笔宽度大于 1 个像素,那么画笔的中心会处于边界之上,这样画出的轮廓线部分将会在边框之外。但是对于 PS_INSIDEFRAME 画笔样式,整个轮廓线都会在边框内。
举几个例子。
假定,我们分别用 PS_SOLID 和 PS_INSIDEFRAME 样式创建两个黑色的 6 像素宽度的画笔,并且都去画同样的矩形。那么,在 PS_INSIDEFRAME 样式里面,所有的 6 像素画笔线条,都位于边框之内。而在 PS_SOLID 样式里面,有 3 像素的线条像素在边框内,另外的 3 像素的线条像素在边框之外。
再来假定,我们分别用 PS_SOLID 和 PS_INSIDEFRAME 样式创建两个黑色的 7 像素宽度的画笔,并且都去画同样的矩形。那么,在 PS_INSIDEFRAME 样式里面,所有的 7 像素画笔线条,都位于边框之内。而在 PS_SOLID 样式里面,有 4 像素的线条像素在边框之内,另外的 3 像素的线条像素在边框之外。
如果用两种样式创建 10 像素宽度的实线来绘制矩形,那么,在 PS_INSIDEFRAME 样式里面,所有的 10 像素画笔线条像素,都位于边框之内。而在 PS_SOLID 样式里面,有 5 像素的线条像素在边框之内,另外的 5 像素的线条像素在边框之外。
如果用两种样式创建 11 像素宽度的实线来绘制矩形,那么,在 PS_INSIDEFRAME 样式里面,所有的 11 像素画笔线条像素,都位于边框之内。而在 PS_SOLID 样式里面,有 6 像素的线条像素在边框之内,另外的 5 像素的线条像素在边框之外。
如果用两种样式创建 2n(n 为大于或等于 1 的整数)像素宽度的实线来绘制矩形,那么,在 PS_INSIDEFRAME 样式里面,所有的 2n 像素画笔线条像素,都位于边框之内。而在 PS_SOLID 样式里面,有 n 像素的线条像素在边框之内,另外的 n 像素的线条像素在边框之外。
如果用两种样式创建 2n+1(n 为大于或等于 1 的整数)像素宽度的实线来绘制矩形,那么,在 PS_INSIDEFRAME 样式里面,所有的 2n+1 像素的画笔线条像素,都位于边框之内。而在 PS_SOLID 样式里面,有 n+1 像素的线条像素在边框之内,另外的 n 像素的线条像素在边框之外。
我想,这么来解释,你应该能明白 PS_INSIDEFRAME 样式与非 PS_INSIDEFRAME 样式在绘制矩形椭圆时,在绘制外侧边缘的区别了吧。
我们接着讲。
也可以通过建立一个类型为 LOGPEN(逻辑画笔)的结构,并调用 CreatePenIndirect 函数来建立一个画笔。如果你的程序在初始化时需要创建很多不同的画笔,这种方法会很有效。
为了使用 CreatePenIndirect 函数,首先要定义一个类型为 LOGPEN 的结构体变量。
LOGPEN logpen;
这个结构有三个字段:lopnStyle(无符号整型,或 UINT)表示画笔样式,lopnWidth(一个 POINT 结构)是以逻辑单位表示的画笔宽度,还有 lopnColor(COLORREF)表示画笔的颜色。Windows 仅使用 lopnWidth 结构中的 x 字段来设置画笔的宽度,y 字段会被忽略。
可以通过将该结构的地址传递到 CreatePenIndirect 函数来建立画笔。
hPen = CreatePenIndirect (&logpen);
注意:CreatePen 和 CreatePenIndirect 函数不需要设备环境句柄。在调用 SelectObject 之前,这些函数创建的逻辑画笔和设备环境没有任何联系。可以为几种不同的设备,例如屏幕和打印机,使用相同的逻辑画笔。
这里给出创建、选择和删除画笔的一种方法。假定程序使用三种画笔(宽度为 1 的黑色画笔、宽度为 3 的红色画笔和黑色的点线画笔)。可以首先定义存储这些画笔的句柄的静态变量。
static HPEN hPen1, hPen2, hPen3;
在处理 WM_CREATE 消息时,可以创建这三种画笔。
hPen1 = CreatePen (PS_SOLID, 1, RGB(0, 0, 0));
hPen2 = CreatePen (PS_SOLID, 3, RGB(255, 0, 0));
hPen1 = CreatePen (PS_DOT, 0, RGB(0, 0, 0));
在处理 WM_PAINT 消息时(或者在任何拥有有效的设备环境句柄时),可以将其中任何一支画笔选入到设备环境,并且使用它来绘制线条。
SelectObject (hdc, hPen2);
[画线函数]
SelectObject (hdc, hPen1);
[画线函数]
在处理 WM_DESTROY 消息时,可以删除这三种画笔。
DeleteObject (hPen1);
DeleteObject (hPen2);
DeleteObject (hPen3);
这是用于创建、选择和删除画笔最直接的办法。但是,很显然,程序必须知道需要使用什么样的画笔。因此,可能需要在每个 WM_PAINT 消息处理时创建画笔,并且调用 EndPaint 函数后删除它们。也可以在调用 EndPaint 函数前删除它们,但是需要小心,不要删除已被选入设备环境的当前画笔。
可以随时创建画笔,还可以将 CreatePen 和 SelectObject 调用组合到一条语句中。
SelectObject (hdc, CreatePen (PS_DASH, 0, RGB(255, 0, 0)));
现在,在绘制直线时,将使用一个红色的虚线画笔。红色虚线绘制完成后,可以删除画笔。糟糕,你没有保存画笔句柄,怎么才能删除画笔呢?前面介绍过,SelectObject 函数将返回先前被选入设备环境的画笔句柄。也就是说,可以通过将备用的 BLACK_PEN 选入到环境中来得到需要被删除的画笔句柄,然后删除它。
DeleteObject (SelectObject (hdc, GetStockObject (BLACK_PEN)));
下面给出另一种方法。当将一支画笔选入到一个新创建的设备环境时,保存 SelectObject 返回的画笔句柄。
hPen = SelectObject (hdc, CreatePen (PS_DASH, 0, RGB(255, 0, 0)));
hPen 究竟是什么呢?如果这是在获得设备环境后第一次调用 SelectObject 函数,hPen 函数就是 BLACK_PEN 的句柄。现在可以在同一条语句中选择该画笔到设备环境,并且删除自己创建的画笔(第二次 SelectObject 调用返回你创建的画笔的句柄)。
DeleteObject (SelectObject (hdc, hPen));
如果有一个画笔的句柄,LOGPEN 结构中各个字段成员的值就可以通过调用 GetObject 函数获得。
GetObject (hPen, sizeof(LOGPEN), (LPVOID) &logpen);
如果需要获得当前被选入设备环境的画笔句柄,则进行如下调用。
hPen = GetCurrentObject (hdc, OBJ_PEN);
我们将在以后讨论另外一个画笔创建函数,ExtCreatePen 。
结束语
本节内容有点长。好好学吧。