MFC绘图程序出现的错误!!!什么原因?

MFC绘图程序出现的错误!!!什么原因?,第1张

你把点击那个工具栏按钮时触发的函数代码贴出来看下,这种断言没有半点提示的完全看不懂

CGdiObject* PASCAL CGdiObject::FromHandle(HGDIOBJ h)

{

CHandleMap* pMap = afxMapHGDIOBJ(TRUE)//create map if not exist

ASSERT(pMap != NULL)

CGdiObject* pObject = (CGdiObject*)pMap->FromHandle(h)

ASSERT(pObject == NULL || pObject->m_hObject == h)

return pObject

}某个GDI对象已经生成了

_AFXWIN_INLINE int CFont::GetLogFont(LOGFONT* pLogFont)

{ ASSERT(m_hObject != NULL)

return ::GetObject(m_hObject, sizeof(LOGFONT), pLogFont)}是不是这句话,CFont类的某个对象有问题

同样的,我们可以编写其他菜单项的处理函数代码,其代码如下:

//PS_DASH菜单项处理函数

void CDrawTestView::OnPenDash()

{

// TODO: Add your command handler code here

m_PenStyle = PS_DASH

this->SetMenuItemCheck(0,0,7,1)

}

//PS_DOT菜单项处理函数

void CDrawTestView::OnPenDot()

{

// TODO: Add your command handler code here

m_PenStyle = PS_DOT

this->SetMenuItemCheck(0,0,7,2)

}

//PS_DASHDOT菜单项处理函数

void CDrawTestView::OnPenDashdot()

{

// TODO: Add your command handler code here

m_PenStyle = PS_DASHDOT

this->SetMenuItemCheck(0,0,7,3)

}

//PS_DASHDOTDOT菜单项处理函数

void CDrawTestView::OnPenDashdotdot()

{

// TODO: Add your command handler code here

m_PenStyle = PS_DASHDOTDOT

this->SetMenuItemCheck(0,0,7,4)

}

//PS_NULL菜单项处理函数

void CDrawTestView::OnPenNull()

{

// TODO: Add your command handler code here

m_PenStyle = PS_NULL

this->SetMenuItemCheck(0,0,7,5)

}

//PS_INSIDEFRAME菜单项处理函数

void CDrawTestView::OnPenInsideframe()

{

// TODO: Add your command handler code here

m_PenStyle = PS_INSIDEFRAME

this->SetMenuItemCheck(0,0,7,6)

}

//宽度1菜单项处理函数

void CDrawTestView::OnPen1()

{

// TODO: Add your command handler code here

m_PenWidth = 1

this->SetMenuItemCheck(0,1,4,0)

}

//宽度3菜单项处理函数

void CDrawTestView::OnPen3()

{

// TODO: Add your command handler code here

m_PenWidth = 3

this->SetMenuItemCheck(0,1,4,1)

}

//宽度5菜单项处理函数

void CDrawTestView::OnPen5()

{

// TODO: Add your command handler code here

m_PenWidth = 5

this->SetMenuItemCheck(0,1,4,2)

}

//宽度7菜单项处理函数

void CDrawTestView::OnPen7()

{

// TODO: Add your command handler code here

m_PenWidth = 7

this->SetMenuItemCheck(0,1,4,3)

}

//画笔颜色下黑色菜单项处理函数

void CDrawTestView::OnPenBlack()

{

// TODO: Add your command handler code here

m_PenColor = RGB(0,0,0)

this->SetMenuItemCheck(0,2,4,0)

}

//画笔颜色下红色菜单项处理函数

void CDrawTestView::OnPenRed()

{

// TODO: Add your command handler code here

m_PenColor = RGB(255,0,0)

this->SetMenuItemCheck(0,2,4,1)

}

//画笔颜色下绿色菜单项处理函数

void CDrawTestView::OnPenGreen()

{

// TODO: Add your command handler code here

m_PenColor = RGB(0,255,0)

this->SetMenuItemCheck(0,2,4,2)

}

//画笔颜色下蓝色菜单项处理函数

void CDrawTestView::OnPenBlue()

{

// TODO: Add your command handler code here

m_PenColor = RGB(0,0,255)

this->SetMenuItemCheck(0,2,4,3)

}

//None菜单项处理函数

void CDrawTestView::OnBrushNone()

{

// TODO: Add your command handler code here

m_BrushStyle = -1

this->SetMenuItemCheck(1,0,7,0)

}

//HS_BDIAGONAL菜单项处理函数

void CDrawTestView::OnBrushBdiagonal()

