三、Windows图像处理—画点和线(画笔的建立、选择和删除)
尽管使用现有画笔非常方便,但却受限于实心的黑画笔、实心的白画笔或者没有画笔这三种情况。如果想得到更丰富多彩的效果,就必须建立自己的画笔。
这一过程通常是:使用函数CreatePen或CreatePenIndirect建立一个「逻辑画笔」,这仅仅是对画笔的描述。这些函数传回逻辑画笔的句柄;然后,呼叫SelectObject将画笔选进设备内容。现在,就可以使用新的画笔来画线了。在任何时候,都只能有一种画笔选进设备内容。在释放设备内容(或者在选择了另一种画笔到设备内容中)之后,就可以呼叫DeleteObject来删除所建立的逻辑画笔了。在删除后,该画笔的句柄就不再有效了。
逻辑画笔是一种「GDI对象」,它是您可以建立的六种GDI对象之一,其它五种是画刷、位图、区域、字体和调色盘。除了调色盘之外,这些对象都是通过SelectObject选进设备内容的。
在使用画笔等GDI对象时,应该遵守以下三条规则:
-
最后要删除自己建立的所有GDI对象。
-
当GDI对象正在一个有效的设备内容中使用时,不要删除它。
-
不要删除现有对象。
这些规则当然是有道理的,而且有时这道理还挺微妙的。下面我们将举些例子来帮助理解这些规则。
CreatePen函数的语法形如:
hPen = CreatePen (iPenStyle, iWidth, crColor) ;
其中,iPenStyle参数确定画笔是实线、点线还是虚线,该参数可以是WINGDI.H表头文件中定义的以下标识符,图5-14显示了每种画笔产生的画笔样式。
对于PS_SOLID、PS_NULL和PS_INSIDEFRAME画笔样式,iWidth参数是画笔的宽度。iWidth值为0则意味着画笔宽度为一个图素。现有画笔是一个图素宽。如果指定的是点划线或者虚线式画笔样式,同时又指定一个大于1的实际宽度,那么Windows将使用实线画笔来代替。
CreatePen的crColor参数是一个COLORREF值,它指定画笔的颜色。对于除了PS_INSIDEFRAME之外的画笔样式,如果将画笔选入设备内容中,Windows会将颜色转换为设备所能表示的最相近的纯色。PS_INSIDEFRAME是唯一一种可以使用混色的画笔样式,并且只有在宽度大于1的情况下才如此。
在与定义一个填入区域的函数一起使用时,PS_INSIDEFRAME画笔样式还有另外一个奇特之处:对于除了PS_INSIDEFRAME以外的所有画笔样式来说,如果用来画边界框的画笔宽度大于1个图素,那么画笔将居中对齐在边界框在线,这样边界框线的一部分将位于边界框之外;而对于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, 0) ; hPen2 = CreatePen (PS_SOLID, 3, RGB (255, 0, 0)) ; hPen3 = CreatePen (PS_DOT, 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将传回设备内容中上一次选择的画笔句柄。所以,您可以通过呼叫SelectObject将BLACK_PEN选进设备内容,并删除从SelectObject传回的值:
DeleteObject (SelectObject (hdc, GetStockObject (BLACK_PEN))) ;
下面是另一种方法,在将新建立的画笔选进设备内容时,保存SelectObject传回的画笔句柄:
hPen = SelectObject (hdc, CreatePen (PS_DASH, 0, RGB (255, 0, 0))) ;
现在hPen是什么呢?如果这是在取得设备内容之后第一次呼叫SelectObject,则hPen是BLACK_PEN对象的句柄。现在,可以将hPen选进设备内容,并删除所建立的画笔(第二次SelectObject呼叫传回的句柄),只要一道叙述即可:
DeleteObject (SelectObject (hdc, hPen)) ;
如果有一个画笔的句柄,就可以通过呼叫GetObject取得LOGPEN结构各个成员的值:
GetObject (hPen, sizeof (LOGPEN), (LPVOID) &logpen) ;
如果需要目前选进设备内容的画笔句柄,可以呼叫:
hPen = GetCurrentObject (hdc, OBJ_PEN) ;
在第十七章将讨论另一个建立画笔的函数ExtCreatePen。