解决MFC绘图过程中的闪烁

解决MFC绘图过程中的闪烁
解决MFC绘图过程中的闪烁

如何解决绘图过程中的闪烁

在VC中进行绘图过程处理时,如果图形刷新很快,

经常出现图形闪烁的现象。利用先在内存绘制,然后

拷贝到屏幕的办法可以消除屏幕闪烁,具体的方法是先在内存

中创建一个与设备兼容的内存设备上下文,也就是开辟一快内

存区来作为显示区域,然后在这个内存区进行绘制图形。在绘制完成后利用

BitBlt函数把内存的图形直接拷贝到屏幕上即可。

具体的代码实现为:

(1)创建内存区域

CDC* pMem=new CDC;

CBitmap* pBmp=new CBitmap;

CBitmap* pOldBmp;

CDC* pDC=GetDC();

CRect rectTemp;为绘图区域

pMem->CreateCompatibleDC(pDC);

pBmp->CreateCompatibleBitmap(pDC, rectTemp.Width(), rectTemp.Height());

pOldBmp=pMem->SelectObject(pBmp);

(2)进行图形绘制

pMem->LineTo(...); 进行绘图处理

(3)拷贝到屏幕

pDC->BitBlt(rectTemp.left,rectTemp.top,rectTemp.Width(),rectTemp.Height(),pMem,0,0,SRCCO PY);

pMem->SelectObject(pOldBmp);

pBmp->DeleteObject() ;

pMem->DeleteDC();

====================================================================== ==========================

双缓存机制解决VC++绘图时的闪烁问题

显示图形如何避免闪烁,如何提高显示效率是问得比较多的问题。

而且多数人认为MFC的绘图函数效率很低,总是想寻求其它的解决方案。

MFC的绘图效率的确不高但也不差,而且它的绘图函数使用非常简单,

只要使用方法得当,再加上一些技巧,用MFC可以得到效率很高的绘图程序。

我想就我长期(呵呵当然也只有2年多)使用MFC绘图的经验谈谈

我的一些观点。

1、显示的图形为什么会闪烁?

我们的绘图过程大多放在OnDraw或者OnPaint函数中,OnDraw在进行屏

幕显示时是由OnPaint进行调用的。当窗口由于任何原因需要重绘时,

总是先用背景色将显示区清除,然后才调用OnPaint,而背景色往往与绘图内容

反差很大,这样在短时间内背景色与显示图形的交替出现,使得显示窗口看起来

在闪。如果将背景刷设置成NULL,这样无论怎样重绘图形都不会闪了。

当然,这样做会使得窗口的显示乱成一团,因为重绘时没有背景色对原来

绘制的图形进行清除,而又叠加上了新的图形。

有的人会说,闪烁是因为绘图的速度太慢或者显示的图形太复杂造成的,

其实这样说并不对,绘图的显示速度对闪烁的影响不是根本性的。

例如在OnDraw(CDC *pDC)中这样写:

pDC->MoveTo(0,0);

pDC->LineTo(100,100);

这个绘图过程应该是非常简单、非常快了吧,但是拉动窗口变化时还是会看见

闪烁。其实从道理上讲,画图的过程越复杂越慢闪烁应该越少,因为绘图用的

时间与用背景清除屏幕所花的时间的比例越大人对闪烁的感觉会越不明显。

比如:清楚屏幕时间为1s绘图时间也是为1s,这样在10s内的连续重画中就要闪

烁5次;如果清楚屏幕时间为1s不变,而绘图时间为9s,这样10s内的连续重画

只会闪烁一次。这个也可以试验,在OnDraw(CDC *pDC)中这样写:

for(int i=0;i<100000;i++)

{

pDC->MoveTo(0,i);

pDC->LineTo(1000,i);

}

呵呵,程序有点变态,但是能说明问题。

说到这里可能又有人要说了,为什么一个简单图形看起来没有复杂图形那么

闪呢?这是因为复杂图形占的面积大,重画时造成的反差比较大,所以感觉上要

闪得厉害一些,但是闪烁频率要低。

那为什么动画的重画频率高,而看起来却不闪?这里,我就要再次强调了,

闪烁是什么?闪烁就是反差,反差越大,闪烁越厉害。因为动画的连续两个帧之间的差异很小所以看起来不闪。如果不信,可以在动画的每一帧中间加一张纯白的帧,不闪才怪呢。

2、如何避免闪烁

在知道图形显示闪烁的原因之后,对症下药就好办了。首先当然是去掉MFC

提供的背景绘制过程了。实现的方法很多,

* 可以在窗口形成时给窗口的注册类的背景刷付NULL

* 也可以在形成以后修改背景

static CBrush brush(RGB(255,0,0));

SetClassLong(this->m_hWnd,GCL_HBRBACKGROUND,(LONG)(HBRUSH)brush);

* 要简单也可以重载OnEraseBkgnd(CDC* pDC)直接返回TRUE

这样背景没有了,结果图形显示的确不闪了,但是显示也象前面所说的一样,

变得一团乱。怎么办?这就要用到双缓存的方法了。双缓冲就是除了在屏幕上有

图形进行显示以外,在内存中也有图形在绘制。我们可以把要显示的图形先在内存中

绘制好,然后再一次性的将内存中的图形按照一个点一个点地覆盖到屏幕上去(这个

过程非常快,因为是非常规整的内存拷贝)。这样在内存中绘图时,随便用什么反差

大的背景色进行清除都不会闪,因为看不见。当贴到屏幕上时,因为内存中最终的图形

与屏幕显示图形差别很小(如果没有运动,当然就没有差别),这样看起来就不会闪。

3、如何实现双缓冲

首先给出实现的程序,然后再解释,同样是在OnDraw(CDC *pDC)中:

CDC MemDC; //首先定义一个显示设备对象

CBitmap MemBitmap;//定义一个位图对象

//随后建立与屏幕显示兼容的内存显示设备

MemDC.CreateCompatibleDC(NULL);

//这时还不能绘图,因为没有地方画 ^_^

//下面建立一个与屏幕显示兼容的位图,至于位图的大小嘛,可以用窗口的大小MemBitmap.CreateCompatibleBitmap(pDC,nWidth,nHeight);

//将位图选入到内存显示设备中

//只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上

CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap);

//先用背景色将位图清除干净,这里我用的是白色作为背景

//你也可以用自己应该用的颜色

MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255));

//绘图

MemDC.MoveTo(……);

MemDC.LineTo(……);

//将内存中的图拷贝到屏幕上进行显示

pDC->BitBlt(0,0,nWidth,nHeight,&MemDC,0,0,SRCCOPY);

//绘图完成后的清理

MemBitmap.DeleteObject();

MemDC.DeleteDC();

上面的注释应该很详尽了,废话就不多说了。

4、如何提高绘图的效率

我主要做的是电力系统的网络图形的CAD软件,在一个窗口中往往要显示成千上万个电力元件,而每个元件又是由点、线、圆等基本图形构成。如果真要在一次重绘过程重画这么多

元件,可想而知这个过程是非常漫长的。如果加上了图形的浏览功能,鼠标拖动图形滚动时需要进行大量的重绘,速度会慢得让用户将无法忍受。怎么办?只有再研究研究MFC 的绘图过程了。

实际上,在OnDraw(CDC *pDC)中绘制的图并不是所有都显示了的,例如:你

在OnDraw中画了两个矩形,在一次重绘中虽然两个矩形的绘制函数都有执行,但是很有可能只有一个显示了,这是因为MFC本身为了提高重绘的效率设置了裁剪区。裁剪区的作用就是:只有在这个区内的绘图过程才会真正有效,在区外的是无效的,即使在区外执行了绘图函数也是不会显示的。因为多数情况下窗口重绘的产生大多是因为窗口部分被遮挡或者窗口有滚动发生,改变的区域并不是整个图形而只有一小部分,这一部分需要改变的就是pDC 中的裁剪区了。因为显示(往内存或者显存都叫显示)比绘图过程的计算要费时得多,有了裁剪区后显示的就只是应该显示的部分,大大提高了显示效率。但是这个裁剪区是MFC设置的,它已经为我们提高了显示效率,在进行复杂图形的绘制时如何进一步提高效率呢?那就只有去掉在裁剪区外的绘图过程了。可以先用 pDC->GetClipBox()得到裁剪区,然后在绘图时判断你的图形是否在这个区内,如果在就画,不在就不画。

如果你的绘图过程不复杂,这样做可能对你的绘图效率不会有提高。

====================================================================== ===========================

====================================================================== =================================================

VC++大数据量绘图时无闪烁刷屏技术实现

引言