{

// TODO: Add your command handler code here

m_BrushStyle = HS_BDIAGONAL

this->SetMenuItemCheck(1,0,7,1)

}

//HS_CROSS菜单项处理函数

void CDrawTestView::OnBrushCross()

{

// TODO: Add your command handler code here

m_BrushStyle = HS_CROSS

this->SetMenuItemCheck(1,0,7,2)

}

//HS_DIAGCROSS菜单项处理函数

void CDrawTestView::OnBrushDiagcross()

{

// TODO: Add your command handler code here

m_BrushStyle = HS_DIAGCROSS

this->SetMenuItemCheck(1,0,7,3)

}

//HS_FDIAGONAL菜单项处理函数

void CDrawTestView::OnBrushFdiagonal()

{

// TODO: Add your command handler code here

m_BrushStyle = HS_FDIAGONAL

this->SetMenuItemCheck(1,0,7,4)

}

//HS_HORIZONTAL菜单项处理函数

void CDrawTestView::OnBrushHorizontal()

{

// TODO: Add your command handler code here

m_BrushStyle = HS_HORIZONTAL

this->SetMenuItemCheck(1,0,7,5)

}

//HS_VERITICAL菜单项处理函数

void CDrawTestView::OnBrushVertical()

{

// TODO: Add your command handler code here

m_BrushStyle = HS_VERTICAL

this->SetMenuItemCheck(1,0,7,6)

}

//画刷颜色下白色菜单项处理函数

void CDrawTestView::OnBrushWhite()

{

// TODO: Add your command handler code here

m_BrushColor = RGB(255,255,255)

this->SetMenuItemCheck(1,1,4,0)

}

//画刷颜色下红色菜单项处理函数

void CDrawTestView::OnBrushRed()

{

// TODO: Add your command handler code here

m_BrushColor = RGB(255,0,0)

this->SetMenuItemCheck(1,1,4,1)

}

//画刷颜色下绿色菜单项处理函数

void CDrawTestView::OnBrushGreen()

{

// TODO: Add your command handler code here

m_BrushColor = RGB(0,255,0)

this->SetMenuItemCheck(1,1,4,2)

}

//画刷颜色下蓝色菜单项处理函数

void CDrawTestView::OnBrushBlue()

{

// TODO: Add your command handler code here

m_BrushColor = RGB(0,0,255)

this->SetMenuItemCheck(1,1,4,3)

}

我们在CDrawTestView类中再添加两个函数GetPen和GetBrush来获得自定义画笔和画刷指针,函数代码如下:

//获得自定义画笔指针

CPen* CDrawTestView::GetPen()

{

return new CPen(m_PenStyle,m_PenWidth,m_PenColor)

}

//获得自定义画刷指针

CBrush* CDrawTestView::GetBrush()

{

//判断是否是阴影线画刷

if (m_BrushStyle == -1)

//不是阴影线画刷

return new CBrush(m_BrushColor)

else

//是阴影线画刷

return new CBrush(m_BrushStyle,m_BrushColor)

}

编写这两个函数的好处在于:如果以后需要更改获得画笔或者画刷的方式(比如画笔改用第三个构造函数来构造),只需要修改这两个函数即可。如果在每个绘图函数菜单项的处理函数中写构造画笔和画刷的代码,一旦画笔和画刷的构造方式要发生改变,就必须逐个修改每个绘图函数菜单项的处理函数。

现在修改绘图函数菜单项的处理函数,调用GetPen和GetBrush函数获得画笔和画刷,然后选用画笔和画刷,并在函数的最后删除画笔和画刷。这是因为获得画笔和画刷的函数每次都是构造新的画笔和画刷,而它们将占用系统资源,所以在使用完毕后要进行删除。而LineTo等绘制线形图形的绘图函数不受画刷影响,所以在这些绘图函数的处理函数中不需要选用画刷。因为所有线形图形绘图函数菜单项的处理函数要添加的代码是相同的,同样所有区域图形绘图函数菜单项的处理函数要添加的代码也是相同的,所以这里只列出“LineTo”(绘制线形图形)菜单项和“Rectangle”(绘制区域图形)菜单项修改后的处理函数代码,读者只需按照相同方法修改其它处理函数即可。修改后的代码如下:

//LineTo菜单项处理函数

void CDrawTestView::OnDrawLineto()

{

// TODO: Add your command handler code here

CClientDC dc(this)

CPen* pen = this->GetPen()

dc.SelectObject(pen)

dc.MoveTo(300,300)

dc.LineTo(400,400)

pen->DeleteObject()

}

