【新书推荐】4.3.5画笔、背景模式和绘图模式

使用默认画笔

在实例LINEDEMO.C中,我们使用默认黑色画笔分别绘制了直线、矩形、椭圆和圆角矩形几种不同类型的边框。但是仔细观察代码,我们并没有设置黑色画笔就可以绘制了。这是因为Windows系统默认的画笔就是黑色画笔。当然我们也可以选择其他系统预定义的画笔。

在动手实验中,我们分别给出了Windows系统预定义的仅有的三种画笔:白色画笔(WHITE_PEN)、空画笔(NULL_PEN)和黑色画笔(BLACK_PEN)。不过需要首先调用GetStockObject函数获取系统预定义画笔的句柄,然后再调用SelectObject函数,将画笔句柄选人设备环境hdc中,替换之前设备环境中的画笔,这样就可以使用了。

画笔作为GDI对象,使用时必须符合以下三个原则(所有GDI对象都必须遵守):

1.Windows系统预定义的GDI对象不可以删除。

2.自定义的GDI对象使用完之后必须删除。

3.正在使用中的GDI对象不可以删除。

创建、选择和删除画笔

●创建画笔

如果想获得更丰富的效果,则必须创建自己的画笔。

这里是创建画笔的一般过程:调用CreatePen或者CreatePenlndirect函数创建一个“逻 辑画笔”,它只是说明你想得到一个什么样的画笔。这些函数会返回一个逻辑画笔的句柄。 然后需要调用SelectObject函数将画笔选入设备环境中。接着,就可以使用这个新的画笔来绘制线条。一次只能有一支画笔被选入设备环境。释放设备环境之后(或者将其他画笔选入设备环境之后),需要调用DeleteObject函数来删除你创建的逻辑画笔。此后,画笔的句柄不再有效。

逻辑画笔是一个“GDI对象”,一个程序可以创建6种GDI对象,它是其中之一,其他5种分别是画刷、位图、区域、字体和调色板。除了调色板之外,所有这些对象都通过SelectObject函数选入设备环境。

1.CreatePen函数:用于创建一个新的画笔对象。

CreatePen函数的原型可能会因所使用的编程语言或图形库而有所不同。以下是在Windows API中的CreatePen函数的常见原型:

HPEN CreatePen(

int fnPenStyle, // 画笔样式

int nWidth, // 画笔宽度

COLORREF crColor // 画笔颜色

);

nPenStyle:画笔的样式

含义

PS_SOLID

触控笔是实心的。

PS_DASH

触控笔虚线。 仅当笔宽度为 1 或更少(以设备单位为单位)时,此样式才有效。

PS_DOT

笔被点点。 仅当笔宽度为 1 或更少(以设备单位为单位)时,此样式才有效。

PS_DASHDOT

笔具有交替的短划线和点。 仅当笔宽度为 1 或更少(以设备单位为单位)时,此样式才有效。

PS_DASHDOTDOT

笔具有交替的短划线和双点。 仅当笔宽度为 1 或更少(以设备单位为单位)时,此样式才有效。

PS_NULL

笔不可见。

PS_INSIDEFRAME

触控笔是实心的。 在采用边框的任何 GDI 绘图函数中使用此笔时,图形的尺寸会缩小,使其完全适合边界矩形,同时考虑笔的宽度。 这仅适用于几何笔。

表4-1 画笔样式

图4-5 画笔样式

对于PS_SOLID、PS_NULL和PSJNSIDEFRAME样式,参数iWidth表示画笔的宽度。 当iWidth值为0时,Windows把画笔的宽度设定为1个像素。备用画笔总是1个像素宽。 如果指定使用虚线或点线样式,同时把画笔宽度设定为大于1个像素,那么Windows会使 用实心的画笔来代替。

CreatePen的参数crColor是一个COLORREF值,它用来指定画笔的颜色。对所有的除 了PS_INSIDEFRAME之外的画笔样式,当将画笔选入到设备环境时,Windows将该颜色转换为设备所能表示的接近的纯色。