当我们需要在用户区显示一些图形时,先把图形在客户区画上,虽然已经画好但此时我们还无法看到,还要通过程序主动地刷新用户区,强制Windows发送一条WM_PAINT消息,这将引发视类OnDraw函数简单地将所有的图形对象重画,这样才完成了图形的显示工作,但在刷新的同时会引起较明显的闪烁尤其是当画面面积较大、图像元素过多时尤为明显甚至达到无法正常工作的地步。因此,我们需要做相应的处理。本文介绍了采用先在内存中绘制图形,然后再把绘好的图形以位图方式从内存拷贝到窗口客户的消除刷屏闪烁的一种方法。

WM_PAINT消息和无效区

·在用户移动窗口或显示窗口时,窗口中先前被隐藏的区域重新可见。

·用户改变窗口的大小。

·滚动窗口用户区。

·程序调用InvalidateRect或InvalidateRgn函数显式地发送一条WM_PAINT消息。

当上面情况之一发生时,就要求应用程序一定刷新其用户区的一部分或全部,Windows会向窗口函数发送一条WM_PAINT消息。另外,当 Windows删除覆盖窗口部分区域的对话框或消息框时和菜单下拉出来又被释放时窗口用户区被临时覆盖,系统会试图保存显示区域,但是不一定能成功,可能向窗口函数发送一条WM_PAINT消息,要求应用程序刷新其用户区。需要说明的是:光标或图符穿过窗口用户区时,也可能覆盖显示内容,但这种情况下,系统一定能保留并恢复被覆盖的区域,所以此时并不会发送WM_PAINT消息来要求应用程序去刷新其显示区。在Windows 应用程序的窗口函数中,对WM_PAINT消息的处理就是刷新其用户区,这是一种固定的程序结构。

为提高刷新效率,我们可以只刷新用户区的一小部分,其余没有发生变化的我们可以不予刷新,窗口函数可以通过调用函数InvalidateRect显式地使用户区内的一个矩形无效。而且只有当窗口客户区的某一部分失效时,其窗口函数才会收到WM_PAINT消息。

刷屏闪烁的产生原因与解决方法

当客户区有所改动,而又要将改动显示出来,就必然要强制Windows发送一条WM_PAINT 消息,从而引发OnDraw函数的重画,这样虽完成了图形的显示,却也会引起较明显的闪烁,当画面上数据不是很多时尚不明显,当客户区有成千上万个点的时候刷新一次会引起整幅画面的剧烈跳动,尤其是对于许多实时监控软件和矢量电子地图软件,此类软件通常在屏幕上都会动辄几千、几万个要素点,很明显单靠发送WM_PAINT 消息引发OnDraw 的重画根本满足不了实际需求。

为了解决上述问题,我们需要做一些相应的处理。首先要先检取无效区,然后创建一个与原设备环境句柄 pDC相兼容的内存设备环境,之后就可以采用在内存中绘制图形并把绘好的图形以位图方式从内存拷贝到窗口客户的方法来消除刷屏时引起的闪烁。这还需要创建一个与原设备环境句柄pDC相兼容的、大小为整个客户区的位图。然后再使新的设备环境dc与pDC具有同样的映射关系,将位图选入内存环境。再使dc的整个客户区都成无效区,再“与上”所检取的无效区,使内存环境与pDC检取的无效区相等。之后便可以进行绘图工作了,绘图完毕之后应当释放所获取的设备环境句柄pDC。否则会造成系统资源的浪费。

程序示例

本示例程序通过打开任意存档文件,将其ASCII码码值当作要显示的数据,并通过一图画控件将其数据以图形的形式依次显示出来。本程序要处理的数据量较大,如不采用本文所述方法将会有很明显的闪烁。

首先新建一基于CFormView的单文档应用程序WaveShower并在Form上添加一"picture"控件,设置其ID为IDC_SCREEN、 Type为Rectangle、Color为Black。在"Extended Styles"属

性页里选中Modal Frame检查框。继续添加一菜单“打开数据文件”,并生成其响应函数OnOpenData()。同时在视类中添加如下成员变量:

int m_BufLen; //数据长度

unsigned char* buffer; //数据缓存

int m_dx; //数据偏移量

int m_DY; //数据显示区的幅度

CPoint* value; //将要显示的数值

int m_DX; //数据显示区的宽度

int m_Y0; //数据显示区参照点位置

CRect rect; //数据显示区矩形

然后在视类中添加函数GetScreenRect()用以获取数据显示区的大小及其他参数;添加函数CleanScreen()完成清除数据显示区的功能;添加函数DrawPoint()以便在数据显示区画点:

void CWaveShowerView::GetScreenRect()

{

CWnd* pStatic = GetDlgItem(IDC_SCREEN);

pStatic->GetWindowRect(&rect);

ScreenToClient(&rect);

rect.top+=4;

rect.left+=4;

rect.bottom-=4;

rect.right-=4;

m_Y0=(rect.bottom-rect.top)/2+rect.top;

m_DX=rect.Width();

m_DY=rect.Height()/2;

value=new CPoint[m_DX];

}

void CWaveShowerView::CleanScreen()

