用CScrollView作view类的基类生成了一个SDI的MFC程序,然后用memory dc画图,再把图像帖回dc中。为了实现缩放功能,使用了StetchBlt。当水平滚动范围小于等于数据长度时,缩放采取改变水平滚动范围的方法,此时程序功能正常。当水平滚动范围等于数据长度时,缩放采用隔点画线的方法,但是此时如果水平滚动条不在最左边(或垂直滚动条不在最上边),帖的图只能看到当前可视范围的右边(或下边),往左(或上)拖没图像。当然在缩放时将滚动条用SetScrollPos()归零可以解决问题,但是看起来很奇怪。在memory dc画图之前将滚动条归零然后再复原,这种办法在数据少时可以忍受,数据多了(memory dc绘图耗时长了)就会发现滚动条的来回移动。不动滚动条位置能否显示完整的视图?
另外,我发现当数据大于2^19多一点的时候,memory dc贴出来的图是黑的,直接画就没问题。怎么解决数据点数问题?
请各位老大帮帮忙
部分代码:
CScrollTestView::CScrollTestView()
:ready(false)
,length(1<<15)
,zoom(1)
,hPos(0)
{
}void CScrollTestView::OnDraw(CDC* pDC)
{
CScrollTestDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return; if (!ready)
{
hPos = GetScrollPos(SB_HORZ);
SetScrollPos(SB_HORZ, 0);
PrepDraw();
SetScrollPos(SB_HORZ, hPos);
} pDC->StretchBlt(-300, -3000, sizeTotal.cx, 6000, &dcMem, -300, -3000, length, 6000, SRCCOPY);
}void CScrollTestView::OnInitialUpdate()
{
CScrollView::OnInitialUpdate();
sizeTotal.cx = length;
sizeTotal.cy = 6000;
SetScrollSizes(MM_TWIPS, sizeTotal); CMainFrame *pMain=(CMainFrame *)AfxGetApp()->m_pMainWnd;
pMain->m_wndStatusBar.SetPaneText(pMain->m_wndStatusBar.CommandToIndex(ID_INDICATOR_X)," 0");
pMain->m_wndStatusBar.SetPaneText(pMain->m_wndStatusBar.CommandToIndex(ID_INDICATOR_Y)," 0");
pMain->m_wndStatusBar.SetPaneText(pMain->m_wndStatusBar.CommandToIndex(ID_INDICATOR_SIZE)," 0");
}void CScrollTestView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
{
CScrollView::OnPrepareDC(pDC, pInfo);
CRect rect;
GetClientRect(rect);
pDC->SetWindowOrg(-300, 3000);
}void CScrollTestView::PrepDraw(void)
{
CClientDC dc(this);
if (dcMem.m_hDC)
dcMem.DeleteDC();
dcMem.CreateCompatibleDC(&dc);
OnPrepareDC(&dcMem); CSize p1(1, 1);
dcMem.DPtoLP(&p1); CBitmap* pbmNew = new CBitmap;
CRect rect, flRect;
rect.right = length/p1.cx;
rect.bottom = 6000/p1.cy;
pbmNew->CreateCompatibleBitmap(&dc, rect.right, rect.bottom); CBitmap* pOldBitmap = dcMem.SelectObject(pbmNew);
CBrush brBkg(::GetSysColor(COLOR_WINDOW));
flRect.right = length;
flRect.bottom = 6000;
dcMem.FillRect(flRect, &brBkg);
int x = length - 500, y = 140;
//x for x scope, y for y scope
CPen* ppBlue = new CPen;
ppBlue->CreatePen(PS_SOLID, 1, RGB(0,0,255));
dcMem.SelectObject(ppBlue); dcMem.MoveTo(-150, 0);
dcMem.LineTo(x, 0);//draw x axis
dcMem.MoveTo(x, 0);
dcMem.LineTo(x-100, 50);//draw x axis arrow
dcMem.MoveTo(x, 0);
dcMem.LineTo(x-100, -50);//draw x axis arrow
dcMem.MoveTo(0, -20*y);
dcMem.LineTo(0, 20*y);//draw y axis
dcMem.MoveTo(0, 20*y);
dcMem.LineTo(50, 20*y-100);//draw y axis arrow
dcMem.MoveTo(0, 20*y);
dcMem.LineTo(-50, 20*y-100);//draw y axis arrow CPen* ppRed = new CPen;
ppRed->CreatePen(PS_SOLID, 1, RGB(255,0,0));
dcMem.SelectObject(ppRed);
dcMem.MoveTo(0, 0);
for(int i=0; i<x/zoom; i++)
{
dcMem.LineTo(i*zoom, exp(-i/10000.)*20*sin(i/50.)*100);
} delete ppBlue;
delete ppRed;
delete pbmNew; ready = true;
}void CScrollTestView::OnLButtonUp(UINT nFlags, CPoint point)
{
if (sizeTotal.cx>1<<10 && sizeTotal.cx<=length && zoom==1)
sizeTotal.cx /= 2;
else if (sizeTotal.cx>length || zoom>1)
{
if (zoom > 1)
zoom --;
else
zoom = 1;
ready = false;
}
else
sizeTotal.cx = 1<<10;
SetScrollSizes(MM_TWIPS, sizeTotal);
CString valSize;
valSize.Format("%d", sizeTotal.cx);
CMainFrame *pMain=(CMainFrame *)AfxGetApp()->m_pMainWnd;
pMain->m_wndStatusBar.SetPaneText(pMain->m_wndStatusBar.CommandToIndex(ID_INDICATOR_SIZE),valSize);
Invalidate();
CScrollView::OnLButtonUp(nFlags, point);
}void CScrollTestView::OnRButtonUp(UINT nFlags, CPoint point)
{
if (sizeTotal.cx<=length/2 && zoom==1)
sizeTotal.cx *= 2;
else
{
sizeTotal.cx = length;
if (zoom < 16)
zoom ++;
else
zoom = 16;
ready = false;
}
SetScrollSizes(MM_TWIPS, sizeTotal);
CString valSize;
valSize.Format("%d", sizeTotal.cx);
CMainFrame *pMain=(CMainFrame *)AfxGetApp()->m_pMainWnd;
pMain->m_wndStatusBar.SetPaneText(pMain->m_wndStatusBar.CommandToIndex(ID_INDICATOR_SIZE),valSize);
Invalidate();
CScrollView::OnRButtonUp(nFlags, point);
}void CScrollTestView::OnMouseMove(UINT nFlags, CPoint point)
{
CClientDC dc(this);
OnPrepareDC(&dc);
dc.DPtoLP(&point); CString valX, valY;
valX.Format("%d", point.x);
valY.Format("%d", point.y); CMainFrame *pMain=(CMainFrame *)AfxGetApp()->m_pMainWnd;
pMain->m_wndStatusBar.SetPaneText(pMain->m_wndStatusBar.CommandToIndex(ID_INDICATOR_X),valX);
pMain->m_wndStatusBar.SetPaneText(pMain->m_wndStatusBar.CommandToIndex(ID_INDICATOR_Y),valY); CScrollView::OnMouseMove(nFlags, point);
}
另外,我发现当数据大于2^19多一点的时候,memory dc贴出来的图是黑的,直接画就没问题。怎么解决数据点数问题?
请各位老大帮帮忙
部分代码:
CScrollTestView::CScrollTestView()
:ready(false)
,length(1<<15)
,zoom(1)
,hPos(0)
{
}void CScrollTestView::OnDraw(CDC* pDC)
{
CScrollTestDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return; if (!ready)
{
hPos = GetScrollPos(SB_HORZ);
SetScrollPos(SB_HORZ, 0);
PrepDraw();
SetScrollPos(SB_HORZ, hPos);
} pDC->StretchBlt(-300, -3000, sizeTotal.cx, 6000, &dcMem, -300, -3000, length, 6000, SRCCOPY);
}void CScrollTestView::OnInitialUpdate()
{
CScrollView::OnInitialUpdate();
sizeTotal.cx = length;
sizeTotal.cy = 6000;
SetScrollSizes(MM_TWIPS, sizeTotal); CMainFrame *pMain=(CMainFrame *)AfxGetApp()->m_pMainWnd;
pMain->m_wndStatusBar.SetPaneText(pMain->m_wndStatusBar.CommandToIndex(ID_INDICATOR_X)," 0");
pMain->m_wndStatusBar.SetPaneText(pMain->m_wndStatusBar.CommandToIndex(ID_INDICATOR_Y)," 0");
pMain->m_wndStatusBar.SetPaneText(pMain->m_wndStatusBar.CommandToIndex(ID_INDICATOR_SIZE)," 0");
}void CScrollTestView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
{
CScrollView::OnPrepareDC(pDC, pInfo);
CRect rect;
GetClientRect(rect);
pDC->SetWindowOrg(-300, 3000);
}void CScrollTestView::PrepDraw(void)
{
CClientDC dc(this);
if (dcMem.m_hDC)
dcMem.DeleteDC();
dcMem.CreateCompatibleDC(&dc);
OnPrepareDC(&dcMem); CSize p1(1, 1);
dcMem.DPtoLP(&p1); CBitmap* pbmNew = new CBitmap;
CRect rect, flRect;
rect.right = length/p1.cx;
rect.bottom = 6000/p1.cy;
pbmNew->CreateCompatibleBitmap(&dc, rect.right, rect.bottom); CBitmap* pOldBitmap = dcMem.SelectObject(pbmNew);
CBrush brBkg(::GetSysColor(COLOR_WINDOW));
flRect.right = length;
flRect.bottom = 6000;
dcMem.FillRect(flRect, &brBkg);
int x = length - 500, y = 140;
//x for x scope, y for y scope
CPen* ppBlue = new CPen;
ppBlue->CreatePen(PS_SOLID, 1, RGB(0,0,255));
dcMem.SelectObject(ppBlue); dcMem.MoveTo(-150, 0);
dcMem.LineTo(x, 0);//draw x axis
dcMem.MoveTo(x, 0);
dcMem.LineTo(x-100, 50);//draw x axis arrow
dcMem.MoveTo(x, 0);
dcMem.LineTo(x-100, -50);//draw x axis arrow
dcMem.MoveTo(0, -20*y);
dcMem.LineTo(0, 20*y);//draw y axis
dcMem.MoveTo(0, 20*y);
dcMem.LineTo(50, 20*y-100);//draw y axis arrow
dcMem.MoveTo(0, 20*y);
dcMem.LineTo(-50, 20*y-100);//draw y axis arrow CPen* ppRed = new CPen;
ppRed->CreatePen(PS_SOLID, 1, RGB(255,0,0));
dcMem.SelectObject(ppRed);
dcMem.MoveTo(0, 0);
for(int i=0; i<x/zoom; i++)
{
dcMem.LineTo(i*zoom, exp(-i/10000.)*20*sin(i/50.)*100);
} delete ppBlue;
delete ppRed;
delete pbmNew; ready = true;
}void CScrollTestView::OnLButtonUp(UINT nFlags, CPoint point)
{
if (sizeTotal.cx>1<<10 && sizeTotal.cx<=length && zoom==1)
sizeTotal.cx /= 2;
else if (sizeTotal.cx>length || zoom>1)
{
if (zoom > 1)
zoom --;
else
zoom = 1;
ready = false;
}
else
sizeTotal.cx = 1<<10;
SetScrollSizes(MM_TWIPS, sizeTotal);
CString valSize;
valSize.Format("%d", sizeTotal.cx);
CMainFrame *pMain=(CMainFrame *)AfxGetApp()->m_pMainWnd;
pMain->m_wndStatusBar.SetPaneText(pMain->m_wndStatusBar.CommandToIndex(ID_INDICATOR_SIZE),valSize);
Invalidate();
CScrollView::OnLButtonUp(nFlags, point);
}void CScrollTestView::OnRButtonUp(UINT nFlags, CPoint point)
{
if (sizeTotal.cx<=length/2 && zoom==1)
sizeTotal.cx *= 2;
else
{
sizeTotal.cx = length;
if (zoom < 16)
zoom ++;
else
zoom = 16;
ready = false;
}
SetScrollSizes(MM_TWIPS, sizeTotal);
CString valSize;
valSize.Format("%d", sizeTotal.cx);
CMainFrame *pMain=(CMainFrame *)AfxGetApp()->m_pMainWnd;
pMain->m_wndStatusBar.SetPaneText(pMain->m_wndStatusBar.CommandToIndex(ID_INDICATOR_SIZE),valSize);
Invalidate();
CScrollView::OnRButtonUp(nFlags, point);
}void CScrollTestView::OnMouseMove(UINT nFlags, CPoint point)
{
CClientDC dc(this);
OnPrepareDC(&dc);
dc.DPtoLP(&point); CString valX, valY;
valX.Format("%d", point.x);
valY.Format("%d", point.y); CMainFrame *pMain=(CMainFrame *)AfxGetApp()->m_pMainWnd;
pMain->m_wndStatusBar.SetPaneText(pMain->m_wndStatusBar.CommandToIndex(ID_INDICATOR_X),valX);
pMain->m_wndStatusBar.SetPaneText(pMain->m_wndStatusBar.CommandToIndex(ID_INDICATOR_Y),valY); CScrollView::OnMouseMove(nFlags, point);
}
解决方案 »
- [散分]星期五散个分,祝大家周末愉快啊!
- 关于FindWindow无法获取窗口句柄
- 如何实时检测录音时人肉声音的强度
- VC同一个CPP文件中有两个函数A和B,如何让B调用A函数??
- 请问各位高人,我的非模式对话框怎么显示不出来啊
- Directx初学者问题!在线。。。
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~对面的高手瞧过来~~~~~~~~~~~~~~~~~~~~~~~
- MajorVon()老师进来领分
- 怎样用‘<<‘ 把0~15的数字转换成2进制
- 各位高手,如何才能给CListCtrl每行增加一个按钮呢?
- 得到CColorDialog的handle 如何设定其默认选定的颜色?
- 我想在类定义里实现这样的初始化功能.
VS.net 2k3, win xp sp2
查了很多资料都没查到,难道要去读mfc的源码?
把length初始化为1<<18,然后修改PrepDraw()中的背景颜色:
.....
CBrush brBkg(::GetSysColor(COLOR_WINDOW));
//CBrush brBkg(RGB(0, 0, 0));
.....
最后将上述代码中的OnRButtonUp函数中的代码修改如下
if .....
else
{
sizeTotal.cx *= 2;
//sizeTotal.cx = length;
if (zoom < 16)
zoom ++;
else
zoom = 16;
//ready = false;
}
.....
再点几下鼠标右键,就可以看到黑色了。2. 水平滚动条来回移动很明显的问题只有在数据量大时看得到,顶楼代码不变,在我的机器上length初始化为1<<20,然后将水平滚动条往右拖动一下,再点右键就会发现水平滚动条明显地来回移动了。这样的话,看起来效果就很差了。如果length初始化为1<<21或更多,那么出来的是白板!不解中...3. 往左拖没图像的重现:
把OnDraw()中的
if (!ready)
{
hPos = GetScrollPos(SB_HORZ);
SetScrollPos(SB_HORZ, 0);
PrepDraw();
SetScrollPos(SB_HORZ, hPos);
}
改为
if (!ready)
{
//hPos = GetScrollPos(SB_HORZ);
//SetScrollPos(SB_HORZ, 0);
PrepDraw();
//SetScrollPos(SB_HORZ, hPos);
}
运行时往右拖滚动条,点右键,再往左拖,就可以发现左面没图像了。当时就是发现滚动条的位置导致了该问题,所以才在OnDraw()中使用SetScrollPos移动滚动条。但奇怪的是,在sizeTotal<length的时候,往左拖就有图像。个人认为,移动滚动条只是权益之计,因为有上述的问题2,所以才问“不动滚动条位置能否显示完整的视图?”
CSize p1(1, 1);
dcMem.DPtoLP(&p1); CBitmap* pbmNew = new CBitmap;
CRect rect, flRect;
rect.right = length/p1.cx;
rect.bottom = 6000/p1.cy;
pbmNew->CreateCompatibleBitmap(&dc, rect.right, rect.bottom);
因为CreateCompatibleBitmap使用的是device unit,而我知道的却是logic unit,所以用上述代码作了一下转换。length小的时候没有问题,大的时候就发黑。你所说的好像是有一部分是黑的,我所说的是length大的时候全发黑。
......
郁闷,考虑读mfc源码或者....换C#
我研究了你的代码,终于发现了问题1的所在!
你在OnDraw()中没有采用特殊的映射模式才使我找到了问题的大致所在。你的代码中width=500,而500*65=32500<32768<33000=500*66!而我采用的MM_TWIPS与MM_TEXT的比例是17:1,正好17*32768=557056>524288=2^19!这可能意味着在MFC内部,StretchBlt甚至BitBlt的宽度和高度采用的都是signed short类型,一旦超出2^15(32768)就会溢出了。不过这个问题已经不重要了,我限制了数据点数,让它不能超出17*32768。问题是,那个滚动条拖动时有的部分不能显示,不管数据多少有这个问题!Bill help me...
pDC->StretchBlt(0,1,32767,300,&memDC,0,0,width,height,SRCCOPY);
没事。一旦写成,
pDC->StretchBlt(0,1,32768,300,&memDC,0,0,width,height,SRCCOPY);
马上变黑。可它怎么会变黑?溢出了以后,成为0了,应该变白才对嘛顺便猜测一下,那个滚动条没准也是溢出
pDC->StretchBlt(-300, -3000, sizeTotal.cx, 6000, &dcMem, -300, -3000, length, 6000, SRCCOPY);
改成
pDC->StretchBlt(-300, -3000, 526103, 6000, &dcMem, -300, -3000, length, 6000, SRCCOPY);然后把 526103 改成 526103+1,变黑(在我的机器上),那就是说526103是个临界点。
映射比例:MM_TWIPS:MM_TEXT = 526103 / 32767 = 16.055879390850550859096041749321。如果把-300改成-303,那么那个临界数值就成了526106。
映射比例:MM_TWIPS:MM_TEXT = 526106 / 32767 = 16.055970946378978850672933133946。继续试验,-300改成0,那么那个临界数值就成了526108。
映射比例:MM_TWIPS:MM_TEXT = 526108 / 32767 = 16.056031983397930845057527390362。查了一下资料,MM_TWIPS的一个单位为1/20磅(1/72英寸),对应不同的显示器,其与MM_TEXT(像素映射)的比例数据不是固定的,这点可以理解。可是这和第一个参数有什么关系?毕竟第三个参数的语义是“宽”,如果是复制区域矩形相对于前两个参数的点的对应对角线另一个端点,那两者之间你变我也变可以理解。可它定义的是“宽”,那没道理有影响。就算StrechBlt内部对于这个参数的处理是short而不是int这一点可能是个bug,但是这个“bug”也不能解释这种现象。我快疯了。
CSize p1(1, 1);
dcMem.DPtoLP(&p1);
的结果。
原以为是MFC的代码有错,但是跟踪到windows内部好像代码都是正确的,一直是用的32位寄存器来存放那个srcWidth。在我的机器上跟踪到windows内部的结果(倍数为66):
77EFC72F push dword ptr [ebp+30h]
77EFC732 mov dword ptr [eax+6D0h],edi
77EFC738 push dword ptr [ebp+2Ch]
77EFC73B push dword ptr [ebp+28h]
77EFC73E push dword ptr [ebp+24h]
77EFC741 push dword ptr [ebp+20h]
77EFC744 push dword ptr [ebp+1Ch]
77EFC747 push dword ptr [ebp+18h]
77EFC74A push dword ptr [ebp+14h] ;[ebp+14h]=0x80e8=500*66
77EFC74D push dword ptr [ebp+10h]
77EFC750 push dword ptr [ebp+0Ch]
77EFC753 push esi
77EFC754 call 77EFC764
一直是用的dword,我在77EFC74D时观察了内存[ebp+14h]中的数值,依然是32位的。不知道是怎么回事。我试试flyback的方法,看起来像是可以解决最重要的问题3的样子:)
1. 添加private成员变量CPoint ScrPos;
2. 在OnLButtonUp和OnRButtonUp中加入ScrPos = GetScrollPosition();
3. 在OnPrepareDC中修改原来的原点映射为:pDC->SetWindowOrg(-300-ScrPos.x, 3000-ScrPos.y);
4. 在OnDraw中修改贴图坐标为:pDC->StretchBlt(-300-ScrPos.x, -3000, sizeTotal.cx, 6000, &dcMem, -300, -3000, length, 6000, SRCCOPY);然后去掉来回移动水平滚动条的语句。
如此一来,就不用考虑问题2了。
问题1真的是MS的bug吗?......没有人回答......