VC++(十一)图形的保存和重绘
-
Invalidate使整个窗口客户区无效。
窗口的客户区无效意味着需要重绘。Windows会在应用程序的消息队列里放置WM_PAINT消息。 -
UpdateWindow()的作用是使窗口立即重绘,可使WM_PAINT消息被直接发送到目标窗口,从而导致窗口立即重绘。
-
坐标空间和转换
Win32应用程序编程接口(API)使用四种坐标空间:世界坐标系空间、页面空间、设备空间、和物理设备空间。应用程序运用世界坐标系空间对图形输出进行旋转、斜切或者反射。
Win32 API把世界坐标系空间和页面空间称为逻辑空间;最后一种坐标空间(即物理设备空间)通常指应用程序窗口的客户区;但是它也包括整个桌面、完整的窗口(包括框架、标题栏和菜单栏)或打印机的一页或绘图仪的一页纸。物理设备的尺寸随显示器、打印机或绘图仪所设置的尺寸而变化。 -
如果该应用程序调用了SetWorldTransform函数,那么映射就从应用程序的世界坐标系空间开始;否则,映射在页面空间中进行。实际编程中,主要处理的是从页面空间到设备空间的转换。
-
逻辑坐标和设备坐标
几乎在所有的GDI函数中使用的坐标值都是采用的逻辑单位。Windows必须将逻辑单位转换为“设备单位”,即像素。这种转换是由映射方式、窗口和视口的原点以及窗口和视口的范围所控制的。
Windows对所有的消息(如WM_SIZE、WM_MOUSEMOVE、WM_LBUTTONDOWN、WM_LBUTTONUP),所有的非GDI函数和一些GDI函数(例如GetDeviceCaps函数),永远使用设备坐标。 -
窗口和视口
窗口是基于逻辑坐标的,逻辑坐标以cm mm为单位
视口是基于设备坐标的,设备坐标以像素为单位 -
两个原则:
1.总是由窗口原点映射为视口原点。即无论你窗口的原点和视口的原点怎么变,窗口的原点总是映射到视口的原点。(记住:映射方向是窗口到视口,而不是视口向窗口映射)
2.不管利用函数(如:SetViewportOrgExt和SetWindowOrgEx)对窗口和视口的原点做怎么改变,设备点(0,0)始终是客户区的左上角。 -
移动窗口和视口的原点
SetWindowOrg(100, -300)则逻辑点(100,-300)映射为设备点(0,0)
SetViewportOrg(100, -300)则逻辑点(0,00)映射为设备点(100,-300)
不管原点怎么变化,永远都是原点映射到原点。 -
CPtrArray对象存储多个对象的地址。
-
OnPaint 和 OnDraw
窗口重绘时会发送一个WM_PAINT消息:消息响应函数OnPaint().
CView类派生自CWnd类。
OnPaint()是CWnd的类成员,同时负责响应WM_PAINT消息。
OnDraw()是CVIEW的成员函数,并且没有响应消息的功能。这就是为什么你用VC成的程序代码时,在视图类只有 OnDraw没有OnPaint的原因。 -
要想在屏幕上绘图或显示图形,首先需要建立设备环境DC。其实DC是一个数据结构,它包含输出设备的绘图属性的描述。MFC提供了CPaintDC类和CWindwoDC类来实时的响应,而CPaintDC支持重画。
-
当视图变得无效时(包括大小的改变,移动,被遮盖等等),Windows 将 WM_PAINT 消息发送给它。该视图的 OnPaint 处理函数通过创建 CPaintDC 类的DC对象来响应该消息并调用视图的 OnDraw 成员函数。通常我们不必编写重写的 OnPaint 处理成员函数。
//CView默认的标准的重画函数
void CView::OnPaint()
{
CPaintDC dc(this);
OnPreparDC(&dc);
OnDraw(&dc); //调用了OnDraw
}
既然OnPaint最后也要调用OnDraw,因此我们一般会在OnDraw函数中进行绘制。下面是一个典型的程序
///视图中的绘图代码首先检索指向文档的指针,然后通过DC进行绘图调用。
void CMyView::OnDraw( CDC* pDC )
{
CMyDoc* pDoc = GetDocument();
CString s = pDoc->GetData(); // Returns a CString
CRect rect;
GetClientRect( &rect );
pDC->SetTextAlign( TA_BASELINE | TA_CENTER );
pDC->TextOut( rect.right / 2, rect.bottom / 2, s, s.GetLength() );
}
-
设备环境(Device Context)(设备上下文)
-
设备上下文DC是一个Windows数据结构,它包含了某个设备的绘制属性。通常,绘制调用都是借助于上下文对象,而这些设备上下文对象封装了用于画线、形状、文本等的Windows API。设备上下文是设备无关的,所以它既可以用于绘制屏幕,也可以用于绘制打印机甚至元文件。设备上下文在内存中创建,而内存经常受到扰动,所以它的地址是不固定的。因此,一个设备上下文句柄不是直接指向设备上下文对象,而是指向另外一个跟踪设备上下文地址的指针。
-
我个人认为设备上下文相当于画图过程中的画布(画纸),在VC中,这个画布可以是显示器,也可以使打印机,设备上下文决定了画布的属性,而且封装了在画布上画画的方法,比如画线,画点,等等,例如: pDc->LineTo(512,0); //从左下角到右上角的一条红色直线 。我们在VC中画图时,首先要得到这块画布才可以画画,所以要进行获取设备环境。
常见设备上下文及区别:
-
CClientDC,CPaintDC,CWindowDC
-
CClientDC:(客户区设备上下文)用于客户区的输出,与特定窗口关联,可以让开发者访问目标窗口中客户区,其构造函数中包含了GetDC,析构函数中包含了ReleaseDC。只能在客户区绘制图形。
-
CPaintDC:只能在客户区绘制图形。
(1)CPaintDC类是CDC类的一个派生类,该类一般用在响应WM_PAINT消息的函数OnPaint()中。
(2)WM_PAINT消息是当窗口的某个区域需要重画时激发的窗口消息。当程序中的消息循环接到WM_PAINT消息时就自动调用消息处理函数OnPaint(),如果在OnPaint函数内定义了CPaintDC类的对象,通过这个类对象就可以使用CDC类的成员函数完成视图客户区中的图形绘制操作。
(3)CPaintDC用于响应窗口重绘消息(WM_PAINT)时的绘图输出。CPaintDC在构造函数中调用BeginPaint()取得设备上下文,在析构函数中调用EndPaint()释放设备上下文。EndPaint()除了释放设备上下文外,还负责从消息队列中清除WM_PAINT消息。因此,在处理窗口重画时,必须使用CPaintDC,否则WM_PAINT消息无法从消息队列中清除,将引起不断的窗口重画。CPaintDC也只能用在WM_PAINT消息处理之中。 -
CWindowsDC:
(1)可在非客户区绘制图形,而CClientDC,CPaintDC只能在窗口的客户区绘制图形。
(2)坐标原点是在窗口的左上角,CClientDC,CPaintDC下坐标原点是在客户区的左上角。
CDC:GetDC,ReleaseDC
CClientDC:GetDC,ReleaseDC封装起来,只能访问客户区
CWindowDC:GetDC,ReleaseDC封装起来,不仅仅可以访问客户区
CPaintDC:窗口重绘时会发送一个WM_PAINT消息:消息响应函数OnPaint,调用CPaintDC
下面介绍一下获得这块画布的方法,即获取设备上下文的方法:
1、通过OnDraw()函数获得CPaintDC:在OnDraw()函数中入口参数CDC *pDC,传入操作的设备上下文,这个上下文是CPaintDC。
他是通过OnPaint()构建,并传入OnDraw(),View类如果没有重载OnPaint(),则窗口刷新时自动调用MFC底层代码的OnPaint()函数,
从而调用OnDraw()。我们就可以通过OnDraw()在客户区进行初始化的绘制。
2、通过CClientDC,CPaintDC,CWindowDC定义一个DC。
例如:CClientDC dc(this);创建一个dc,
在当前对象的客户区。创建一个CWindowDC类的对象:CWindowDC dc(this);
3、通过HDC GetDC(HWND hWnd);hWnd:设备上下文环境被检索的窗口的句柄,如果该值为NULL,
GetDC则检索整个屏幕的设备上下文环境,返回值是hdc,
即一个DC描述符,入口参数是一个窗口类型的句柄。
例如: pWnd=GetDlgItem(IDC_STATIC_PIC); //IDC_STATIC_PIC是对话框图片空间的一个标识号
pWnd->SetWindowPos(NULL,0,0,512,120,SWP_NOZORDER|SWP_NOMOVE); //调整长宽为(512,120)
pDc=pWnd->GetDC();
GDI及GDI函数
GDI对象就是绘画时用的工具,比如画笔,画刷,字体,位图,调色板。
如果绘图的时候,需要先把相应的GDI设备选入设备环境,就是为画图选个画笔,工具。
注意MFC中,GDI和CDC是两个独立的类
- 窗口滚动功能的实现
OnInitialUpdate
视图窗口完全建立后第一个被框架调用的函数。框架在第一次调用OnDraw前会调用OnInitialUpdate,因此OnInitialUpdate是设置滚动视图的逻辑尺寸和映射模式的最合适的地方。
OnCreate()后窗口产生, 然后才是视图的OnInitialUpDate,一般在这里对视图的显示做初始化。
一、OnCreate和OnInitialUpDate
-
OnCreate()只是产生VIEW的基本结构和变量.OnCreate()不产生窗口,只是在窗口显示前设置窗口的属性如风格位置等。create负责注册并产生窗口。
OnInitialUpDate()中,主要初始化视图中控件等。对各个变量进行初始化操作。 -
OnCreate()是生成结构的
OnInitialUpDate()是对结构进行初始化。
两个一个相当于硬件一个相当于软件,功能和作用完全不同。 -
OnCreate
oncreate 消息响应函数,是用来“表示一个窗口正在生成”。
在CWnd::Create中,又会调用OnCreate函数,但是实际上这个时候Create函数还没有退出,CWnd的某些部分还没有创建好。 -
create()不是对应于消息wm_create的,oncreate()才是。
create()只用于产生窗口,像动态创建控件中的create()一样。 -
设备坐标(0,0)点始终位于窗口客户区的左上角。
-
两种保存图形和重绘图形的方式:元文件和兼容设备描述表
元文件
元文件设备上下文类:CMetaFileDC
元文件并没有包含所绘图形的图形数据,它包含的是图形的绘制命令。