{

CDC* pDC=GetDC();

CPen pen1(PS_SOLID,1,RGB(0,0,0));

CPen* oldPen1=pDC->SelectObject(&pen1);

for(int i=rect.top;i

{

pDC->MoveTo(rect.left,i);

pDC->LineTo(rect.right,i);

}

pDC->SelectObject(&oldPen1);

CPen pen2(PS_SOLID,1,RGB(0,0,255));

CPen* oldPen2=pDC->SelectObject(&pen2);

pDC->MoveTo(rect.left,m_Y0);

pDC->LineTo(rect.right,m_Y0);

pDC->SelectObject(&oldPen2);

ReleaseDC(pDC);

}

void CWaveShowerView::DrawPoint(CPoint pt, COLORREF color)

{

CDC* pDC=GetDC();

pDC->SetPixel(rect.left+pt.x,m_Y0-pt.y,color);

ReleaseDC(pDC);

}

接下来,在视类的OnInitialUpdate()初始化函数中添加代码以进行数据显示的各项前期准备工作,并在“打开数据文件”菜单的响应函数中添加代码以读取文件的内码。

void CWaveShowerView::OnInitialUpdate()

{

CFormView::OnInitialUpdate();

GetParentFrame()->RecalcLayout();

ResizeParentToFit();

GetScreenRect();

for(int i=0;i

value[i].x=value[i].y=0;

SetTimer(0,10,NULL);

}

void CWaveShowerView::OnOpenData()

{

CString FileName="";

CFile file;

CFileDialog dlg(TRUE,"*","*.*",

OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,"所有文件(*.*)|*.*||",NULL);

if(dlg.DoModal()==IDOK)

{

KillTimer(1);

FileName=dlg.GetPathName();

file.Open(FileName,CFile::modeReadWrite);

m_BufLen=file.GetLength();

buffer= new unsigned char[m_BufLen+m_DX+10];

file.Read(buffer,m_BufLen);

file.Close();

SetTimer(1,10,NULL);

}

}

下面将要添加的定时器响应函数正是本文的重点,为方便对比起见,笔者写了两个OnTimer

响应函数,前一个是采用常规的普通方法描点的,运行起来可以很明显地看到画面的闪烁跳动。而后一种则是采用本文所述方法采用的内存画图的方法,运行后几乎画面无闪烁。下面便是两段对比代码的原码部分:

//代码一:有闪烁的代码

void CWaveShowerView::OnTimer(UINT nIDEvent)

{

if(nIDEvent==0)

{

CleanScreen();

for(int i=0;i

DrawPoint(value[i],RGB(0,255,0));

}

if(nIDEvent==1)

{

m_dx+=2;

for(int i=0;i

{

value[i].x=i;

if(m_dx+i<0)

buffer[m_dx+i]=128;

if(m_dx+i<-m_DX)

m_dx-=2;

if(m_dx+i>m_BufLen)

buffer[m_dx+i]=128;

if(m_dx+i>m_BufLen+m_DX)

m_dx-=2;

value[i].y=m_DY*(buffer[m_dx+i]-128)/256;

}

}

CFormView::OnTimer(nIDEvent);

}

//代码二:无闪烁的代码

void CWaveShowerView::OnTimer(UINT nIDEvent)

{

if(nIDEvent==0)

{

CDC* pDC=GetDC();

CDC dc;

CBitmap bitmap;

CBitmap* pOldBitmap;

CRect client;

pDC->GetClipBox(client); //检取无效区

//创建一个与pDC兼容的内存设备环境

if(dc.CreateCompatibleDC(pDC))

{

//创建一与pDC兼容的位图,大小为整个客户区

if(bitmap.CreateCompatibleBitmap(pDC,rect.Width(), rect.Height()))

{

//使dc与pDC具有同样的映射关系

OnPrepareDC(&dc,NULL);

//将位图选入内存环境

pOldBitmap=dc.SelectObject(&bitmap);

//使dc的整个客户区都成无效区

dc.SelectClipRgn(NULL);

//再“与上”检取的无效区,使内存环境与

//pDC检取的无效区相等

dc.IntersectClipRect(client);

}

}

CleanScreen();

for(int i=0;i

DrawPoint(value[i],RGB(0,255,0));

dc.SelectObject(pOldBitmap);

ReleaseDC(pDC);

}

if(nIDEvent==1)

{

m_dx+=2;

for(int i=0;i

{

value[i].x=i;

if(m_dx+i<0)

buffer[m_dx+i]=128;

if(m_dx+i<-m_DX)

m_dx-=2;

if(m_dx+i>m_BufLen)

buffer[m_dx+i]=128;

if(m_dx+i>m_BufLen+m_DX)

m_dx-=2;

value[i].y=m_DY*(buffer[m_dx+i]-128)/256;

}

}

CFormView::OnTimer(nIDEvent);

}

虽然通过上述几步可以实现所有的功能,但为了防止内存泄露和养成良好的编程习惯,我们还须做些工作,在视类的构造函数中释放我们曾经申请过的内存以及定时器:

CWaveShowerView::~CWaveShowerView()

{

delete[] value;

KillTimer(0);

KillTimer(1);

}

小结

编译运行此程序,通过菜单选取需要显示的文件(任意文件均可),如在定时器响应代码中采用的是第一种代码,则会看到数据显示的同时伴随着明显的闪烁而采用后一种代码编码则会很平稳的将数据显示出来。本文介绍的这种方法适用于各种牵扯到数组数据图形显示的程序,比如监控软件、数据分析软件、测量软件等等,具有广泛的应用前景。本文所述程度代码在Windows 2000 Professional + SP4下由Microsoft Visual C++ 6.0编译通过。

====================================================================== =======================

用双缓存技术,和重载OnEraseBKgnd(),

怎么重载OnEraseBkgnd()函数??- -

选择View->ClassWizard->classinfo选项

卡:message filter下拉框: 选择window,然后再选择

message maps选项卡,在messages下拉框应该可以找到

wm_erasebkgnd.双击添加.

====================================================================== ================================

如何实现双缓冲

首先给出实现的程序,然后再解释,同样是在OnDraw(CDC *pDC)中:

CDC MemDC; //首先定义一个显示设备对象

CBitmap MemBitmap;//定义一个位图对象

//随后建立与屏幕显示兼容的内存显示设备

MemDC.CreateCompatibleDC(NULL);

//这时还不能绘图,因为没有地方画 ^_^

//下面建立一个与屏幕显示兼容的位图,至于位图的大小嘛,可以用窗口的大小,也可以自己定义(如:有滚动条时就要大于当前窗口的大小,在BitBlt时决定拷贝内存的哪部分到屏幕上)

MemBitmap.CreateCompatibleBitmap(pDC,nWidth,nHeight);

//将位图选入到内存显示设备中

//只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上

CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap);

//先用背景色将位图清除干净,这里我用的是白色作为背景

//你也可以用自己应该用的颜色

MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255));

//绘图

MemDC.MoveTo(……);

MemDC.LineTo(……);

//将内存中的图拷贝到屏幕上进行显示

pDC->BitBlt(0,0,nWidth,nHeight,&MemDC,0,0,SRCCOPY);

//绘图完成后的清理

MemBitmap.DeleteObject();

MemDC.DeleteDC();

====================================================================== ============================

用vc 做程序,如何画图是一个大家都很关心,但是却感到很难以理解的问题,因为在mfc 的封装之下,没有现成的画图函数供你直接调用,像vb等等里面直接来个 point之类的,常常让人感到无从下手。这两天帮人解决了一个用内存缓冲画图的问题,顺便也就谈谈这些

东西,也算是总结。

我先来解释一下在mfc里面很关键的设备环境描述符,也就是所谓的DC(device context)。

还是从历史来看吧,dos时代,我们如果要绘图,必须通过一系列系统函数来启动图形环境(用过turbo pascal或者turbo c的人该还有印象吧),这之间对各种硬件的初始化参数都不相同,非常的烦人,常常还要查阅硬件手册,那时的程序智能针对最流行的硬件来编写,对不流行的就没有办法了。windows操作系统为了屏蔽不同的硬件环境,让编程时候不考虑具体的硬件差别,采取了一系列办法,设备环境描述符就是这样产生的。简单的说,设备描述符抽象了不同的硬件环境为标准环境,用户编写时使用的是这个虚拟的标准环境,而不是真实的硬件,与真实硬件打交道的工作一般交给了系统和驱动程序完成(这同样解释了为什么我们需要经常更新驱动程序的问题)。使用在windows图形系统(gdi,而不包括direct x)上面,就体现在一系列的图形DC上面,我们如果要在gdi上面绘图,就必须先得到图形DC的句柄(handle),然后指定句柄的基础上进行图形操作。

再来回忆一下,我们怎么在sdk的环境下面绘图呢,我想这个大家都不太清楚吧,但是确实很基础。在windows的sdk环境下面,我们用传统的c编写程序,在需要的绘图地方(比如响应WM_PAINT消息的分支)这样做:

hdc = GetDC( hwnd );

oldGdiObject = SelectObject( hdc,newGdiObject );

...绘图操作...

SelectObject( hdc,oldGdiObject );

DeleteObject( newGdiObject );

ReleaseDC( hdc);

或者是这样

BeginPaint( hwnd,&ps );//PAINTSTRUCT ps -- ps is a paint struct

...绘图操作...

EndPaint( hwnd )

这就是大概的过程,我们看到了hdc(图形DC句柄)的应用,在绘图的部分,每一个绘图函数基本上也要用到这个句柄,最后我们还必须释放它,否则将严重影响性能。每次我们都必须调用GetDC这个api函数得到(不能用全局变量保存结果重复使用,我在后面解释)。这些是最最基本的windows图形操作的方式,相比dos时代简单了些,但是有些概念也难理解了些。vb里面的简单的point函数其实最后也是被转化为这样的方式来执行,系统帮助做了很多事情。

到了mfc里面,由于有了封装,所有的hdc被隐藏在对象中做为隐藏参数传递(就是DC类的this啦~~),所以我们的关键话题就转变为了怎样得到想要的 DC类而已,这个过程其实大同小异的。在消息响应的过程中,WM_PAINT被转变为OnDraw(),OnPaint()一系列函数来响应,这些函数一般都有个参数CDC *pDC传入进来,因此在这些函数里面,我们就只需要直接画图就可以了,和以前sdk的方式一样。

但是WM_PAINT消息响应的频度太高了,比如最小化最大化,移动窗体,覆盖等等都引起重绘,经常的这样画图,很是消耗性能;在有些场合,比如随机作图的场合,每一次就改变,还导致了程序的无法实现。怎么解决后一种问题呢。

ms 在msdn的例子里面交给我们document/view的经典解决办法,将图形的数据存储在document类里面,view类只是根据这些数据绘图。比如你要画个圆,只是将圆心和半径存在document里面,view类根据这个里面的数据在屏幕上面重新绘制。那么,我们只需要随机产生一次数据就可以了。

这样还是存在性能的问题,于是我们开始考虑另外的解决方法。我们知道,将内存中的图片原样输出到屏幕是很快的,这也是我们在dos时代经常做的事情,能不能在windows也重新利用呢?答案就是内存缓冲绘图,我们今天的主题。

我们还是回到DC上来,既然DC是绘图对象,我们也就可以自己来在内存里面造一个,让它等于我们想要的图,图(CBitmap)可以存储在 document 类里面,每一次刷新屏幕都只是将这个图输出到屏幕上面,每一次作图都是在内存里面绘制,保存在document的图里面,必要时还可以将图输出到外存保存。这样既保证了速度,也解决了随机的问题,在复杂作图的情况下对内存的开销也不大(总是一副图片的大小)。这是一个很好的解决办法,现在让我们来实现它们。

我们在document类里面保存一个图片

CBitmap m_bmpBuf;//这里面保存了我们做的图,存在于内存中

在view类里面我们需要将这个图拷贝到屏幕上去

位于OnDraw(CDC *pDC)函数中:

CDC dcMem;//以下是输出位图的标准操作

CBitmap *pOldBitmap = NULL;

dcMem.CreateCompatibleDC(NULL);

pOldBitmap = dcMem.SelectObject(&pDoc->m_bmpBuf);

BITMAP bmpinfo;

pDoc->m_bmpBuf.GetBitmap(&bmpinfo);

pDC->BitBlt(0,0,bmpinfo.bmWidth,bmpinfo.bmHeight,&dcMem,0,0,SRCCOPY);

dcMem.SelectObject(pOldBitmap);

dcMem.DeleteDC();

在我们需要画图的函数里面,我们完成绘图工作

CBmpDrawDoc *pDoc = GetDocument(); //得到document中的bitmap对象

CDC *pDC = GetDC();

CDC dcMem;

dcMem.CreateCompatibleDC(NULL);//这里我们就在内存中虚拟建造了DC

pDoc->m_bmpBuf.DeleteObject();

pDoc->m_bmpBuf.CreateCompatibleBitmap(pDC,100,100);//依附DC创建bitmap

CBitmap *pOldBitmap = dcMem.SelectObject(&pDoc->m_bmpBuf);//我们调入了我们bitmap 目标

dcMem.FillSolidRect(0,0,100,100,RGB(255,255,255));//这些时绘图操作,随便你^_^ dcMem.TextOut(0,0,"Hello,world!");

dcMem.Rectangle(20,20,40,40);

dcMem.FillSolidRect(40,40,50,50,RGB(255,0,0));

pDC->BitBlt(0,0,100,100,&dcMem,0,0,SRCCOPY);//第一次拷贝到屏幕

dcMem.SelectObject(pOldBitmap);

dcMem.DeleteDC();

全部的过程就是这样,很简单吧。以此为例子还可以实现2个缓冲或者多个缓冲等等,视具体情况而定。当然在缓冲区还可以实现很多高级的图形操作,比如透明,合成等等,取决于具体的算法,需要对内存直接操作(其实就是当年dos怎么做,现在还怎么做)。

再来解释一下前面说的为什么不能用全局变量保存DC问题。其实DC也是用句柄来标识的,所以也具有句柄的不确定性,就是只能随用随取,不同时间两次取得的是不同的(使用过文件句柄地话,应该很容易理解的)。那么我们用全局变量保存的DC就没什么意义了,下次使用只是什么也画不出来。(这一点的理解可以这样: DC需要占用一定的内存,那么在频繁的页面调度中,位置难免改变,于是用来标志指针的句柄也就不同了)。

====================================================================== ===========

VC 双缓冲绘图

以下内容来自:https://www.360docs.net/doc/114198644.html,/VC/Problem/2008/12/1919491244642.shtml

在图形图象处理编程过程中,双缓冲是一种基本的技术。我们知道,如果窗体在响应WM_PAINT消息的时候要进行复杂的图形处理,那么窗体在重绘时由于过频的刷新而引起闪烁现象。解决这一问题的有效方法就是双缓冲技术。

因为窗体在刷新时,总要有一个擦除原来图象的过程OnEraseBkgnd,它利用背景色填充窗体绘图区,然后在调用新的绘图代码进行重绘,这样一擦一写造成了图象颜色的反差。当WM_PAINT的响应很频繁的时候,这种反差也就越发明显。于是我们就看到了闪烁现象。

我们会很自然的想到,避免背景色的填充是最直接的办法。但是那样的话,窗体上会变的一团糟。因为每次绘制图象的时候都没有将原来的图象清除,造成了图象的残留,于是窗体重绘时,画面往往会变的乱七八糟。所以单纯的禁止背景重绘是不够的。我们还要进行重新绘图,但要求速度很快,于是我们想到了使用 BitBlt函数。它可以支持图形块的复制,速度很快。我们可以先在内存中作图,然后用此函数将做好的图复制到前台,同时禁止背景刷新,这样就消除了闪烁。以上也就是双缓冲绘图的基本的思路。

一、普通方法:

先按普通做图的方法进行编程。即在视类的OnDraw函数中添加绘图代码。在此我们绘制若干同心圆,代码如下:

CBCDoc* pDoc = GetDocument();

ASSERT_V ALID(pDoc);

CPoint ptCenter;

CRect rect,ellipseRect;

GetClientRect(&rect);

ptCenter = rect.CenterPoint();

for(int i=20;i>0;i--)

{

ellipseRect.SetRect(ptCenter,ptCenter);

ellipseRect.InflateRect(i*10,i*10);

pDC->Ellipse(ellipseRect);

}

编译运行程序,尝试改变窗口大小,可以发现闪烁现象。

二、双缓冲方法:

在双缓冲方法中,首先要做的是屏蔽背景刷新。背景刷新其实是在响应WM_ERASEBKGND 消息。我们在视类中添加对这个消息的响应,可以看到缺省的代码如下:

BOOL CMYView::OnEraseBkgnd(CDC* pDC)

{

return CView::OnEraseBkgnd(pDC);

}

是调用父类的OnEraseBkgnd函数,我们屏蔽此调用,只须直接return TRUE;即可。

下面是内存缓冲作图的步骤。

CPoint ptCenter;

CRect rect,ellipseRect;

GetClientRect(&rect);

ptCenter = rect.CenterPoint();

CDC dcMem; //用于缓冲作图的内存DC

CBitmap bmp; //内存中承载临时图象的位图

dcMem.CreateCompatibleDC(pDC); //依附窗口DC创建兼容内存DC

bmp.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height());//创建兼容位图dcMem.SelectObject(&bmp); //将位图选择进内存DC

//按原来背景填充客户区,不然会是黑色

dcMem.FillSolidRect(rect,pDC->GetBkColor());

for(int i=20;i>0;i--) //在内存DC上做同样的同心圆图象

{

ellipseRect.SetRect(ptCenter,ptCenter);

ellipseRect.InflateRect(i*10,i*10);

dcMem.Ellipse(ellipseRect);

}

pDC->BitBlt(0,0,rect.Width(),rect.Height(),

&dcMem,0,0,SRCCOPY);//将内存DC上的图象拷贝到前台

dcMem.DeleteDC(); //删除DC

bm.DeleteObject(); //删除位图

由于复杂的画图操作转入后台,我们看到的是速度很快的复制操作,自然也就消除了闪烁现象。

注意:bmp.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height());

这里面CreateCompatibleBitmap第一个参数不能用dcMem,这样的话创建的是黑白位图。如果你要创建彩色位图,需要用pDC,它用来创建了内存DC. 详细请见下面的MSDN:When a memory device context is created, it initially has a 1-by-1 monochrome bitmap selected into it. If this memory device context is used in CreateCompatibleBitmap, the bitmap that is created is a monochrome bitmap. To create a color bitmap, use the hDC that was used to create the memory device context, as shown in the following code:

HDC memDC = CreateCompatibleDC ( hDC );

HBITMAP memBM = CreateCompatibleBitmap ( hDC, nWidth, nHeight );

SelectObject ( memDC, memBM );

mfc绘图程序上机步骤

首先生成MFC程序空框架 空框架中的每个类的职责是什么要自己理解清楚 第一次课: 1.定义自己的数据类CLine直线类,用于绘图 点击菜单:插入—类 选择Generic class,输入类名,每个类都是分成两个文件.h 中只有类体,包含类的数据成员定义和成员函数声明,.cpp中就是成员函数的类体外实现 classCLine { int x1,y1,x2,y2; public: CLine(int a=0,int b=0,int c=0,int d=0); virtual ~CLine(); int Getx1(); int Gety1(); int Getx2(); int Gety2(); void SetPoint1(intx,int y);很多函数是在后面使用中发现问题逐渐添加的 void SetPoint2(intx,int y); }; 2.自定义的类要作为数据成员出现在Doc类中 classCSmallCADDoc : public CDocument { protected: // create from serialization only CSmallCADDoc(); DECLARE_DYNCREATE(CSmallCADDoc) // Attributes public: CLine line1; //数据成员应该是私有,但是由于文档类与视图类交换频繁,为了访问方便,直接定义成公有了。你也可以定义成私有,再定义一个Get函数间接访问,取他的值3.注意:添加了这个数据成员后,需要增加几个#include “Line.h”语句 添加的原则是,每个cpp文件都单独编译,哪里用到新的类,相应的cpp前就要增加#include “。。。”语句 此处是在CSmallCADDoc类中添加了一个CLine line1;类对象,在SmallCADDoc.h文件中。所以,凡是包含了#include “SmallCADDoc.h”的地方都要在前面添加#include “Line.h” 语句。共3处:app doc view 这3个类的cpp文件前 4.注意:初始化 CLine line1; //数据成员是文档类的,它不像dos下程序,明确能看到定义,调用构造

mfc简单绘图程序

M F C 简 单 的 绘 图 程 序 ——王帅

目录 摘要 (2) 关键字 (2) 1 引言 (2) 设计目的 ............................................................................................................ 错误!未定义书签。 2 功能说明 (2) 2.1菜单栏....................................................................................................... 错误!未定义书签。 2.1.1图形 (3) 2.1.2画笔 (4) 2.1.3画硬币 (4) 2.2工具栏 (5) 2.3状态栏 (5) 3 功能的实现 (5) 3.1 视图设计 (5) 3.2 编辑资源 (6) 3.3 编程思路及各个函数的实现 (7) (1)思路 (7) (2)代码的实现 (7)

1.为基本形状创建一个基类 (8) 2.基本形状类的创建 (8) 2.1矩形类的创建及定义 (8) 2.2圆形类的创建及定义 (10) 2.3正五边形类的创建及定义 (11) 2.4正三角形类的创建及定义 (14) 2.5椭圆类的创建及定义 (16) 2.6正四边形类的创建及的定义 (17) 2.7正六边形类的创建及定义 (18) 2.8直线类的创建及定义 (19) 3.各基本形状类在CMyDraw2_0类中的调用和绘图的实现 (20) 3.1矩形类的调用与与绘图的实现 (20) 3.2圆形类的调用 (24) 3.3正三角形类的调用 (25) 3.4基本类型调用的剩余代码 (26) 4.画笔的使用、颜色及大小的调整 (29) 5.画硬币 (35) 6.工具栏中的自定义控件 (38) 7.状态栏中的显示 (39) 4程序功能的测试 (41) 5最后总结 (42)

简单的绘图程序实验报告

面向对象程序设计实训(基于MFC程序设计) 题目: 简单的绘图程序 院系专业: 姓名: 学号: 同组其他学生(学号):

简单绘图程序说明 1、功能分析 目前这个软件的主要功能有如下: 1、画直线:通过OnLine()函数实现。 2、画矩形:通过OnRectangle()函数实现。 3、画圆角矩形:通过OnRoundrect()函数实现。 4、画椭圆:通过OnEllipse()函数实现。 5、铅笔工具:可以画任意线条。通过直接在OnMouseMove(UINT nFlags, CPoint point)函数里面添加代码实现。 6、右键弹出菜单:可以在客户中点击鼠标右键,快速选择常用菜单。通过 OnContextMenu函数实现。 7、状态栏显示鼠标移动的坐标:在程序的右下角显示,通过调用setWidnowText 和GetParent()实现。 8、画图颜色选择:可以画任何颜色的线条,通过OnColor()函数实现。 9、线条类型及线宽的设置:可以将画出的线条设置成实线、虚线、点线、点划 线,双点划线,还可以设置线条的粗细,,通过实例这一功能立刻显示所选择线条的粗细及线型。通过新建CLineSettingDlg类,其中OnSelchangeLineStyle()函数实现线型的改变,OnChangeEditLineWidth()函数实现线宽的改变。再在CDrawView类中调用OnLineSetting()函数实现画笔的对话框, CLineSettingDlg类中的OnPaint()函数是实现示例功能的。 10、窗口的重绘时不擦除原来的内容:新建CShape类用来保存线条的颜色,线 宽,填充色等属性,在窗口大小发生变化时有OnDraw(CDC* pDC)函数进行重绘工作,重绘中调用了各个绘图函数的Draw(CDC *pDC)函数。 11、所绘图形的保存于读取,通过Serialize(CArchive &ar)函数进行序列化操作, 将所绘图形的信息通过文件的形式保存起来。

C++实验报告(MFC简单画图程序)

VC++程序设计实验报告 一、实验目的 掌握MFC编程 二、实验内容 用MFC向导创建单文档应用程序,一个简单的画图程序。 ?使用C++语言实现 ?使用VC++6.0集成开发环境开发 ?使用MFC应用程序开发框架 三、实验步骤 ?基本功能描述 1. 打开exe文件,在绘图下拉菜单中可分别设置绘制的图形形状,如直线、 曲线、矩形及椭圆,线宽选项,有1-6可供选择,线型选项有实线、虚线、点线和点段线供设置,还可以设置线色以及填充色,通过弹出的颜色对话框选择需要的颜色,如果不选择线宽、线色以及填充色,则按默认的画笔,画刷来绘制选择的图形。 2. 选择好图形后,通过鼠标可以绘制出相应的直线,矩形或椭圆,鼠标的按下确定图形的起点,鼠标的拖动则确定了图形的终点,即通过鼠标的拖动来决 定图形的大小,当鼠标弹起,此图形则绘制完毕。 ?设计思路 1. 对需要用到的变量进行初始化。 2. 选择相应的图形之后就响应相应的消息处理函数,给shape赋对应的值。选择不同的线宽,线色与填充色,即可改变画笔或画刷的属性。 3. 鼠标的按下响应函数OnLButtonDown(),捕捉鼠标当前位置得到起点的坐标,鼠标的拖动响函数OnMouseMove()改变终点的坐标,鼠标的弹起响应OnLButtonUp(),确定终点坐标,刷新,得到绘制图形。 4. 选择图形或其它属性,可进行下一次绘制。 ?软件设计 A 设计步骤 1.创建单文档 创建一个MFC AppWizard[exe]工程,命名为“Draw”,如图1所示,并创建单文档,如图2所示。创建成功后,系统自动生成相应的类,如图3所示。

图1 创建工程 图2 创建单文档

mfc简单绘图程序报告

简单绘图程序 1 需求说明 1.1 问题描述 设计一个简单的绘图应用程序,可以绘制图形或自由绘制线段,可以更改颜色、画笔粗细、保存文件。 1.2功能说明 1.图形绘制功能:直线、椭圆、矩形。 在菜单栏中选择需要的图形(也可以通过工具栏中选择)用鼠标便能在视图中绘出相应的图形。 2.可以绘制自由线段 3.对图形的操作:能通过菜单栏弹出对话框选择线宽、自定义颜色,也可以擦除绘制的线段。 4.可以保存绘图文件,保存后打开可以继续绘制。 2.1.3 数据说明 在程序运行以后,当用户单击某个菜单项时,应该把用户的选择保存起来,以便随后的绘图操作使用。在CDzyView类中添加一个私有变量xz;用来保存用户的选择(直线、矩形、椭圆、自由绘图)在绘制时都可有两点来确定其图形。当鼠标左击时得到一个点,当鼠标停止移动时得到另外一个点。为视图类CDzyView分别捕获鼠标左键按下和弹起这两个消息。当鼠标左键按下时,需要将鼠标当前按下点保存至sx、sy,在鼠标移动停止后,将当前坐标保存至ex,ey.

其他主要数据说明: int fd 判断是否为自由绘图模式int w 线宽 int R RGB中的R int G RGB中的G int B RGB中的B CGraph类中 int m_nType:保存后重绘时用户的选择int qdx; 重绘起点x int qdy; 重绘起点y int zdx; 重绘终点x int zdy; 重绘终点y CLine类中 int cx; 重绘线宽 int sx; 重绘起点x int sy; 重绘起点y int zx; 重绘终点x int zy; 重绘终点y int w; 重绘线宽 自定义颜色对话框关联变量: IDC_EDIT_RED int m_Red IDC_EDIT_GREEN int m_Green IDC_EDIT_BLUE int m_Blue IDC_SCROLL_RED CScrollBar m_Sred IDC_SLIDER_GREEN CSliderCtrl m_Sgreen IDC_SPIN_BLUE CSpinButtonCtrl m_Sblue 2 分析、设计与实现 2.1 主要功能设计与实现

MFC实现简单画图形程序(学习相关)

《MFC编程及应用》课程设计报告题目:简单画图形程序 学号:姓名: 指导老师: 时间: 评语:

程序设计步骤: 一、建立基于对话框的应用程序框架; 二、CMy0910200155Dlg类中关键新增变量的作用: CPtrArray pta; //用于保存已绘图形的相关信息。 CMemoryNode *pmN; //指向CMemoryNode类的指针,程序运行过 程中动态保存对象信息。 CMemoryNode *pmn; //指向CMemoryNode类的指针,从文件中读 取信息时动态创建类的对象。 COLORREF m_CurrentBrushColor; //用于存放当前画刷的颜色。 COLORREF m_CurrentPenColor; //用于存放当前画笔的颜色。 int num; //用于存放从"Index.txt"文件中读取的数字。 int flag=0; //用于标识:当为1时,表示按下了”画图”按钮;当为2时, 表示按下了”撤消”按钮;当为3时,表示按下了”加载历史” 按钮,则从文件中读取信息。 int mark; //用于标识:当为0时,表示刚画过矩形;当为1时,表示 刚画过圆角矩形;当为2时,表示刚画过椭圆。 int index; //用于存放pta数组的容量。 int flag1=0; //用于标识,和flag搭配,用来处理多种情况下的窗口重绘 问题。 int ButtonState=0; //用于标识,是类CShow和类CMy0910200155Dlg 的一个接口,通过其值在1和0之间转换,来处理弹出式对 话框的初次绘制和移动时的重绘问题。

c面向对象程序设计MFC简单计算器实验报告

计算机与信息工程学院 《程序设计基础》课程设计报告 题目名称:60.编写一个能实现简单功能的计算器学生姓名:刘沛东 学生学号:2011508154 专业班级:电子信息工程(1)班 指导教师:高攀

1 课程设计的题目 编写一个能实现简单功能的计算器 2 题目要求 1. 有一个计算器图形。 2. 能实现加、减、乘、除及乘方的运算。 3. 当输入题目时,屏幕上要在指定位置上显示出相应的题目内容,且相应的数字键要改变颜色 例如:输入数字1 时,在计算器图形上的1键变为红色。 4. 屏幕、图形颜色、形状自定 3 总体设计 3.1 总体框架 图1 系统框架

3.2 系统功能说明 在VC++6.0中绘制计算器界面,各控件的设置 对0~9控件设定相应的ID和其他属性: 图2 “1”控件设置 对“+、-、*、\”控件设定相应的ID和其他属性: 图2 “+”控件设置 对其它控件设定相应的ID和其他属性: 图3 其它控件设置

主要使用到Layout菜单中的Align功能对各个按钮进行对其,使界面更加整洁。拖出的控件有上面的一个Edit控件用于显示数字,Button控件用于处理鼠标的消息。 4 程序详细设计 4.1系统主调模块 图5 程序流程图

4.2各模块详细设计 4.2.1 建立的变量,控件的命名,对应的消息处理函数对应表 double poz; //保存小数点的位置,初始化为1,表示poz-1个小数点。 double m_Dis; //Edit控件上需要显示的数字 BOOL point_flag; //小数点表示位,判定是否是小数,是小数为1,不是小数为0。 double numfirst; //保存计算过程中的前一个数字, double numsecond;//保存计算过程中的第二个数字 char op;//记录当前的计算符号,可以为’+’,’-’,’*’,’/’,’=’,’c’,’n’ 变量初始化: poz=1; m_Dis = 0.0; numfirst=0; numsecond=0; op=0;

MFC经典绘图方法总结

MFC经典绘图方法总结 Windows 绘图的实质就是利用windows提供的图形设备接口GDI(Graphics Device Interface)将图形会制在显示器上。 为了支持GDI绘图,MFC提供了两种重要的类: 设备上下文DC(Device Context)类,用于设置绘图属性和绘制图形; 绘图对象类,封装了各种GDI绘图对象,包括画笔、刷子、字体、位图、调色板和区域。 CDC类介绍: 在MFC中,CDC是设备上下文类的基类,派生类包括:CClientDC, CPaintDC, CWindowDC, CMetaFileDC类 CClientDC 客户区设备上下文,绘制客户区时 CPaintDC 一般发生在窗口需要重绘时 CWindwDC 可以绘制整个窗口,通常在窗口WM_NCPAINT消息的响应函数CWnd::OnNCPaint()中使用 CMetaFileDC 专门用于图元文件的绘制,图元文件记录一组GDI命令,重建图形输出。 CDC包含m_hDC和m_hAttribDC二个设备上下文。CDC指导所有对m_hDC的输出GDI调用(SetTextColor)以及对m_hAttribDC的大部分属性GDI调用(GetTextColor)。 CDC对象的重要函数如下: 1、为指定设备创建上下文virtual BOOL CreateDC(...) 比如创建一个为屏幕的设备上下文 CDC dc; dc.CreateDC("DISPLAY", NULL, NULL,NULL); 2、创建内存设备上下文,与指定设备上下文兼容virtual BOOL CreateCompatibleDC( CDC * pDC) 3、删除CDC对象对应的Windows设备上下文, 通常不调用该函数而是使用析构程序virtual BOOL DeleteDC(); 4、将HDC句柄转化为设备上下文的句柄: CDC *pDC=CDC::FromHandle( hDC ) 5、选择GDI对象入选到设备上下文中,一共有五种形式: CPen * SelectObject( CPen * pPen) ; CBrush * SelectObject( CBrush * pBrush) ; virtual CFont * SelectObject( CFont * pFont) ; CBitmap * SelectObject( CBitmap * pBitmap) ; int SelectObject( CRgn * pRgn) ;

简易画图板设计报告

设计报告:Graphic简易画 图板 -----韩伟谢程焜肖越周峰 电科二班

1设计目的 设计一个单文档类型的MFC AppWizard (exe)工程,工程取名为:Graphic。此程序将实现简单的绘图功能,包括点、直线、矩形、椭圆、扇形和连续线的绘制。并且能实现绘图的控制,包括线宽、线型和颜色的设置,图形的保存和打开以及笔刷的使用。 2 总体设计 设计图如图6

图6 3详细设计 首先,新建一个单文档类型的MFC AppWizard (exe)工程,工程取名为:Graphic。为此程序添加一个子菜单,菜单名称为“绘图”,并为其添加六个菜单项,分别用来控制不同图形的绘制。当用户选择其中的一个菜单项后,程序将按照当前的选择进行相应图形的绘制。添加的六个菜单项的ID及名称如表1所示。然后分别为这六个菜单项添加命令响应,本程序让视类(CGraphicView)对这些菜单命令进行响应,这六个响应函数的名称分别如表1所示。

在程序运行以后,当用户单击某个菜单项时,应该把用户的选择保存起来,以便随后的绘图操作使用。因此在CGraphicView类中添加一个私有变量m_nDrawType;用来保存用户的选择,该变量的定义如下所述: private: UINT m_nDrawType; 接着,在视类的构造函数中将此变量初始化为0,程序代码如下: CGraphicView::CGraphicView() { // TODO: add construction code here m_nDrawType=0; } 利用switch/case语句,来分别完成相应图形的绘制。当用户选择【绘图】菜单下的不同子菜单项时,将变量m_nDrawType设置为不同的值。程序代码如下:void CGraphicView::OnDot() { // TODO: Add your command handler code here m_nDrawType=1; } void CGraphicView::OnLine() { // TODO: Add your command handler code here m_nDrawType=2; } void CGraphicView::OnRectangle() { // TODO: Add your command handler code here m_nDrawType=3; } void CGraphicView::OnEllipse() { // TODO: Add your command handler code here m_nDrawType=4;

基于MFC的OpenGL绘图

基于MFC的OpenGL绘图 本文更新版本请点击 [置顶]《基于MFC的OpenGL编程》系列文章 一、简介 GDI是通过设备句柄(Device Context以下简称"DC")来绘图,而OpenGL则需要绘制环境(Rendering Context,以下简称"RC")。每一个GDI命令需要传给它一个DC,但与GDI不同,OpenGL使用当前绘制环境(RC)。一旦在一个线程中指定了一个当前RC,在此线程中其后所有的OpenGL命令都使用相同的当前RC。虽然在单一窗口中可以使用多个RC,但在单一线程中只有一个当前RC。下面我将首先产生一个OpenGL RC并使之成为当前RC,这将分为三个步骤:设置窗口像素格式;产生RC;设置为当前RC。 二、MFC中的OpenGL基本框架 1、首先创建工程 用AppWizard产生一个MFC EXE项目,其他默认即可。 2、将此工程所需的OpenGL文件和库加入到工程中 在工程菜单中,选择"Build"下的"Settings"项。单击"Link"标签,选择"General"目录,在Object/Library Modules的编辑框中输入"opengl32.lib glu32.lib glut.lib glaux.lib"(注意,输入双引号中的内容,各个库用空格分开;否则会出现链接错误),选择"OK"结束。然后打开文件"stdafx.h",加入下列头文件: #include #include 3、改写OnPreCreate函数并给视图类添加成员函数和成员变量

OpenGL需要窗口加上WS_CLIPCHILDREN(创建父窗口使用的Windows风格,用于重绘时裁剪子窗口所覆盖的区域)和WS_CLIPSIBLINGS(创建子窗口使用的Windows风格,用于重绘时剪裁其他子窗口所覆盖的区域)风格。把OnPreCreate改写成如下所示:BOOL COpenGLDemoView::PreCreateWindow(CREATESTRUCT& cs) { // TODO: Modify the Window class or styles here by modifying // the CREATESTRUCT cs cs.style |= (WS_CLIPCHILDREN | WS_CLIPSIBLINGS); return CView::PreCreateWindow(cs); } 产生一个RC的第一步是定义窗口的像素格式。像素格式决定窗口着所显示的图形在内存中是如何表示的。由像素格式控制的参数包括:颜色深度、缓冲模式和所支持的绘画接口。在下面将有对这些参数的设置。我们先在COpenGLDemoView的类中添加一个保护型的成员函数BOOL SetWindowPixelFormat(HDC hDC)(用鼠标右键添加)和保护型的成员变量:int m_GLPixelIndex;并编辑其中的代码如下: BOOL COpenGLDemoView::SetWindowPixelFormat(HDC hDC) {//定义窗口的像素格式 PIXELFORMATDESCRIPTOR pixelDesc= { sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL| PFD_DOUBLEBUFFER|PFD_SUPPORT_GDI, PFD_TYPE_RGBA, 24, 0,0,0,0,0,0, 0, 0,

MFC下基于OpenGL绘图程序的开发步骤和框架

宁夏师范学院数学与计算机科学学院《计算机图形学》实验报告

pixelDesc.iPixelType = PFD_TYPE_RGBA; https://www.360docs.net/doc/114198644.html,olorBits = 32; pixelDesc.cRedBits = 8; pixelDesc.cRedShift = 16; pixelDesc.cGreenBits = 8; pixelDesc.cGreenShift = 8; pixelDesc.cBlueBits = 8; pixelDesc.cBlueShift = 0; pixelDesc.cAlphaBits = 0; pixelDesc.cAlphaShift = 0; pixelDesc.cAccumBits = 64; pixelDesc.cAccumRedBits = 16; pixelDesc.cAccumGreenBits = 16; pixelDesc.cAccumBlueBits = 16; pixelDesc.cAccumAlphaBits = 0; pixelDesc.cDepthBits = 32; pixelDesc.cStencilBits = 8; pixelDesc.cAuxBuffers = 0; pixelDesc.iLayerType = PFD_MAIN_PLANE; pixelDesc.bReserved = 0; pixelDesc.dwLayerMask = 0; pixelDesc.dwVisibleMask = 0; pixelDesc.dwDamageMask = 0; m_GLPixelIndex = ChoosePixelFormat( hDC, &pixelDesc); if (m_GLPixelIndex==0) // Let's choose a default index. { m_GLPixelIndex = 1; if (DescribePixelFormat(hDC, m_GLPixelIndex, sizeof(PIXELFORMA TDESCRIPTOR), &pixelDesc) == 0 ) { return FALSE; } } if (SetPixelFormat( hDC, m_GLPixelIndex, &pixelDesc) == FALSE) { return FALSE; } return TRUE; } 其中,m_GLPixelIndex为在视类中添加的一私有型的成员变量:

基于MFC的简单画图程序实验

简单画图程序 【实验目的】 本实验目的是通过构建基于MFC的windows画图程序,使学生: (1) 理解MFC应用程序的运行机制 (2) 掌握使用MFC构建Windows应用程序的基本结构及编程的基本方法 (3) 理解和掌握MFC应用程序消息处理机制及应用 (4) 掌握类向导(ClassWizard)的使用 【实验要求】 (1) 必须做好实验原理的预习。 (2) 需要对提供的程序代码进行分析,并明确实验时还应在何处添加哪些语句。 【实验环境】 Microsoft Windows XP Microsoft Visual C++ 6.0 1 基本功能描述 1) 在单文档菜单中,在菜单行中可插入一个菜单项,命名为绘图,在下拉菜单中可分别设置绘制的图形形状,如直线、矩形及椭圆,线宽选项,有1-5可供选择,还可以设置线色以及填充色,通过弹出的颜色对话框选择需要的颜色,如果不选择线宽、线色以及填充色,则按默认的画笔,画刷来绘制选择的图形。 2) 选择好图形后,通过鼠标可以绘制出相应的直线,矩形或椭圆,鼠标的按下确定图形的起点,鼠标的拖动则确定了图形的终点,即通过鼠标的拖动来决定图形的大小,当鼠标弹起,此图形则绘制完毕。 3) 增添工具栏,设置绘制的图形形状,线色以及填充色,可更方便地选择相应的功能。 2 设计思路 1) 对需要用到的变量进行初始化。 2) 选择相应的图形之后就响应相应的消息处理函数,给shape赋对应的值。选择不同

