2 有关屏幕输出
2.1 设备上下文工作原理
在绝大多数的WINDOWS应用都需要在屏幕上显示自己的数据。由于WINDOWS是一个设备无关的操作系统,所以任何向屏幕上进行输出的功能都要间接地通一个叫做设备上下文(device context)的对象来完成。我们向设备上下文提出屏幕输出的要求,然后由WINDOWS自己来调用具体的输出设备的驱动程序来完成实际的输出工作。围绕设备上下文,MFC提供了一系列与其配合使用的绘图对象,这其中包括画笔对象、刷子对象以及字体对象等等。它们的工作模型是这样的:首先对设备上下文对象——我们简称为DC对象——进行设置,然后选择进行屏幕输出所需要的工具,最后用DC对象的输出函数绘制图形。屏幕输出的目标一般都是窗口的客户区,它是一个万能的输出区域,可以接受无论是文本、位图、还是其他类型的数据(比方说OLE对象)。
2.2 实例绘图原理剖析
在有关用户输入部分的内容当中,我们曾经介绍过一个实例,它访问一个保存歌曲曲目的数据库,用户可以通过对话框让用户定义一个曲目表,并选定一张背景图,然后在一个没有系统菜单的窗口客户区上滚动显示曲目的名字。我们已经介绍了通过对话框介绍用户输入的方法,接下来就着重介绍如何把用户输入的信息在屏幕上显示出来。
我们将用户选定的字符串在一张背景图上面滚动输出。前面已经介绍了使用设备上下文进行工作的基本模型。即:首先选择绘图的工具,然后调用DC 的绘图函数来进行绘制。在WINDOWS当中,每次窗口的外观发生改变的时候都会发出一个WM_PAINT消息,窗口的重绘工作都是在响应这个消息的处理函数当中进行的。可以使用CLASSWIZARD来添加这个消息响应函数。之后,就可以在这个函数当中进行屏幕输出了。还有什么时候会触发重绘事件呢?在程序中调用CWND的UpdateWindow 和RedrawWindow数的时候都会触发重绘事件。我们还可以直接使用SendMessage函数向一个指定的窗口送出重绘消息。另外调用CWND的 Invalidate函数可以指示重绘的时候是否需要擦去背景,如果使用InvalidateRect函数还可以设置客户区的无效区域,系统重绘的时候将只把该区域的内容重新绘制,我们首先在窗口的客户区上帖一张位图,然后滚动输出文本。如何实现滚动输出呢我们的方法是在程序中设置一个定时器,在定时器计时已满事件WM_TIMER触发的时候来,调用REDRAWWINDOW函数,触发重绘事件,我们只要在它的消息响应函数ONPAINT当中重新绘制背景,擦去原来的文本,然后不断的改变文本输出的位置就可以达到目的了。您可能会重绘整个背景的做法会很耗费了过多的系统时间并且可能产背景的闪烁。这种担心是必要的。在WINDOWS95当中,系统对重绘的机制进行了优化,在我们没有指定无效区域的情况之下,系统自己会选择一个最小的无效区域,只对这一区域进行重绘。
2.3 绘图操作实现
下面介绍绘图操作的源程序使您对设备上下文的使用有一个大致的了解。
首先生成一个设备上下文。CPaintDC是MFC提供的一个从CDC继承出来的类。使用它有什么好处呢? 如果直接使用CDC的话,我们需要首先调用CWnd的BeginPaint函数为重绘工作做一些准备工作,在完成绘制之还要用EndPaint函数表示结束绘制工作。所有的绘图操作都必须在这两个函数之间完成。CPaintDC封装了这两个函数,自动地对它们进行调用,使用者无须再去进行这些调用。
CPaintDC dc(this);
BITMAP bm;
m_bitmap_>GetBitmap(&bm);
CDC dcImage;
if (!dcImage.CreateCompatibleDC(&dc))
return;
CBitmap* pOldBitmap = dcImage.SelectObject(m_bitmap);
dc.BitBlt(0, 0, bm.bmWidth, bm.bmHeight,
&dcImage, 0, 0, SRCCOPY);
以上这段代码完成向屏幕上面输出位图的工作。首先根据资源生成一个位图对象,然后生成成一个和CPaintDC一致的内存DC对象,在内存DC当中选择这个位图。BITMAP是一个WIN32提供的位图结构,我们将这幅位图的信息保存在这个结构当中。这样做的原因是由于在使用到位图的位置及大小信息。BITMAP结构的定义如下:
typedef struct tagBITMAP { /* bm */
int bmType;
int bmWidth;
int bmHeight;
int bmWidthBytes;
BYTE bmPlanes;
BYTE bmBitsPixel;
LPVOID bmBits;
} BITMAP;
作好这些准备工作之后。调用DC对象的BitBlt函数把位图从内存DC当中贴到绘图DC当中来。前四个参数指示了位图在目的DC上的位置和大小。第五个参数是原来保存位图的内存DC的地址。接下来的两个参数是从位图的哪一点开始进行拷。最后这个参数设置该位图和屏幕上当前内容的相互关系,SRCCOPY的意思是拷贝过来覆盖原来的内容。这个参数还有其他的许多选择比方说取反操作或者异或操作,设置不同的参数可以获得丰富的效果。
下面介绍如何输出文本。首先对DC对象进行设置:
dc.SetBkMode(TRANSPARENT);
dc.SetTextColor(RGB(0 , 155 , nowX*nowX));
这里把文本输出的背景置为透明,然后设置输出文本的前景颜色。
下面这段程序的意思是为将要输出的文本选择字体。
LOGFONT logfont;
memset(&logfont, 0, sizeof(logfont));
logfont.lfWeight = 50;
logfont.lfHeight = 50;
lstrcpy(logfont.lfFaceName, "黑体");
nowFont.CreateFontIndirect(&logfont);
dc.SelectObject(&nowFont);
首先声一个WIN32提供的字体信息结构。为其分配内存空间。再按照我们的要求填写这个字体结构,设置字体的宽度,设置字体的高度,选择字体种类,根据这个结构生成一个CFONT字体对象,让DC对象选中这个字体对象,最后使用DC的文本输出函数来输出一个字符串。
总而言之,进行屏幕输出的规则如下:
第一 必须通过CDC对象进行屏幕输出;
第二 设置DC对象的输出属性;
第三 选择绘图工具
第四 用CDC对象的绘图函数。
有关屏幕输出的内容就介绍到这里。
2.4 有关屏幕映射方式
在一般的情况之下,我们都以像素作为绘图的单位,我们称之为设备坐标。我们在进行绘图操作的时候,不可避免的要用到设备坐标系。
WINDOWS提供了几种映射方式,或称坐标系。可以通过它们来和设备上下文相联系。比方说不管是什么样的显示设备,如果我们需要在上面显示一个2英寸高,2英寸宽的矩形,该怎样处理呢?这就要依赖于我们所设定的坐标系。如果我们指定了MM_TEXT 方式,这时坐标原点就位于屏幕的左上角,X轴和Y轴的方向分别指向我们面对屏幕的右方和下方,它的绘图单位是像素,如果一英寸对应72个像素的话,我们就需要这样绘制这个矩形:
DC.Rectangle(CRect( 0,0,72*2,72*2));
所以我们如果我们指定了MM_LOENGLISH 方式,那么一个绘图单位就是百分之一英寸,坐标原点仍然位于屏幕的左上角,但是X轴和Y轴的方向恰好和MM_TEXT方式下的轴方向相反,同样完成绘制上面提到的矩形的工作,我们就需要写出这样的代码:
DC.Rectangle(CRect(0,0,200,_200));
可见,坐标系的选择对我们编写程序有很大的影响。
此外,在有些时候,我们需要在几个不同的坐标系下面工作,那么还需要在进行在这些坐标系之间的转换工作。所以 ,我们有必要在这里详细介绍以下WINDOWS的坐标映射方法。
一般来说,最常用的就是WM_TEXT方式。在WM_TEXT坐标方式下面,坐标被映射到了像素,X的值向右方递增,Y的值向下递增,并且可以通过调用CDC的SetViewpotOrg函数来改变坐标原点。下面的代码把屏幕映射方式设为MM_TEXT方式,并且把坐标原点设在(300,300)处:
DC.SetMapMode(MM_TEXT);
DC.SetViewportOrg(CPoint(300,300));
另外,WINDOWS提供了一组非常重要的比例固定的映射方式,在这些映射方式下面,我们可以改变它的坐标原点,却无法改变它的比例因子。对于MM_LOENGLISH映射方式,我们已经知道它的X值是向右递减的,Y的值是向下递减的,所有的固定比例的映射方式都遵循这一原则。它们的比例因子也各不相同。我们列表如下:
映射方式
|
逻辑单位
|
MM_LOENGLISH
|
0.01英寸
|
MM_HIENGLISH
|
0.001英寸
|
MM_LOMETRIC
|
0.1毫米
|
MM_HIMETRIC
|
0.01毫米
|
MM_TWIPS
|
1/1440英寸
|
最后一种映射方式MM_TWIPS常常用语打印机,一个’twip’单位相当于1/20个点(一点近似与1/72)英寸。例如,如果指定的MM_TWIPS一个社单位,那么对于12点大小的字模来说,字符的高度为12x20,即240个twip。
除了固定比例的映射方式,WINDOWS还提供了比例可变的映射方式,在这种映射方式下面,我们除了可以改变它们比例因子之外还可以改变比例因子。借助于这样的映射方式,当用户改变窗口的尺寸的时候,绘制的图形的大小也可以根据比例发生相应的变化;同样,当我们翻转某个轴的时候,他们所绘制的图像,也以另外的一个轴为轴心进行翻转。这样的映射方式有两种:MM_ISOTROPIC和 MM_ANIOTROPIC。
在MM_ISOTROPIC方式下,纵横的比例为1:1,换句话说,无论比例因子如何变化,我们画出的图形不会改变自己的形状。但是在MM_ANIOSTROPIC方式下面,X和Y的比例因子可以独立地变化,图形的形状可以发生变化。
我们分析下面这段程序:
void CAView::OnDraw(CDC *pDC)
{
CRect clientDC;
GetClientRect(clientRect);
pDC_>SetMapMode(MM_ANISOTROPIC);
pDC_>SetWindowExt(1000,1000);
pDC_>SetViewportExt(clientRect.right,_clientRect.bottom);
pDC_>SetViewportOrg(clientRect.right/2, clientRect.bottom/2);
pDC_>Ellipse(CRect(_500, _500, 500, 500));
}
这段代码的功能是这样的,首先取得窗口客户区矩形的大小,然后用SetWindowExt和SetViewportExt函数设定比例,结果窗口尺寸的大小被设为1000个逻辑单位高和1000个逻辑单位宽,坐标原点被设为窗口的中心,在这样的设置之下,绘制出一个半径为500个逻辑单位的椭圆。
在这里如果将映射方式改变为MM_ISOTROPIC那么就将画出一个圆。圆的直径是窗口举行宽和高的最小值。
下面我们给出逻辑单位到设备单位的公式:
X比例因子 = X视口范围/X窗口范围
Y比例因子 = Y视口范围/Y窗口范围
设备X = 逻辑X *X比例因子 + X坐标原点偏移
设备Y = 逻辑Y *Y比例因子 + Y坐标原点偏移
当我们设定了设备上下文的映射方式之后,就可以直接使用逻辑坐标作为其参数了,但是从WM_MOUSEMOVE消息所获得的鼠标的坐标值是设备坐标。许多其他的MFC库函数,尤其是类CRect的成员函数,只接受设备坐标。所以我们有时要利用CDC的LPtoDP和DPtoLP在逻辑坐标和设备坐标之间进行转换的工作。
下面我们列出进行坐标映射工作的时候所要遵循的一些规则:
- 可以认为CDC的所有成员函数都以逻辑坐标作为参数,但和CRect有关的函数例外。
- 可以认为CWnd 的成员函数都以设备坐标作为参数。
- 所有的HIT_TEST操作都应该考虑设备坐标。
- 以逻辑坐标的形式来保存数据,否则用户对窗口进行滚动操作的时候,这个数据就不再有效了。
一、什么是设备场景
关于设备场景,叫法颇多,有些书上说为设备环境、显示场景,更常见的叫做设备描述表或设备描述体。当然你爱怎么叫随你的便,我还是喜欢说为设备场景。
那 么究竟什么是设备场景呢? 设备场景是一种windows对象,而windows则是一种图形环境,其图形系统令人难以自信地灵活和强大。而实质上,widnows下的所有绘图都是通过设备场景进行的,而不是直接对窗口和设备本身进行。为了说明设备场景,很多书都拿一些现实生活中的现象来进行对照说明。其中,最常见的是把它比喻为一位画家在作画。我想大家都看过画家是如何画画的,最起码是在电影里或者是在道旁的广告牌上作画的画家。我们可以想象一下∶有个风景秀丽的白云山(是我瞎起的名)上,有位画家一只手拿着调色板,另一只手则拿着画笔,面对一个画板正在写风景画。有些书认为画家的调色板相当于设备场景,有些书则认为画板相当于设备场景,说法不一。鉴于这种情况,我认为还是直接去说明设备场景比较好。
作为windows的对象,设备场景实际上是一种windows内部的数据结构。就象pointapi数据结构具有x和y两个属性一样,设备场景同样具有着它自身的属性,只是属性比较多而已,如下表∶
设备场景属性
属 性 默 认 值
背景色(background color) 白色(white)
背景模式(background mode) 不透明(opaque)
位图(bitmap)无(none)
刷子(brush)白色刷子(white brush)
刷子起点(brush origin) 0,0
剪切区(clipping region) 整个窗口或设备表面(entire window or device surface)
调色板(color palette) 默认调色板(default palette)
画笔位置(pen position) 0,0
绘图模式(drawing mode) r2_copypen
字体(font) 系统字体
字间距(intercharater spacing) 0
影射模式(mapping mode) mm_text
画笔(pen) 黑色(black)
多边形填充模式(mapping mode) alternate
伸缩模式(stretching mode) blackonwhite
文本色(text color) 黑色(black)
视口起点(viewport origin) 0,0
视口范围(viewport extents) 1,1
窗口起点(window origin) 0,0
窗口范围(window extents) 1,1
请你多看看这张表,对设备场景都有哪些属性,脑子里应当有个印象。事实上,设备场景的很多属性对应于vb中的form、picturebox、text等窗体或控件的属性。比如,字体、背景色、绘图模式等等。可想而知,很多学vb的朋友尽管并不知道什么叫设备场景,但实质上都不知不觉地使用了设备场景。可以说,设备场景是windows编程中最重要的概念之一。
对于设备场景,有些朋友可能一时不大好理解,这很自然,不用担心谁都是一样。不知对你能否作为一个帮助,我是把设备场景想象成一种配套的(包括画板、调色板、画笔、刷子等)的绘画工具。其中画板是最重要的,其他的东西都是为这个画板服务的。如过你创建了一个设备场景,就等于是你从百货商店买来了这一套绘画工具,从而具备了绘画的条件。但,你的房间总不是那么宽敞的。为了继续绘出别的画、继续购买新的绘画工具,无用的工具应当及时清理掉。因为设备场景本身是占用内存的。不要担心这会降低运行速度,对计算机来说创建一个设备场景,再删掉一个设备场景,那都是瞬息之间的事情,根本谈不上什么浪费时间,绝对不像跑一趟百货商店那么麻烦、费时。对于绘图,你应当认识的一点是,绘图并不是简单地指绘画,输出文本也是一种绘图过程。尽管如此,api函数中图形函数与文本函数大体都是各自各的。绘画和写文本都是在同样的设备场景中进行,这一点很重要。
我想,你大概还是没有理解好,不过没有关系,继续往下看好了。本节中请记住一点∶
widnows下的所有绘图都是通过设备场景进行的。
一、什么是设备场景
关于设备场景,叫法颇多,有些书上说为设备环境、显示场景,更常见的叫做设备描述表或设备描述体。当然你爱怎么叫随你的便,我还是喜欢说为设备场景。
那 么究竟什么是设备场景呢?
设备场景是一种windows对象,而windows则是一种图形环境,其图形系统令人难以自信地灵活和强大。而实质上,widnows下的所有绘图都是
通过设备场景进行的,而不是直接对窗口和设备本身进行。为了说明设备场景,很多书都拿一些现实生活中的现象来进行对照说明。其中,最常见的是把它比喻为一
位画家在作画。我想大家都看过画家是如何画画的,最起码是在电影里或者是在道旁的广告牌上作画的画家。我们可以想象一下∶有个风景秀丽的白云山(是我瞎起
的名)上,有位画家一只手拿着调色板,另一只手则拿着画笔,面对一个画板正在写风景画。有些书认为画家的调色板相当于设备场景,有些书则认为画板相当于设
备场景,说法不一。鉴于这种情况,我认为还是直接去说明设备场景比较好。
作为windows的对象,设备场景实际上是一种windows内部的数据结构。就象pointapi数据结构具有x和y两个属性一样,设备场景同样具有着它自身的属性,只是属性比较多而已,如下表∶
设备场景属性
属 性 默 认 值
背景色(background color) 白色(white)
背景模式(background mode) 不透明(opaque)
位图(bitmap)无(none)
刷子(brush)白色刷子(white brush)
刷子起点(brush origin) 0,0
剪切区(clipping region) 整个窗口或设备表面(entire window or device
surface)
调色板(color palette) 默认调色板(default palette)
画笔位置(pen position) 0,0
绘图模式(drawing mode) r2_copypen
字体(font) 系统字体
字间距(intercharater spacing) 0
影射模式(mapping mode) mm_text
画笔(pen) 黑色(black)
多边形填充模式(mapping mode) alternate
伸缩模式(stretching mode) blackonwhite
文本色(text color) 黑色(black)
视口起点(viewport origin) 0,0
视口范围(viewport extents) 1,1
窗口起点(window origin) 0,0
窗口范围(window extents) 1,1
一、什么是设备场景
关于设备场景,叫法颇多,有些书上说为设备环境、显示场景,更常见的叫做设备描述表或设备描述体。当然你爱怎么叫随你的便,我还是喜欢说为设备场景。
那 么究竟什么是设备场景呢?
设备场景是一种windows对象,而windows则是一种图形环境,其图形系统令人难以自信地灵活和强大。而实质上,widnows下的所有绘图都是
通过设备场景进行的,而不是直接对窗口和设备本身进行。为了说明设备场景,很多书都拿一些现实生活中的现象来进行对照说明。其中,最常见的是把它比喻为一
位画家在作画。我想大家都看过画家是如何画画的,最起码是在电影里或者是在道旁的广告牌上作画的画家。我们可以想象一下∶有个风景秀丽的白云山(是我瞎起
的名)上,有位画家一只手拿着调色板,另一只手则拿着画笔,面对一个画板正在写风景画。有些书认为画家的调色板相当于设备场景,有些书则认为画板相当于设
备场景,说法不一。鉴于这种情况,我认为还是直接去说明设备场景比较好。
作为windows的对象,设备场景实际上是一种windows内部的数据结构。就象pointapi数据结构具有x和y两个属性一样,设备场景同样具有着它自身的属性,只是属性比较多而已,如下表∶
设备场景属性
属 性 默 认 值
背景色(background color) 白色(white)
背景模式(background mode) 不透明(opaque)
位图(bitmap)无(none)
刷子(brush)白色刷子(white brush)
刷子起点(brush origin) 0,0
剪切区(clipping region) 整个窗口或设备表面(entire window or device
surface)
调色板(color palette) 默认调色板(default palette)
画笔位置(pen position) 0,0
绘图模式(drawing mode) r2_copypen
字体(font) 系统字体
字间距(intercharater spacing) 0
影射模式(mapping mode) mm_text
画笔(pen) 黑色(black)
多边形填充模式(mapping mode) alternate
伸缩模式(stretching mode) blackonwhite
文本色(text color) 黑色(black)
视口起点(viewport origin) 0,0
视口范围(viewport extents) 1,1
窗口起点(window origin) 0,0
窗口范围(window extents) 1,1
请你多看看这张表,对设备场景都有哪些属性,脑子里应当有个印象。事实上,设备场景的很多属性对应于vb中
的form、picturebox、text等窗体或控件的属性。比如,字体、背景色、绘图模式等等。可想而知,很多学vb的朋友尽管并不知道什么叫设备
场景,但实质上都不知不觉地使用了设备场景。可以说,设备场景是windows编程中最重要的概念之一。
对于设备场景,有些朋友可能一时不大好理
解,这很自然,不用担心谁都是一样。不知对你能否作为一个帮助,我是把设备场景想象成一种配套的(包括画板、调色板、画笔、刷子等)的绘画工具。其中画板
是最重要的,其他的东西都是为这个画板服务的。如过你创建了一个设备场景,就等于是你从百货商店买来了这一套绘画工具,从而具备了绘画的条件。但,你的房
间总不是那么宽敞的。为了继续绘出别的画、继续购买新的绘画工具,无用的工具应当及时清理掉。因为设备场景本身是占用内存的。不要担心这会降低运行速度,
对计算机来说创建一个设备场景,再删掉一个设备场景,那都是瞬息之间的事情,根本谈不上什么浪费时间,绝对不像跑一趟百货商店那么麻烦、费时。对于绘图,
你应当认识的一点是,绘图并不是简单地指绘画,输出文本也是一种绘图过程。尽管如此,api函数中图形函数与文本函数大体都是各自各的。绘画和写文本都是
在同样的设备场景中进行,这一点很重要。
设备描述表(设备上下文)
Windows下编程 2010-03-24 19:13:45 阅读21 评论0 字号:大中小
Windows 98 支持的设备描述表属性
设备描述表属性 默认值 修改该值的函数 获取该值的函数
Mapping Mode MM_TEXT SetMapMode GetMapMode
Window Origin (0,0) SetWindowOrgEx GetWindowOrgEx
OffsetWindowOrgEx
Viewport Origin (0,0) SetViewportOrgEx GetViewportOrgEx
OffsetViewportOrgEx
Window Extents (1,1) SetWindowExtEx GetWindowExtEx
SetMapMode
ScaleWindowExtEx
Viewport Extents (1,1) SetViewportExtEx GetViewportExtEx
SetMapMode
ScaleViewportExtEx
Pen BLACK_PEN SelectObject SelectObject
Brush WHITE_BRUSH SelectObject SelectObject
Font SYSTEM_FONT SelectObject SelectObject
Bitmap None SelectObject SelectObject
Current Position (0,0) MoveToEx GetCurrentPositionEx
LineTo
PolylineTo
PolyBezierTo
Background Mode OPAQUE SetBkMode GetBkMode
Background Color White SetBkColor GetBkColor
Text Color Black SetTextColor GetTextColor
Drawing Mode R2_COPYPEN SetROP2 GetROP2
Stretching Mode BLACKONWHITE SetStretchBltMode GetStretchBltMode
Polygon Fill Mode ALTERNATE SetPolyFillMode GetPolyFillMode
Intercharacter 0 SetTextCharacterExtra GetTextCharacterExtra
Spacing
Brush Origin (0,0) SetBrushOrgEx GetBrushOrgEx
Clipping Region None SelectObject GetClipBox
SelectClipRgn
IntersectClipRgn
OffsetClipRgn
ExcludeClipRect
SelectClipPath