PS_INSIDEFRAME 画笔样式是唯一能够使用抖动色的画笔样式,并且只有当画笔宽度大于1时才如此。

PS_INSIDEFRAME画笔样式用于填充区域的函数时有另外一个奇特之处。当使用非PS_INSIDEFRAME 样式的笔样式时,如果用于绘制轮廓的画笔宽度大于1个像素,那么画笔的中心会处于边界之上,这样画出的轮廓线部分将会在边框之外。但是对于PS_INSIDEFRAME 画笔样式,整个轮廊线都会在边框内。

也可以通过建立一个类型为LOGPEN(“逻辑画笔”)的结构,并调用CreatePenlndirect 函数来建立一个画笔。如果你的程序在初始化时需要创建很多不同的画笔,这种方法会很有效。

CreatePen函数用于创建一个新的画笔对象,并返回该画笔对象的句柄(HPEN)。通过该句柄,可以在绘图过程中使用该画笔对象来绘制线条。

创建的画笔对象可以使用SelectObject函数将其选入设备上下文(HDC)中,以便在绘图操作中使用该画笔进行线条绘制。

需要注意的是,使用完创建的画笔对象后,应使用DeleteObject函数将其销毁,以释放相关资源并避免内存泄漏。

2.CreatePenIndirect函数是Windows操作系统中的一个函数,用于创建一个根据指定的逻辑画笔结构(LOGPEN)参数描述的画笔(Pen)对象。它的函数原型如下:

HPEN CreatePenIndirect(

const LOGPEN* lplgpn // 指向LOGPEN结构体的指针

);

LOGPEN结构体定义如下:

typedef struct tagLOGPEN {

UINT lopnStyle; // 画笔样式

POINT lopnWidth; // 画笔宽度

COLORREF lopnColor;// 画笔颜色

} LOGPEN;

CreatePenIndirect函数使用指定的“LOGPEN”结构体参数创建一个新的画笔对象,并返回该画笔对象的句柄(`HPEN`)。通过该句柄,可以在绘图过程中使用该画笔对象来绘制线条。

我们会发现,“LOGPEN”结构体其实就是CreatePen函数的三个参数。可以将CreatePenIndirect函数视为CreatePen函数的另一种形式。在Windows API函数中还有很多类似的情形,后面的章节中我们会陆续接触到。

举例

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) ;

●选择画笔

如果是自定义的画笔,调用CreatePenIndirect函数或CreatePen函数都会返回一个新建画笔的句柄,接着调用SelectObject函数将其选入设备上下文(HDC)中,就可以使用该画笔绘图了。

举例

在处理WM_PAINT消息时(或者在任何拥有有效的设备环境句柄时)可以将其中任何一支 画笔选入到设备环境,并且使用它来绘制线条:

SelectObject (hdc, hPen2) ; //选入画笔hPen2

画线函数

SelectObject (hdc, hPen1) ; //绘制完成后,重新选入画笔hPen1

●删除画笔

是否还记得GDI对象三原则,自己创建的GDI对象不用时,需要将其替换出HDC,然后再删除。

删除画笔使用DeleteObject函数:用于销毁(删除)由CreatePen、CreateBrush、CreateFont等函数创建的图形对象,以释放相关资源并避免内存泄漏。

DeleteObject函数的原型可能会因所使用的编程语言或图形库而有所不同。以下是在Windows API中的DeleteObject函数的常见原型:

BOOL DeleteObject(

HGDIOBJ hObject // 图形对象句柄

);

DeleteObject函数用于销毁指定的图形对象,释放相关的资源。一旦对象被销毁,与之相关联的设备上下文(HDC)将不再有效。

在使用完创建的图形对象后,应调用DeleteObject函数将其销毁,以确保资源的释放和避免内存泄漏。这适用于画笔、画刷、字体等各种类型的图形对象。