3) 鼠标的按下响应函数OnLButtonDown(),捕捉鼠标当前位置得到起点的坐标,鼠标的拖动响函数OnMouseMove()改变终点的坐标,鼠标的弹起响应OnLButtonUp(),确定终点坐标,刷新,得到绘制图形。 4) 选择图形或其它属性,可进行下一次绘制。

c++实验报告mfc简单画图程序).doc

V C++程序设计实验报告 一、实验目的 掌握MFC编程 二、实验内容 用MFC向导创建单文档应用程序,一个简单的画图程序。 ?使用C++语言实现 ?使用VC++6.0集成开发环境开发 ?使用MFC应用程序开发框架 三、实验步骤 ?基本功能描述 1. 打开exe文件,在绘图下拉菜单中可分别设置绘制的图形形状,如直线、曲线、矩形及椭圆,线宽选项,有1-6可供选择,线型选项有实线、虚线、点线和点段线供设置,还可以设置线色以及填充色,通过弹出的颜色对话框选择需要的颜色,如果不选择线宽、线色以及填充色,则按默认的画笔,画刷来绘制选择的图形。 2. 选择好图形后,通过鼠标可以绘制出相应的直线,矩形或椭圆,鼠标的按下确定图形的起点,鼠标的拖动则确定了图形的终点,即通过鼠标的拖动来决定图形的大小,当鼠标弹起,此图形 则绘制完毕。 ?设计思路 1. 对需要用到的变量进行初始化。 2. 选择相应的图形之后就响应相应的消息处理函数,给shape赋对应的值。选择不同的线宽,线色与填充色,即可改变画笔或画刷的属性。 3. 鼠标的按下响应函数OnLButtonDown(),捕捉鼠标当前位置得到起点的坐标,鼠标的拖动响函数OnMouseMove()改变终点的坐标,鼠标的弹起响应OnLButtonUp(),确定终点坐标,刷新,得到绘制图形。 4. 选择图形或其它属性,可进行下一次绘制。 ?软件设计 A 设计步骤 1.创建单文档 创建一个MFC AppWizard[exe]工程,命名为“Draw”,如图1所示,并创建单文档,如图2所示。创建成功后,系统自动生成相应的类,如图3所示。

