目录
使用画刷绘图
简单画刷
FillRect函数
位图画刷
透明画刷
接上:VC++ 利用MFC的CWindowDC类实现画线功能 在桌面窗口中画线 绘制彩色线条 CPen nPenStyle nWidth crColor
接下:VC++ 绘制连续线条 绘制扇形效果的线条
MFC提供了一个CBrush类,可以用来创建画刷对象。画刷通常用来填充一块区域。
如下所示代码实现的功能是利用一个红色画刷填充鼠标拖曳过程中形成的一块矩形区域。
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point)
{
//创建一个红色画刷
CBrush brush(RGB(255, 0, 0));
//创建并获得设备描述表
CClientDC dc(this);
//利用红色画刷填充鼠标拖拽过程中形成的矩形区域
dc.FillRect(CRect(m_ptOrigin, point), &brush);
CView::OnLButtonUp(nFlags, point);
}
在上例代码中,首先创建一个红色画刷;接着创建设备描述表对象;然后调用设备描述表对象的成员函数 FillRect,利用指定的画刷填充一块指定的矩形区域,而鼠标拖动过程中的起点和终点就决定了需要填充的矩形区域的大小,因此,在代码中通过CRect类利用鼠标拖动的起点和终点构造了这块矩形区域。CRect类提供了多个构造函数,本例使用的是下面这个构造函数,即通过指定矩形区域的左上角和右下角这两个点来构造一块矩形区域。
CRect(
POINT topLeft,
POINT bottomRight
) throw( );
在上面代码中使用CDC类的成员函数FillRect,该函数的功能是用指定的画刷填充一个矩形。该函数将填充全部的矩形,包括左边和上部边界,但不填充右边和底部边界。FillRect函数的声明如下所示。
void FillRect(
LPCRECT lpRect,
CBrush* pBrush
);
该函数有两个参数,各自的含义如下所述。
■ lpRect指向一个RECT结构体或CRect对象的指针,该结构体或对象中包含了要填充的矩形的逻辑坐标。
■ pBrush指向用于填充矩形的画刷对象的指针。编译并运行Draw程序,并在程序窗口中任意拖动鼠标,将会得到多个红色区域。
提示: 这里我们只是用指定的画刷填充一块区域,因此,并不需要把画刷选入设备描述表中。在设备描述表中存在一个默认的白色画刷。
CBrush类有下面这样一个构造函数。
explicit CBrush(
CBitmap* pBitmap
);
该构造函数要求一个CBitmap类型的指针,CBitmap类是位图类,于是我们就会这样想:利用这个构造函数是否可以创建一个位图画刷呢?事实确实如此。在创建CBitmap对象时,仅调用其构造函数并不能得到一个有用的位图对象,还需要调用一个初始化函数来初始化这个位图对象。CBitmap 类提供多个初始化函数,例如, LoadBitmap、CreateBitmap、CreateBitmapIndirect等。本例使用LoadBitmap函数来加载一幅位图,该函数的声明如下。
BOOL LoadBitmap(
LPCTSTR lpszResourceName
);
BOOL LoadBitmap(
UINT nIDResource
);
其中第二种声明需要一个资源ID作为参数。
首先需要给 Draw 程序增加一个位图资源。在Visual Studio 开发环境右侧的“解决方案资源管理器”窗口中,在“资源文件”文件夹上单击鼠标右键,在弹出菜单中选择【添加】→【资源】,出现如图所示的“添加资源”对话框。
选择Bitmap资源类型,单击【新建】按钮,即可创建一个默认名称为IDB_BITMAP1的位图资源,并在Visual Studio集成开发环境左边的代码编辑区域中打开位图编辑器,如图所示。
可以利用编辑器左边的颜色面板和上图矩形框中的图像编辑器工具栏按钮来编辑位图资源,还可以通过拉伸位图编辑器中网格周围的黑色方点来调整位图的大小。本例创建了一个如下图所示的位图资源。
提示: 项目中的资源都是在一个资源文件中定义的,针对本例,该文件名为Draw.rc,这是一个文本文件,在了解其格式的情况下,我们可以手动编写资源文件。而在集成开发环境中,显然不需要我们这么做。在Visual Studio 2017开发环境中,单击菜单栏上的【视图】菜单,选择【其他窗口】→【资源视图】,就可以打开资源视图,在该视图窗口中,我们可以查看项目中的所有资源,并以可视化的方式编辑它,如下图所示。
在创建了位图资源之后,就可以利用代码来创建位图画刷了,具体的实现代码如下所示。
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point)
{
//创建位图对象
CBitmap bitmap;
//加载位图资源
bitmap.LoadBitmap(IDB_BITMAP1);
//创建位图画刷
CBrush brush(&bitmap);
//创建并获得设备描述表
CClientDC dc(this);
//利用位图画刷填充鼠标拖拽过程中形成的矩形区域
dc.FillRect(CRect(m_ptOrigin, point), &brush);
CView::OnLButtonUp(nFlags, point);
}
同时在CDrawView的源文件的头部添加下面一句包含语句。
#include "Resource.h"
这是因为我们刚添加的位图资源,其ID名IDB_BITMAP1是在该文件中定义的。
编译并运行Draw程序,然后在程序窗口内拖动鼠标,即可看到利用所创建的位图画刷填充的效果,如下图所示。
下面利用CDC的Rectangle函数绘制一个矩形,代码如下所示。
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point)
{
//创建并获得设备描述表
CClientDC dc(this);
//绘制一个矩形
dc.Rectangle(CRect(m_ptOrigin, point));
CView::OnLButtonUp(nFlags, point);
}
编辑并运行Draw程序,然后拖动鼠标即可在程序窗口中绘制矩形。但是,当我们绘制两个相互重叠的矩形时,后绘制的矩形就会遮盖住先前绘制的矩形,如下图所示。这是因为在设备描述表中有一个默认的白色画刷,在绘图时,它会利用这个画刷来填充矩形内部。所以,当位置存在重叠时,后绘制的矩形就会把先前绘制的矩形遮挡住。
如果希望矩形内部是透明的,能够看到被遮挡的图形,那么就要创建一个透明画刷。此时,我们会立即想到CBrush类是否有创建透明画刷的方法。但很遗憾,CBrush 类并没有提供这样的函数,我们只能另想其他的办法了。
在前面介绍过GetStockObject这个函数,利用该函数可以获取一个黑色或白色的画刷句柄。这个函数是否能够获得一个透明画刷的句柄呢?从 MSDN 提供的帮助信息中,可以看到该函数的参数取值之一可以是NULL_BRUSH,以获取一个空画刷。那么,这个空画刷是否就是我们所需要的透明画刷呢?可以试试看。但这时存在一个问题,利用GetStockObject函数获取的是一个画刷句柄,而我们在进行绘制操作时需要的是一个画刷对象。如何从画刷句柄转换为画刷对象呢?CBrush类提供了一个FromHandle函数用来实现这样的功能。该函数的声明如下所示。
static CBrush* PASCAL FromHandle(
HBRUSH hBrush
);
如下就是具体的创建透明画刷并进行相应绘制操作的实现代码。
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point)
{
//创建并获得设备描述表
CClientDC dc(this);
//创建一个空画刷
CBrush *pBrush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
//将空画刷选入设备描述表
CBrush *pOldBrush = dc.SelectObject(pBrush);
//绘制一个矩形
dc.Rectangle(CRect(m_ptOrigin, point));
//恢复先前的画刷
dc.SelectObject(pOldBrush);
CView::OnLButtonUp(nFlags, point);
}
上面所示代码中有以下几处需要注意的地方。
■ FromHandle函数的调用方式,这是调用类的静态成员函数的方式。
■ 由于GetStockObject 函数返回的类型是HGDIOBJECT,需要进行一个强制类型转换,将其转换为HBRUSH类型。
■ 这里创建的pBrush变量本身就是一个指针类型,因此在调用SelectObject函数时,该变量前面不用再加上取址符(&)。
■ 另外,我们可以比较一下前面使用的FillRect函数和这里的Rectangle函数。首先,二者都能绘制矩形,但前者在参数中提供了绘制使用的画刷,因此它就直接利用此画刷来填充矩形,并不需要先把需要的画刷选入设备描述表中。而后者并没有提供画刷这个参数,因此先要把需要的画刷选入设备描述表中,然后再调用此函数来绘制矩形。
编译并运行Draw程序,拖动鼠标在窗口中任意绘制多个相互重叠的矩形。此时能够看到被覆盖的矩形线条了,如下图所示。由此可见,利用参数NULL_BRUSH调用GetStockObject函数可以实现透明画刷这一功能。