//Rectangle菜单项处理函数

void CDrawTestView::OnDrawRectangle()

{

// TODO: Add your command handler code here

CClientDC dc(this)

CPen* pen = this->GetPen()

CBrush* brush = this->GetBrush()

dc.SelectObject(pen)

dc.SelectObject(brush)

dc.Rectangle(450,100,650,250)

pen->DeleteObject()

brush->DeleteObject()

}

代码中粗体字部分是后添加的代码,修改原则是在获得设备环境对象之后,调用绘图函数之前,先获得画笔或画笔和画刷,然后让设备环境对象选用它们。最后在所有绘图函数执行完后,删除前面获得的画笔或画笔和画刷。

下面我们看一个例子,修改OnDraw函数,输入如下代码:

void CDrawTestView::OnDraw(CDC* pDC)

{

CDrawTestDoc* pDoc = GetDocument()

ASSERT_VALID(pDoc)

// TODO: add draw code for native data here

//构造要输出的文本字符串

CString s

s = "DrawTest Function"

//构造文本要输出的矩形区域

CRect r

r.left = 50r.right =150

r.top =10r.bottom = 40

//绘制矩形,以便确定输出的文本在矩形区域中的位置

pDC->Rectangle(r)

//水平,垂直方向都居中,单行显示,超出区域范围不剪切

pDC->DrawText(s,r,DT_VCENTER|DT_CENTER|DT_SINGLELINE|DT_NOCLIP)

//向下平移矩形区域

r.top = r.top + 50r.bottom = r.bottom +50

pDC->Rectangle(r)

//垂直方向居底,水平方向靠右,单行显示

pDC->DrawText(s,r,DT_BOTTOM|DT_RIGHT|DT_SINGLELINE)

//向下平移矩形区域

r.top = r.top + 50r.bottom = r.bottom +50

pDC->Rectangle(r)

//垂直方向居顶,水平方向靠左,超出矩形区域时,在单词之间换行

pDC->DrawText(s,r,DT_TOP|DT_LEFT|DT_WORDBREAK)

//向下平移矩形区域

r.top = r.top + 50r.bottom = r.bottom +50

pDC->Rectangle(r)

//垂直方向居中,水平方向靠左,单行显示,超出范围时用省略号取代字符串尾部字符

pDC->DrawText(s,r,

DT_VCENTER|DT_LEFT|DT_SINGLELINE|DT_END_ELLIPSIS)

}

而且多数人认为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=0iMoveTo(0,i)pDC->LineTo(1000,i)} 呵呵,程序有点变态,但是能说明问题.说到这里可能又有人要说了,为什么一个简单图形看起来没有复杂图形那么闪呢?这是因为复杂图形占的面积大,重画时造成的反差比较大,所以感觉上要闪得厉害一些,但是闪烁频率要低。那为什么动画的重画频率高,而看起来却不闪?这里,我就要再次强调了,闪烁是什么?闪烁就是反差,反差越大,闪烁越厉害。因为动画的连续两个帧之间的差异很小所以看起来不闪。如果不信,可以在动画的每一帧中间加一张纯白的帧,不闪才怪呢。* 可以在窗口形成时给窗口的注册类的背景刷付NULL* 也可以在形成以后修改背景 static CBrush brush(RGB(255,0,0))SetClassLong(this->m_hWnd,GCL_HBRBACKGROUND,(LONG)(HBRUSH)brush)* 要简单也可以重载OnEraseBkgnd(CDC* pDC)直接返回TRUE 这样背景没有了,结果图形显示的确不闪了,但是显示也象前面所说的一样,变得一团乱。怎么办?这就要用到双缓存的方法了。双缓冲就是除了在屏幕上有图形进行显示以外,在内存中也有图形在绘制。我们可以把要显示的图形先在内存中绘制好,然后再一次性的将内存中的图形按照一个点一个点地覆盖到屏幕上去(这个过程非常快,因为是非常规整的内存拷贝)。这样在内存中绘图时,随便用什么反差大的背景色进行清除都不会闪,因为看不见。当贴到屏幕上时,因为内存中最终的图形与屏幕显示图形差别很小(如果没有运动,当然就没有差别),这样看起来就不会闪。//随后建立与屏幕显示兼容的内存显示设备 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()得到裁剪区,然后在绘图时判断你的图形是否在这个区内,如果在就画,不在就不画。如果你的绘图过程不复杂,这样做可能对你的绘图效率不会有提高。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/11325998.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-15
下一篇 2023-05-15

发表评论

登录后才能评论

评论列表(0条)

保存