图1 创建工程 图2 创建单文档

画图程序设计报告(MFC)

Windows程序设计课程设计报告 班级:计本08-1班 姓名:X X 学号:XXXXXXXXX 指导老师: 2010-11-29

目录 1 引言 (3) 1.1 课题背景 (3) 1.2 课程设计目的 (3) 2 功能说明 (3) 3 系统的实现 (4) 3.1 视图设计 (4) 3.2 编辑资源 (4) 3.3 编程思路及各个函数的实现 (5) 4程序功能的测试 (7) 4.1测试的研究与选择 (7) 4.2 测试环境 (7) 5.3 实例测试结果 (7) 5学习的总结 (9) 6参考文献 (10)

摘要:本课程设计实现的是画椭圆、矩形、直线和随手画功能等。这个画图小程序实现了简单的画图功能,具有简洁大方的图文外观。它的设计按软件工程的方法进行,系统具有良好的界面、必要的交互信息和简易的菜单进行操作。即时准确地获得需要的画图的工具,适合休闲娱乐和简单的画画,对人们的生活有一定的帮助。在课程设计中,系统开发平台为WindowsXP,程序设计设计语言采用Visual C++,在程序设计中,采用了结构化与面向对象两种解决问题的方法。 关键词:程序设计;画图;MFC;画图函数Ellipse()Restangle() 1 引言 1.1 课题背景 本课程设计主要在生活中的娱乐,可以在本程序上进行简单的绘图娱乐。 画图是日常生活中有效的工具,能实现简单的画图。本程序是在VC6.0中使用AppWizard创建基于对话框的应用程序。 1.2 课程设计目的 画图的实现,通过C++语句实现。在视图中实现画线功能,更好的了解MFC、C++等面向对象语言的设计和实现,运用自己所学的一些知识来实现其运用到现实的实践中,从而做到学而有用。 2 功能说明 本程序可以实现的功能如下: 1.运行程序后单击“编辑”菜单,选择“画矩形”后,用鼠标就可以在视图中画出一个矩形。 2.选择“画椭圆”,用鼠标就可以在视图中画出一个椭圆。 3.选择“画直线”,用鼠标就可以在视图中画出一条直线。 4.选择“随手画”,用鼠标就可以在试图中随手画画。 5.选择“TOP”就可以让画图窗口一直保持在其他窗口前面。 6.窗口右下角显示系统的当前时间。 7.将项目的默认图标换成自己设计的图标。

