CClientDC dc(this);
CBrush *b=new CBrush(RGB(0,0,0));
dc.SelectObject(b);
dc.RoundRect(60,60,300,160,10,10);
RoundRect
函数功能:该函数画一个带圆角的矩形,此矩形由当前画笔画轮廓,由当前画刷填充。 函数原型:BOOL RoundRect(HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect, int nWidth, int nHeight); 参数: hdc:设备环境句柄。 nLeftRect:指定矩形左上角的X坐标。 nTopRect:指定矩形左上角的Y坐标。 nRightRect:指定矩形右下角的X坐标。 nbottomRect:指定矩形右下角的Y坐标。 nWidth:指定用来画圆角的椭圆的宽。 nHeight:指定用来画圆角的椭圆的高。 返回值:如果函数调用成功,则返回值非空,否则返回值是0。 Windows NT:若想获得更多的错误信息,请调用GetLastError函数。 备注:此函数不使用和改变当前位置。 Windows 95和Windows 97:限定矩形的坐标之和不能超过32767。nLeftRect和nRightRect之和或nTopRect和nBottomRect之和均不能超过32767。 速查:Windows NT:3.1及以上版本;Windows:95及以上版本;Windows CE:1.0及以上版本;头文件:wingdi.h;库文件:gdi32.lib。 |
可用如下代码:
SDK版本的:
HDC hDC=GetDC(hwnd);
HBRUSH hBrush=CreateSolidBrush(RGB(122,122,122));
RECT rect;
rect.bottom=0;
rect.left=0;
rect.right=100;
rect.top=100;
FillRect(hDC,&rect,hBrush);
MFC版本的:
CClientDC dc(this);
HBRUSH hBrush=CreateSolidBrush(RGB(122,122,122));
CBrush brush(RGB(122,122,122));
CRect rect(0,0,100,100);
dc.FillRect(&rect,&brush);
一、 引言
在GIS(地理信息系统)类软件设计中经常需要在绘图时使用一些相对固定但又频繁使用的一些用以代表地理状态的符号如河流、铁路、海岸线等等。每一种符号均有其各自的风格,但在不同的位置的具体表示却不尽相同,比如代表铁路的符号是一段黑白相间的细矩形,但有时是平直的,在拐弯时用弯曲的矩形来表示。因此对于上述符号的绘制一般不易用固定的图标去实现,而多采用灵活多变的用函数来直接绘制的方法。显然作为GIS基本符号的图形一般都是相对比较复杂的线条,在MFC提供的基本类库中并未提供可以直接使用的相关函数。即使是在绘图功能比较强大的CDC中也仅仅提供了LineTo()、SetPixel()等一些通用的最基本的绘图函数,虽然也可以使用这些基本函数来绘制GIS里的基本符号,但这是效率比较低下的一种办法,这在大量的绘图操作中将会表现的比较明显,因此不宜提倡。本文下面将介绍一种使用Win32 API函数LineDDA来绘制复杂风格线条的方法来解决上述类似问题。
二、 把复杂风格的线条作为基本绘图操作
在Windows NT 3.1中首次出现了Win32 API函数LineDDA,用以创建虚线或点划线,以及其他一些更复杂的线条。因此可以从该函数入手来解决以复杂线条作为基本绘图操作的问题。LineDDA函数主要是通过回调机制来实现其功能的,其绘制线条总是被使用当前显示分辨率的缺省转换和映射模式来计算的。如果不使用缺省方式,也可以向其回调函数LineDDAProc传递手工转换的X和Y值。LineDDA是一个32位的图形设备接口库函数调用,从如下所示的函数原形中可以看出其入口参数是一组线条坐标、一个回调函数的地址以及一个指向应用程序定义数据的指针:
BOOL LineDDA( int nXStart, // 线条起点的X坐标 int nYStart, // 线条起点的Y坐标 int nXEnd, // 线条终点的X坐标 int nYEnd, // 线条终点的Y坐标 LINEDDAPROC lpLineFunc, // 回调函数的指针 LPARAM lpData // 应用程序定义数据的指针); |
由lpLineFunc指针指向的回调函数将在除终点外的线段的每个点上被调用,显然这里是实现复杂线条算法的最佳地方。该回调函数一般可以定义如下:
VOID CALLBACK LineDDAProc(int X, // 被求值点的X坐标 int Y, // 被求值点的Y坐标 LPARAM lpData // 应用程序定义数据的指针); |
在实际调用时一般选当前的设备环境句柄作为应用程序定义数据的指针,该指针会在调用LineDDA函数时将其传送给回调函数LineDDAProc,在回调函数中通过CDC* pDC= (CDC*)lpData;强制转换即可在其中使用当前的设备环境句柄,并通过该句柄进行绘图等工作。下面通过一个具体实例来做进一步的介绍:
类似于CDC类库中的绘图函数,当我们把某种较复杂线条作为一个整体元素进行绘图操作时有以下几个元素需要确定:起始点、终止点坐标,绘图时用的颜色等。因此可以初步确定绘制复杂线条的函数采取如下形式:
void CLineDDAView::DrawWave(CPoint ptFrom, CPoint ptTo, COLORREF crValue) { g_crValue=crValue; //绘制图形用的颜色 CDC* pDC=GetDC(); //获取当前设备环境句柄 //通过LineDDA函数调用回调函数Proc以完成复杂线条的绘图工作 LineDDA(ptFrom.x,ptFrom.y,ptTo.x,ptTo.y,(LINEDDAPROC)Proc,(long)pDC); ReleaseDC(pDC); //释放申请到的设备环境句柄 } |
具体的实质性工作一般都是放在回调函数中进行的,在这里只是象征性的实现一个波浪曲线的绘图工作:
viod CALLBACK Proc(int X, int Y, LPARAM lpData) { //使用lpData传递用于绘图的CDC对象的引用 CDC* pDC; pDC = (CDC*)lpData; //该回调函数将在线上每个点处被调用,因此随着X坐标的变化,纵坐标Y+sin(X) //也就呈正弦波形波动,通过CDC类的SetPixel函数将计算出的波浪线上的每一点 //都显示出来。 pDC->SetPixel(X,Y+sin(X),g_crValue); } |
在应用时应当根据需求的不同采取不同的线条风格设计算法,甚至可以在回调函数中使用TextOut函数实现文本文字的曲线显示等效果。在使用我们设计的DrawWave函数进行绘图操作时,可以象是使用CDC提供的LineTo等函数一样非常简单的画出一条在CDC类中并未提供的波浪线。
小结:
LineDDA函数为画出复杂线条类型提供了一种解决办法。虽然是一种Win32的解决办法,在MFC中并不直接支持,但作为一个一般的解决办法,它还是十分有用的,而且它能解决一些没有直接解决办法的比较独特的问题。通过对LineDDA函数的挖掘可以设计出适合自己需要的可以作为绘图基本单元的复杂线条。
在Windows应用程序中,只要进行绘图,就要使用GDI坐标系统。Windows提供了几种映射方式,每一种映射都对应着一种坐标系。例如,绘制图形时,必须给出图形各个点在客户区的位置,其位置用x 和y两个坐标表示,x 表示横坐标,y表示纵坐标。在所有的GDI绘制函数中,这些坐标使用的是一种“逻辑单位”。当GDI函数将结果输出送到某个物理设备上时,Windows将逻辑坐标转换成设备坐标(如屏幕或打印机的像素点)。本文讨论了图形环境中的各个映射模式,包括它们是什么,怎么工作的,以及它们真正的含义。
一、基础知识
3、全窗口坐标,包括一个程序的整个窗口,包括标题条、菜单、滚动条和窗口框,窗口的左上角为(0,0)。使用GetWindowDC得到的窗口设备环境,可以将逻辑单位转换成窗口”坐标。
(三)映射。映射方式定义了Windows如何将GDI函数中指定的逻辑坐标映射为设备坐标。在下文中我们将介绍常用的映射方式。
当在微软的窗口中进行绘图时,绘图的坐标原点在屏幕的左上角,任何物体在屏幕上定位都要参考这个坐标原点。在笛卡尔坐标系统中这个点被定义为坐标原点(0,0),水平坐标轴的正方向是从该点出发向右延伸,垂直坐标轴的正方向是从该点出发向下延伸。
图一、笛卡尔坐标系 |
这个坐标原点只是操作系统默认的坐标原点,所以如果你调用Ellipse(-100, -100, 100, 100)函数来绘制图形的话,你将得到一个圆,它的圆心位于屏幕的左上角,仅仅只有圆的四分之一部分(270度到360度的部分)显示在屏幕上。代码及效果图如下
void CExoDraw1View::OnPaint()
{
CPaintDC dc(this); // 绘图的设备厂上下文
CPen PenBlue;
// 兰色画笔
PenBlue.CreatePen(PS_SOLID, 1, RGB(0, 12, 255));
dc.SelectObject(&pPen);
dc.Ellipse(-100, -100, 100, 100);
}
|
图二、代码效果图 |
按照同样的原理,你可以使用CpaintDC的方法或按照你的要求创建函数来绘制任何几何或非几何图形。例如,下面的代码绘制了两条相互垂直的直线,垂点位与窗口的中心:
void CExoDraw1View::OnPaint()
{
CPaintDC dc(this); // 绘图的设备上下文
CRect Recto;
CPen PenBlue;
PenBlue.CreatePen(PS_SOLID, 1, RGB(0, 12, 255));
dc.SelectObject(&PenBlue);
dc.Ellipse(-100, -100, 100, 100);
CPen PenBlack;
PenBlack.CreatePen(PS_SOLID, 1, BLACK_PEN);
dc.SelectObject(&PenBlack);
// 得到客户区域的尺寸;
GetClientRect(&Recto);
dc.MoveTo(Recto.Width() / 2, 0);
dc.LineTo(Recto.Width() / 2, Recto.Height());
dc.MoveTo(0, Recto.Height() / 2);
dc.LineTo(Recto.Width(), Recto.Height() / 2);
}
|