需要注意的是,只能销毁由GDI函数创建的图形对象。对于其他类型的对象,如窗口句柄(HWND),应使用适当的函数进行销毁。

举例

方法1:

在处理WM_DESTROY消息时,可以删除这三种画笔:

DeleteObject (hPen1) ;

DeleteObject (hPen2) ;

DeleteObject (hPen3) ;

方法2:

可以通过将备用的BLACK_PEN选入到设备 环境中来得到需要被删除的画笔句柄,然后删除它:

DeleteObject (SelectObject (hdc, GetStockObject (black_PEN)));

方法3:

当将一支画笔选入到一个新创建的设备环境时,保存 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 = GetCurrentObjecc (hdc, 0BJ_PEN);

另外一个画笔创建函数:ExtCreatePen,我们将在第十五章讲述。

4.3.6 背景模式和绘图模式

填充背景颜色

上一小节中我们使用画笔绘制线条构成的基本图形,但是有一个问题,这些图形边框内的间隙怎样填充。这就需要用到画刷。是否还记得,在我们初始化窗口类的时候,选用了Windows系统预定义的白色画刷。

wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;

如果我们想改变填充色,还可以调用SetBkColor (hdc, crColor);

SetBkColor函数用于设置设备上下文(Device Context)中的背景色。SetBkColor函数的原型:

COLORREF SetBkColor(

HDC hdc, // 设备上下文句柄

COLORREF crColor // 背景色

);

SetBkColor函数用于设置设备上下文的背景色。背景色是绘制操作的背景色彩,例如在填充矩形、绘制文本时的背景色。设置背景色后,绘制操作将使用指定的背景色作为背景。

需要注意的是,SetBkColor函数只设置背景色,并不会立即对设备上下文中的现有绘图进行影响。绘图操作实际上是在调用绘图函数(如TextOut、Rectangle等)时应用背景色。

设置背景模式

我们经常会见到窗口客户区内显示的文本会有下划线,这是如何实现的呢?这涉及到窗口的背景模式。其实下划线也是一个独立的字符,有着和正常字符一样的宽度和高度。如果按照正常字符显示,显然会覆盖掉上有一行字符。为了避免这种情形的发生,我们需要将窗口的背景模式由默认的不透明模式(OPAQUE)设置为透明模式(TRANSPARENT)。待绘制完下划线之后,再恢复原有的不透明模式。例如:

SetBkMode (hdc, TRANSPARENT);//将窗口背景模式设置为透明模式。

SetBkMode (hdc, OPAQUE);//将窗口背景模式设置为不透明模式。

SetBkMode函数用于设置设备上下文(Device Context)中的背景模式。

SetBkMode函数的原型:

int SetBkMode(

HDC hdc, // 设备上下文句柄

int mode // 背景模式

);

背景模式定义了绘制操作的背景处理方式。通过设置背景模式,可以控制绘制操作是否填充背景色以及如何处理背景。

如果背景模式设置为TRANSPARENT,绘制操作将不填充背景色,可以实现绘制透明的效果。这在绘制文本时常用,使得文本绘制时只覆盖前景部分,而不影响背景。

如果背景模式设置为OPAQUE,绘制操作将使用背景色填充背景,确保绘制的内容在背景上完全不透明。

需要注意的是,SetBkMode函数只设置背景模式,并不会立即对设备上下文中的现有绘图进行影响。绘图操作实际上是在调用绘图函数(如TextOut、Rectangle等)时应用背景模式。

设置绘图模式

在Windows编程中,绘图模式通常指的是一种指导如何进行绘图操作的模式或策略。在许多不同的绘图操作中,例如绘制线条、填充形状或者显示文本,都存在着各自的绘图模式。以下是一些主要的绘图模式:

1.背景模式(Background Mode):可以使用SetBkMode函数设置。当背景模式设置为OPAQUE时,绘制文本时会使用当前的背景色来填充文本的背景。而当背景模式设置为TRANSPARENT时,绘制文本时不会修改文本的背景。