MFC画笔程序实验报告

功能说明 1、在画图之前,点菜单【画笔】,设置画笔之后才能绘图 图1、画笔设置 2、左侧工具条依次对应直线、自由画线、实体矩形、实体圆形、实体椭圆、选中区域、擦除功能 图2、图形绘制及编辑 3、选中区域后删除,点击菜单【编辑\删除选定区域】,即可完成删除功能 4、颜色设置,点击菜单【颜色】,弹出颜色对话框,选中颜色

图3、颜色设置 5、背景设置,点击菜单【背景设置】,弹出对话框,选择“是”加载默认位图 资源 图4、背景设置 6、点击保存按钮,生成图片保存到工程文件的文件夹中 问题分析 建立MFC单文档工程文件:Draw 一、画图程序支持画直线、自由连线(随鼠标移动连线)、实体圆形、实体矩形、实体椭圆。 在主窗口中添加工具条,工具条上有直线、自由连线、实体圆形、实体矩形、实体椭圆的的图标,命名为IDR_DRAWFRAME,在CmainFrame的OnCreat函数中将工具条加载上去,函数名为:LoadToolBar 二、支持区域选中(通过鼠标拖拽方框选中),并删除选中区域内所画的形状。 鼠标拖拽方框选中:可以设计绘制为背景为透明色的矩形 删除选中区域即将选中矩形上的图像去掉,变为系统白色区域,即:设计矩形填充为白色 三、支持各种笔形画图时的前景色和背景色

