*******************************************************************例子*******************************************************************
********************所有的GDI绘图函数使用的都是逻辑坐标(逻辑范围)*******************
*******************系统默认情况下 物理范围和逻辑范围 是1:1 的对应关系*******************
1. 首先定义类成员:
CDC *m_pDC;
CDC MemDC;
CBitmap MemBitmap;
CBitmap *pOldbitmap;
LONG xRange; // 逻辑范围,x方向宽度
LONG yRange; // 逻辑范围,y方向高度
LONG nWidht; // 物理范围,x方向宽度
LONG nHeight; // 物理范围,y方向高度
2. 在类初始化函数中:
m_pDC = this->GetDC(); // 获取设备上下文句柄
CWnd *wnd = GetDlgItem(IDC_SHOWGRAPH); // 获取界面上显示图形的ID控件的句柄
wnd->GetWindowRect(&rect); // 获取显示/画图区域大小(物理范围)
ScreenToClient(&rect); // 转换为客户区坐标
nWidth = rect.Width(); // 显示/画图区域x方向物理宽度
nHeight = rect.Height(); // 显示/画图区域y方向物理高度
3. 在自定义函数中,设置视口与窗口的比例关系:
m_pDC->SetMapMode(MM_ANISOTROPIC); // 注意MM_ANISOTROPIC和MM_ISOTROPIC的区别
m_pDC->SetWindowExt(XRange,-yRange); // 设定窗口尺寸范围,画图使用的逻辑范围,实现放大或是缩小,坐标方向↑和→为正向
m_pDC->SetViewportExt(nWidth,nHeight); // 设定视口尺寸范围,客户区实际图形显示的区域范围,大小固定
m_pDC->SetViewportOrg(rect.left,rect.bottom); //设定画图的逻辑原点坐标(0,0)在物理坐标的(rect.left,rect.bottom)点上
4. 在自定义函数中,双缓冲技术的使用:
MemDC.CreateCompatibleDC(m_pDC); // 创建内存兼容设备上下文
MemBitmap.CreateCompatibleBitmap(m_pDC,xRange,yRange); // 创建内存兼容画布,大小由逻辑范围决定
pOldbitmap = MemDC.SelectObject(&MemBitmap); // 将画布选入内存设备上下文
MemDC.FillSolidRect(0,0,xRange,yRange,RGB(123,213,132)); // 对内存中的画布填充背景颜色,否则是默认的黑色
// 画图操作,如画一条对角直线
MemDC.MoveTo(0,0);
MemDC.LineTo(xRange*0.9,yRange*0.9);
// 将内存中的画图区域拷贝到界面的控件区域上去
// 第1和第2个参数若是0时,则从物理坐标的(rect.left,rect.bottom)点上开始按上述指定的方向贴图
m_pDC->BitBlt(0,0,xRange,yRange,&MemDC,0,0,SRCCOPY);
5. 在类的析构函数中:
MemDC.SelectObject(pOldbitmap);
bitmap.DeleteObject();
this->ReleaseDC(m_pDC);
6. 至此,就完成了双缓冲及坐标缩放绘图的功能
*********************************************************************************************************************************************
用VC做的画图程序,当所画的图形大于屏幕时,在拖动滚动条时屏幕就会出现严重的闪烁,为了解决这一问题,就得使用双缓冲来解决。程序产生严重的闪烁问题是因为画图过程中前后两次的画面反差很大造成的人的视觉的闪烁。因为在VC中每次在调用OnDraw时系统都是先用背景画刷将画布清除再执行画图命令,这样在你每次移动滚动条时每执行一次OnDraw就会有一个空白页,这样和你的最终结果图象之间有一个很大的反差,因而看起来闪烁,而且滚动条滚动越快闪烁越严重。当然,你可以将背景画刷设为NULL,这样可以解决闪烁问题,但是不能将先前的图象擦除,这样整个屏幕就显得很乱。
下面将利用双缓冲来解决这一问题的思路给大家作一下简单的介绍。
我先来解释一下在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函数其实最后也是被转化为这样的方