2.映射模式(Mapping Mode):控制用于设备上下文的坐标系统如何映射到物理设备,比如位图或打印机等。可用的映射模式包括MM_TEXT(逻辑单位等于物理单位)、MM_LOMETRIC、MM_HIMETRIC(使用公制单位)、MM_LOENGLISH、MM_HIENGLISH(使用英制单位)等。以MM_TEXT为例,此模式下,逻辑单位和实际的像素一一对应。我们将在4.5节详细讲解Windows系统的映射机制。

3.混合模式(ROP2 Mode):控制如何通过二元光栅操作(binary raster operation)来混合源颜色和目标颜色。例如,R2_COPYPEN只绘制源颜色,R2_MASKPEN只在源色和目标色相同的地方绘制目标色等。

混合模式(“Rop” means “Raster OPeration”)可以使用SetROP2函数进行设置。该函数的原型如下:

int SetROP2(

HDC hdc,

int rop2

);

其中,参数hdc是设备上下文的句柄,参数rop2则是指定设置的混合模式。可用的混合模式包括以下几种:

R2_BLACK:总是返回黑色。

R2_COPYPEN:源色覆盖目标色。

R2_MASKNOTPEN:源色与目标色的反色逻辑与。

R2_MASKPEN:源色与目标色的逻辑与。

R2_MASKPENNOT:源色与目标色的逆逻辑与。

R2_MERGENOTPEN:源色与目标色的反色逻辑或。

R2_MERGEPEN:源色与目标色的逻辑或。

R2_MERGEPENNOT:源色与目标色的反色逻辑或。

R2_NOP:忽略源色。

R2_NOT:目标色的反色。

R2_NOTCOPYPEN:源色的反色。

R2_NOTMASKPEN:源色或目标色的反色逻辑与。

R2_NOTMERGEPEN:源色或目标色的反色逻辑或。

R2_NOTXORPEN:源色或目标色的反色逻辑异或。

R2_WHITE:总是返回白色。

R2_XORPEN:源色与目标色的逻辑异或。

这些模式定义了在进行像素绘制操作时源色与目标色的组合方式,可以精确地控制如何渲染新色。例如,R2_COPYPEN模式将简单的用源色覆盖目标色,而R2_XORPEN模式则会用源色与目标色的逻辑异或结果作为新色。这个功能可以使开发者在进行绘图操作时能获得不同的视觉效果或者实现特殊的视觉需求。

如果我们想获取当前绘图模式:iDrawMode = GetR0P2 (hdc);

Windows提供了一个名为GetROP2的宏,用于获取设备上下文(Device Context)中的当前布尔模式(Raster Mode)。

该宏的使用方式如下:

int GetROP2(

HDC hdc // 设备上下文句柄

);

GetROP2宏用于获取当前设备上下文中的布尔模式。布尔模式定义了绘制操作的像素合并规则,即如何将新的绘制操作与已有的图像进行合并。返回值为整型,表示当前的布尔模式。

需要注意的是,GetROP2宏返回的是一个整型值,而不是一个函数。因此,调用GetROP2宏时不需要使用函数调用的语法,而是直接使用宏的名称。

举例

下面是一个示例代码,演示如何使用GetROP2宏获取当前设备上下文的布尔模式:

HDC hdc = GetDC(hwnd); // 获取窗口的设备上下文

int rop2 = GetROP2(hdc); // 获取当前布尔模式

ReleaseDC(hwnd, hdc); // 释放设备上下文

绘图模式可以帮助开发者详细控制绘图操作的行为,根据需要选择适当的模式可以实现更为复杂和精细的绘图效果。例如,改变背景模式,可以控制是否填充文本背景;而改变映射模式,则可以更好地控制如何在不同大小和分辨率的设备上进行绘图。

本文摘自编程达人系列教材《Windows API每日一练》。

举报
评论 0