前景色:在菜单中添加颜色菜单,单击颜色菜单,弹出颜色选择框,选择颜色对画笔的颜色进行设置。 背景色:在菜单栏中添加背景设置按钮,选择背景设置,则默认设置图画位图背景为选定的文件 四、支持图擦功能,选中图擦时,随着图擦的移动,根据图擦的大小,将图擦经过的区域中的形状相关部分清除。 在工具条上添加擦出按钮,随着鼠标的移动,鼠标经过的区域图形消失,采用InvalidateRect函数、 五、支持打印、打印预览功能,并保持图片所见即所得。 在视图窗口绘制图形是在逻辑坐标系中展示的,而打印设备对应的是物理坐标系。为了实习所见即所得,需要继续逻辑坐标系到物理坐标系的转换。 六、支持将所画的内容保存为文件,格式为BMP格式。 在菜单【文件】中有“保存按钮”,点击此按钮,将所绘图形保存为.bmp文件。MFC没有提供文件的保存函数,需要自己设计算法,以完成.bmp文件的保存。首先当前显示的屏幕位图拷贝到BITMAP中,然后将BITMAP保存到内存中。 方案设计 一、画图程序支持画直线、自由连线(随鼠标移动连线)、实体圆形、实体矩形、实体椭圆。 实现步骤: (1)在主窗口中添加工具条,工具条上有直线、自由连线、实体圆形、实体矩形、实体椭圆的的图标,命名为IDR_DRAWFRAME,在CmainFrame 的OnCreat函数中将工具条加载上去,函数名为:LoadToolBar (2)直线图标ID号为:ID_SLINE;自由连线图标ID号为:ID_CLINE; 实体圆形ID号为:ID_ROUND 实体椭圆图标ID号为:ID_ELLIPSE 实体矩形ID号为:ID_RECTANGL (3)在CDrawView类中添加m_nDrawType变量控制画图类型,添加m-ptStart变量,保存鼠标起始点。为以上ID号添加COMMAND函数, 分别为:OnSline() 、OnCline() 、OnRound()、OnRectangle() 、OnEllipse()。 函数中控制m_nDrawType的值,以控制图形类型 (4)在CDrawView类中添加成员函数OnLButtonDown、OnLButtonUp和OnMouseMove,OnLButtonUp中完成直线、实体圆形、实体矩形、实 体椭圆的绘制;OnMouseMove中完成自由连线的绘制。 (5)为保证视图窗口刷新时,图形能够再现,添加类CGraph,此类中包含变量nFlags(控制OnMouseMove函数)、m_nDrawType(绘图类型)

MFC实验报告

实验报告 实验日期: 2013 年 05 月 18 日 一、实验目的 1、熟悉Visual C++的基本操作。 2、基本了解基于对话框的windows应用程序的编写过程。 3、对于Windows Socket编程建立初步概念。 二、实验要求 1、应用Visual C++中MFC CSocket类,实现网络数据传输。 2、仿照本实验步骤,制作实用的局域网一对一聊天程序。 三、实验原理

设置加入连接的数目,通过更改IP地址和端口号,成为不同的客户端,与服务器端连接,实现用户间的聊天功能。 1.程序整体框架:主程序监听一端口,等待客户接入;同时构造一个线程类,准备接管会话。当一个Socket会话产生后,将这个会话交给线程处理,然后主程序继续监听。 2.客户端(Client) 客户端,使用Socket对网络上某一个服务器的某一个端口发出连接请求,一旦连接成功,打开会话;会话完成后,关闭Socket。客户端不需要指定打开的端口,通常临时的、动态的分配一个端口。 3.服务器端(Server) 服务器端,使用ServerSocket监听指定的端口,端口可以随意指定(由于1024以下的端口通常属于保留端口,在一些操作系统中不可以随意使用,所以建议使用大于1024的端口),等待客户连接请求,客户连接后,会话产生;在完成会话后,关闭连接。 4.用户图形界面 用户图形界面方便程序与用户的交互,多个用户参加,完成会话功能,具体的设计要方便用户的使用,直观清晰,简洁明了,友好美观。 四、实验内容 一个最简单的点对点聊天程序 客户机/服务器模式是socket点对点网络程序典型的模式,以下这个实验就是实现一个简单的点对点通信的聊天程序。它用到的方法也是面向连接TCP连接的套接字MFC典型方式。其工作过程是:服务器首先启动,创建套接字后等待客户的连接;客户启动以后,创建套接字,然后和服务器建立连接;连接建立后,客户机和服务器可以通过建立的套接字连接进行信息通信。

MFC02_Visual Studio C++ MFC简单绘图程序

Visual studio C++ MFC 简单绘图程序-绘制国际象棋棋盘 功能:由Button将绘制的棋盘显示于Picture Control中 一、建立一个MFC功程序: 1.启动Visual Studio C++; 2.新建一个MFC对话框项目:New|Project|MFCAplication; https://www.360docs.net/doc/114198644.html,中输入mfc_pic,然后点|OK; 4.在Welcome to the MFC Application窗口:点next; 5.在Application Type窗口:点dialoge base;然后点Finish;出现如图所示的窗口; 6.删除窗口中的TODO…文本对象; 二、向窗口添加一个Picture Control和一个Button共二个对象: 1.修改Button的Caption属性为“绘制棋盘”,ID属性为IDC_BUTTON_DRAR; 2.修改Picture Control的ID属性为IDC_PIC,建立关联变量名为picD; 3.双击Button,添加如下代码: voidCmfcpicDlg::OnBnClickedButton1() { CRect r; intw,h,dxy,i,j; CWnd *pic = GetDlgItem(IDC_PIC); //取Picture的长宽信息 GetDlgItem(IDC_PIC)->GetWindowRect(&r); w=r.Width(); h=r.Height(); dxy=(wGetDC(); pdc->Rectangle(r); CBrushmyBrush; pdc->Rectangle(0,0,8*dxy,8*dxy); //绘制外框线 myBrush.CreateSolidBrush(RGB(0,0,0)); for(i=0;i<8;i++){ for(j=i%2;j<8;j+=2){ r.left=i*dxy; r.top=j*dxy; r.right=(i+1)*dxy; r.bottom=(j+1)*dxy;

c++简单画图程序

目录 1. 基本功能描述 (1) 2. 设计思路 (1) 3. 软件设计 (4) 设计步骤 (4) 界面设计 (6) 关键功能的实现 (7) 4. 结论与心得体会 (8) 5. 思考题 (8) 6. 附录 (10) 调试报告 (10) 测试结果 (10) 关键源代码 (10)

简单画图程序 1. 基本功能描述 简单画图程序实现了常见图形的绘制、图形属性的设置和图形数据的暂存等功能。该程序的具体功能模块包括以下几项: 1) 图形绘制模块。该模块实现直线段、椭圆、矩形等图形的绘制功能。在鼠标移动的过程中能实时显示当前绘制的图形。 2) 图形属性设置模块。该模块实现绘图线条的线宽,线色,图形填充色等属性设置功能。 3) 图形数据暂存模块。该模块实现直线段、椭圆、矩形等图形数据暂存功能,涉及图形的坐标、线宽、线色、填充色等数据。 2. 设计思路 1) 对需要用到的变量进行初始化。 2) 选择相应的图形之后就响应相应的消息处理函数,给shape赋对应的值。选择不同的线宽,线色与填充色,即可改变画笔或画刷的属性。 3) 鼠标的按下响应函数OnLButtonDown(),捕捉鼠标当前位置得到起点的坐标,鼠标的拖动响函数OnMouseMove()改变终点的坐标,鼠标的弹起响应OnLButtonUp(),确定终点坐标,刷新,得到绘制图形。 4) 选择图形或其它属性,可进行下一次绘制。 5) 程序的流程图如下:

图1 程序流程图 3. 软件设计 设计步骤 1) 创建单文档 创建一个MFC AppWizard[exe]工程,命名为“LiYuJing”,如图1所示,并创建单文

简单画图程序课程设计报告

目录 1 基本功能描述----------------------------------------------------------2 2 设计思路----------------------------------------------------------2 3 软件设计----------------------------------------------------------4 3.1 设计步骤----------------------------------------------------------4 3.2 界面设计----------------------------------------------------------8 3.3 关键功能实----------------------------------------------------------9 4. 附录----------------------------------------------------------9 4.1 调试报告----------------------------------------------------------9 4.2 测试结果----------------------------------------------------------10 4.3关键源代码---------------------------------------------------------12 5 结论与心得体会----------------------------------------------------------11 6. 参考文献----------------------------------------------------------11 7. 思考题----------------------------------------------------------11

相关文档
最新文档