坐标和几个函数的用法说明 .

所以说视口和窗口实际上是表示的同一块区域,只不过是因为单位和原点的不同,需要进行映射,逻辑单位就是窗口,就是Window,就是像素,毫米,英寸,就是给人用的单位,就是设备无关的单位,设备单位就是视口,就是Viewport,就只能是像素,就是给设备用的单位,确定的一厘米,在不同的设备上的像素数可能会有区别,所以是设备相关的单位。

intxLogPixPerInch = GetDeviceCaps(hdc, LOGPIXELSX); 
intyLogPixPerInch = GetDeviceCaps(hdc, LOGPIXELSY); 
SetMapMode(MM_ANISOTROPIC); 
SetWindowExt(100, 100); 
SetViewportExt(xLogPixPerInch, yLogPixPerInch); 

 

HDC GetDCEx(HWND hWnd,HRGN hrgnClip,DWORD flags)

最近在学Win32的编程,看的是《Windows程序设计第5版》一书,这本书是台湾人翻译的,有些译法和大陆不一样,书中还有一些错误的地方,很多时候需要中英文对照阅读,下载请点击

Windows中包括以下3种设备坐标,以满足各种不同需要:

 

需要注意的是,使用公制映射模式(非默认映射模式)时,为使输出可见,Y坐标必须为负值。

 

Windows还能将视口(设备)坐标转换为窗口(逻辑)坐标:

//定义逻辑窗口区域,单位为逻辑单位(Logical)
WINGDIAPI BOOL  WINAPI SetWindowExtEx (HDC, int, int, LPSIZE);
此API函数在MFC中封装为CDC::virtual CSize SetWindowExt(int cx, int cy);

2、 GDI函数分类

所以只要牢记开头的公式,得到正确的对应的参数,就可以画出需要的图形。

(1)客户区域坐标,包括应用程序的客户区域,客户区域的左上角为(0,0)。

“窗口”和“视口”的概念:
窗口(Window):对应逻辑坐标系上程序员设定的区域
视口(Viewport):对应实际输出设备上程序员设定的区域
1.窗口原点是指逻辑窗口坐标系的原点在视口(设备)坐标系中的位置,视口原点是指设备实际输出区域的原点。
2.除了映射模式,窗口和视口也是决定一个点的逻辑坐标如何转换为设备坐标的一个因素。一个点的逻辑坐标按照如下式子转换为设备坐标:
  设备(视口)坐标 = 逻辑坐标 – 窗口原点坐标      + 视口原点坐标

SelectObject函数

其实公式拿出来,学数学的小伙伴是不是就懂了大半了,这个公式非常重要,理解了这个公式,后面的很多东西就能理解,首先,公式中的Window,WinOrg,WinExt,就是带了Win的东西,就是使用的逻辑坐标的值,就跟GDI函数中的一样,逻辑坐标的单位可能是像素(MM_TEXT映射)、毫米(单位是0.1mm,在MM_LOMETRIC映射下)等等等等(看下图).

以下是设置映射模式的代码。

GetClient(&rect);
举个例如:有个单文档程序

InvertRect:在某一矩形区域内反显现有颜色。

 

其中,(xWindow,yWindows)是待转换的逻辑点,(xViewport,yViewport)是转换后的设备点。如果设备坐标是客户区域坐标或全窗口坐标,则Windows在画一个对象前,还必须将这些坐标转换成屏幕坐标。

void   MoveWindow(   int   x,   int   y,   int   nWidth,   int  
nHeight,   BOOL   bRepaint   =   TRUE   );   
void   MoveWindow(   LPCRECT   lpRect,   BOOL   bRepaint   =   TRUE  
);   
 参数:
  x指定了CWnd的左边的新位置。  
  y指定了CWnd的顶部的新位置。  
  nWidth指定了CWnd的新宽度。  
  nHeight指定了CWnd的新高度。  
 
bRepaint指定了是否要重画CWnd。如果为TRUE,则CWnd象通常那样在OnPaint消息处理函数中接收到一条WM_PAINT消息。如果这个参数为FALSE,则不会发生任何类型的重画操作。这应用于客户区、非客户区(包括标题条和滚动条)和由于CWnd移动而露出的父窗口的任何部分。当这个参数为FALSE的时候,应用程序必须明确地使CWnd和父窗口中必须重画的部分无效或重画。lpRectCRect对象或RECT结构,指定了新的大小和位置。说明这个函数改变窗口的位置和大小。对于顶层的CWnd对象,x和y参数是相对于屏幕的左上角的。对于子对象,它们是相对于父窗口客户区的左上角的。
 
 
MoveWindow函数发送一条WM_GETMINMAXINFO消息。处理这个消息时,CWnd得到一个改变最大和最小的窗口缺省值的机会。如果传递给MoveWindow成员函数的参数超过了这些值,则在WM_GETMINMAXINFO处理函数中可以用最小或最大值来代替这些值。   
    
  BOOL  CWnd::SetWindowPos(const  CWnd* pWndInsertAfter,  int   x, int
 y, int  cx, int  cy,UINT  nFlags   );  
  返回值如果函数成功,则返回非零值;否则返回0。  
 
参数pWndInsertAfter标识了在Z轴次序上位于这个CWnd对象之前的CWnd对象。这个参数可以是指向CWnd对象的指针,也可以是指向下列值的指针:l
  wndBottom      
将窗口放在Z轴次序的底部。如果这个CWnd是一个顶层窗口,则窗口将失去它的顶层状态;系统将这个窗口放在其它所有窗口的底部。l
  wndTop       将窗口放在Z轴次序的顶部。l   wndTopMost      
将窗口放在所有非顶层窗口的上面。这个窗口将保持它的顶层位置,即使它失去了活动状态。wndNoTopMost
     
将窗口重新定位到所有非顶层窗口的顶部(这意味着在所有的顶层窗口之下)。这个标志对那些已经是非顶层窗口的窗口没有作用。有关这个函数以及这些参数的使用规则参见说明部分。x指定了窗口左边的新位置。y指定了窗口顶部的新位置。cx指定了窗口的新宽度。cy指定了窗口的新高度。nFlags指定了大小和位置选项。这个参数可以是下列值的组合:l
  SWP_DRAWFRAME       围绕窗口画出边框(在创建窗口的时候定义)。l  
SWP_FRAMECHANGED      
向窗口发送一条WM_NCCALCSIZE消息,即使窗口的大小不会改变。如果没有指定这个标志,则仅当窗口的大小发生变化时才发送
WM_NCCALCSIZE消息。l   SWP_HIDEWINDOW       隐藏窗口。SWP_NOACTIVATE
     
不激活窗口。如果没有设置这个标志,则窗口将被激活并移动到顶层或非顶层窗口组(依赖于pWndInsertAfter参数的设置)的顶部。l
  SWP_NOCOPYBITS      
废弃这个客户区的内容。如果没有指定这个参数,则客户区的有效内容将被保存,并在窗口的大小或位置改变以后被拷贝回客户区。l
  SWP_NOMOVE       保持当前的位置(忽略x和y参数)。l  
SWP_NOOWNERZORDER       不改变拥有者窗口在Z轴次序上的位置。l  
SWP_NOREDRAW      
不重画变化。如果设置了这个标志,则不发生任何种类的变化。这适用于客户区、非客户区(包括标题和滚动条)以及被移动窗口覆盖的父窗口的任何部分。当这个标志被设置的时候,应用程序必须明确地无效或重画要重画的窗口和父窗口的任何部分。l
  SWP_NOREPOSITION       与SWP_NOOWNERZORDER相同。l  
SWP_NOSENDCHANGING       防止窗口接收WM_WINDOWPOSCHANGING消息。l  
SWP_NOSIZE       保持当前的大小(忽略cx和cy参数)。l   SWP_NOZORDER  
    保持当前的次序(忽略pWndInsertAfter)。l   SWP_SHOWWINDOW      
显示窗口。  
   
 
调用这个成员函数以改变子窗口、弹出窗口和顶层窗口的大小、位置和Z轴次序。窗口在屏幕上按照它们的Z轴次序排序。在Z轴次序上处于顶端的窗口将程序在所有其它窗口的顶部。子窗口的所有坐标都是客户坐标(相对于父窗口客户区的左上角)。窗口可以被移动到Z轴次序的顶部,既可以通过将
pWndInsertAfter参数设为&wndTopMost,并确保没有设置SWP_NOZORDER标志,也可以通过设置窗口的Z轴次序使它位于所有现存的顶层窗口上方。当一个非顶层窗口被设为顶层窗口时,它拥有的窗口也被设为顶层的。它的拥有者不发生变化。如果顶层窗口被重新定位到Z轴次序的底部(&wndBottom)或任何非顶层窗口之后,则它将不再是顶层窗口。当顶层窗口被变为非顶层窗口时,它所有的拥有者和它拥有的所有窗口都被变为非顶层窗口。如果既没有指定SWP_NOACTIVE标志也没有指定SWP_NOZORDER标志(这意味着应用程序要求窗口被同时激活并放入指定的Z轴次序),则pWndInsertAfter参数中指定的值将只在下列环境下适用:l
  在pWndInsertAfter参数中既没有指定&wndTopMost也没有指定&wndNoTopMost。
 
 
这个窗口不是活动窗口。应用程序不能激活一个非活动窗口但同时又不把它带到Z轴次序的顶部。应用程序可以没有任何限制地改变活动窗口的Z轴次序。非顶层窗口可能拥有一个顶层窗口,但是反之则不成立。任何被顶层窗口拥有的窗口(例如对话框)都将自己变为顶层窗口,以确保所有被拥有的窗口位于它们的拥有者上方。在Windows
 
3.1或更新的版本中,可以将窗口移动到Z轴次序的顶部,并通过设置它们的WS_EX_TOPMOST风格而将之锁定在那里。这种顶层窗口即使在失去活动状态以后也会保持顶层位置。例如,选择WinHelp的Always
  On  
Top命令会使帮助窗口变为顶层,并且在你返回应用程序之后它还保持可见。要创建一个顶层窗口,应在调用SetWindowPos的时候将
pWndInsertAfter参数设为&wndTopMost,或者在创建窗口的时候设置WS_EX_TOPMOST风格。如果Z轴次序中包含了任何具有WS_EX_TOPMOST风格的窗口,则用&wndTopMost移动的窗口将被放到所有非顶层窗口的顶部,但是位于任何顶层窗口的下面。当应用程序激活一个不具有WS_EX_TOPMOST风格的非活动窗口时,该窗口将被移动到所有非顶层窗口的上方,但是位于所有顶层窗口的下方。如果在调用SetWindowPos的时候pWndInsertAfter参数被设为&wndBottom,并且CWnd是一个顶层窗口,则该窗口失去顶层状态(WS_EX_BOTTOM风格被清除),并且系统将窗口放在Z轴次序的底部。

这样至少可以保证宽和高是成比例的

要讲上面的公式,就要先说一下视口(Viewport 台湾译作视埠)和窗口(Window
台湾译作视窗)

Windows在显示时以”逻辑英寸”为单位,逻辑英寸比实际的英寸要大。如果Windows程序使用实际英寸,则普通的10磅文本在显示器上就会小到几乎难以辨认,因此Windows使用放大了的”逻辑英寸”来表示文本。逻辑英寸只影响显示,而不影响打印。

//设置逻辑窗口的原点坐标,缺省原点为(0,0)。
WINGDIAPI BOOL  WINAPI SetWindowOrgEx(HDC, int, int, LPPOINT);
此API函数在MFC中封装为CDC::CPoint SetWindowOrg(int x, int y);
注意:SetWindowOrg(Ex)
只有在映射模式为MM_ANISOTROPIC或MM_ISOTROPIC时才有意义。

例如:

 
 首先,逻辑坐标这个名词就让很多人望而却步,确实,不能“望文生义”地理解的翻译就不是好翻译 
 ——鲁迅。哈哈,开个玩笑,我们要理解这两个东西,首先要想到如果你要用Win32要绘制一个东西,该怎么做呢?比如绘制一个矩形,假设我们调用的是Rectangle(hdc,30,20,50,80),(这个函数的用法是Rectangle(hdc,left,top,right,bottom),我叫雷锋,不用谢我)。可以看到,跟很多GDI函数一样,这个函数里面使用了很多数字,坐标。让我们回忆一下小学知识,绘制一个东西,不仅应当搞清楚他的长度,还应该搞清楚他的单位,那么这里的30,20,50,80的单位是什么呢?很多人会说,是像素!这个答案是对的,但是又不全对。事实上,Windows默认的映射方式(Mapping
Mode,简称就是MM)是MM_TEXT,在MM_TEXT映射方式(TEXT实际上跟文字没有多大关系,是这种映射方式下的坐标方向,从左到右,从上到下,跟文字阅读方式一样)下,这个单位确实是像素。实际上,逻辑坐标和设备坐标的区别就在于他们的单位不一样!

注:MM_TWIPS经常在打印机上,单位是1/20磅(1磅=1/72英寸)。

//设置视口的原点坐标,缺省原点为(0,0)。
WINGDIAPI BOOL  WINAPI SetViewportOrgEx(HDC, int, int, LPPOINT);
此API函数在MFC中封装为CDC:: virtual CPoint SetViewportOrg(int x, int y);

int GetTextFace(HDC hDC,int nCount,LPSTR lpFaceName);

DWORD GetFontLanguageInfo(HDC hDC);

int GetTextCharSet(HDC hDC);

int GetTextCharSetInfo(HDC hDC,LPFONTSIGNATURES lpSig,DWORD dwFlags);

BOOL GetTextMetrics(HDC hDC,LPTEXTMETRIC lptm);

UINT GetOutlineTextMetrics(HDC hDC,UINT cbData,LPOUTLINETEXTMETRIC lpOTM);

就是说我们在调用Win32函数绘图的时候,要知道自己使用的单位(根据映射模式确定的)。因为绘图函数里的数值,使用的就是这些单位,虽然默认的MM_TEXT映射模式使用的单位就是像素,但是很多时候其他单位也很有用,比如你要做一个屏幕尺子的时候,你要用尺子量一下物体有几厘米。尺子上的刻度就可以用其他的映射模式来画。但是屏幕在显示的时候却不能只知道逻辑坐标几厘米啊,屏幕得知道具体的像素位置才行啊!那这个时候,就需要用到上面的公式转换了。讲到这里,公式里的ViewExt/WinExt是什么意思就很明显了。那就是在当前逻辑坐标系下(比如几厘米,打比方哈),实际上是上面映射模式表格里的单位)对应的设备坐标应该是多少个像素!这样转换过后,得到实际的Viewport,就是该逻辑点在屏幕上的位置。

一、映射模式基本知识
当Windows应用程序在其客户区绘制图形时,必须给出在客户区的位置,其位置用x和y
两个坐标表示,x表示横坐标,y表示纵坐标。在所有的GDI绘制函数中,这些坐标使用的是一
种”逻辑单位”。当GDI函数将输出送到某个物理设备上时,Windows将逻辑坐标
转换成设备坐标(如屏幕或打印机的像素点)。逻辑坐标和设备坐标的转换是由映射模式决
定的。映射模式被储存在设备环境中。GetMapMode函数用于从设备环境得到当前的映射模
式,SetMapMode函数用于设置设备环境的映射模式。
1.逻辑坐标

不等同于

(1)
pKernel指向页面池:对每个有效GDI对象,pKernel从不为空,并且值总是唯一的。因此看起来对每个GDI对象有一个相应的数据结构,这个数据结构只能从内核模式代码存取,甚至不能从GDI32.DLL直接存取。对于不同进程的对象,从pKernel的值中看不出明显区分区域来。pKernel指向的对象起始地址是0xE1000000,根据《Inside
Windows
NT》,起始地址是0xE1000000的区域一般是被称为“页面池”的可分页系统的内存堆。

下面我们拿出一个公式

2.逻辑坐标与设备坐标转换时误差的处理

GetWindowRect()
得到的是在屏幕坐标系下的RECT(即以屏幕左上角为原点)
GetClientRect()
得到的是在客户区坐标系下的RECT(即以所在窗口左上角为原点,去掉了标题栏计算,仅仅是个大小,返回值的左上角永远为0,0) 
  
CRect rect;

9、画弧

图片 1

当我们将映射模式设置成基于逻辑英寸的MM_LOMETRIC时,窗口的范围设为256,视口的范围设为96(在VGA显示器下LOGPIXELSX的值),约2.6个逻辑单位对应1个像素,这显然会造成不小的误差,它会表现在应用程序的各个方面:客户区的一个部分没有被刷新;对象之间本来没有间距,却显示出有间距;对象在屏幕的不同位置上会缩小或增大一个像素等问题。

(1)Windows坐标系统
Windows坐标系分为逻辑坐标系和设备坐标系两种,GDI支持这两种坐标系。一般而言,
GDI的文本和图形输出函数使用逻辑坐标,而在客户区移动或按下鼠标的鼠标位置是采用设备坐标。
<1>逻辑坐标系是面向DC的坐标系,这种坐标不考虑具体的设备类型,在绘图时,Windows会根据当前设置的映射模式将逻辑坐标转换为设备坐标。
<2>设备坐标系是面向物理设备的坐标系,这种坐标以像素或设备所能表示的最小长度单位为单位,X轴方向向右,Y轴方向向下。设备坐标系的原点位置(0,
0)不限定在设备显示区域的左上角。

椭圆:Ellipse

最后,这些映射模式,视口原点(ViewOrg),窗口原点(WinOrg)等,都是设备内容(DC
Device
Context,又译作装置内容、设备上下文等)的属性,设备内容其实就是你绘制的区域。有三种BeginPaint(在WM_PAINT时绘制无效区域),GetDC是客户端区域(在更新时绘制整个客户区域),GetWindowsDC是整个窗口区域(可以绘制包括窗口标题栏,菜单栏区域)。这些知识细讲的话又是另一篇博客了

映 射 模 式 每 英 寸 所 对 应 的 逻 辑 单 位 数
MM_LOENGLISH 100
MM_HIENGLISH 1000
MM_LOMETRIC 254
MM_HIMETRIC 2540
MM_TWIPS 1440

 

参考资料:《Windows图形编程》

 

映射方式定义了Windows如何将GDI函数中指定的逻辑坐标映射为设备坐标。要继续讨论映射方式我们要介绍Windows有关映射模式的一些术语:我们将逻辑坐标所在的坐标系称为”窗口”,将设备坐标所在的坐标系称为”视口”。

from:

与某个设备上下文相关联的逻辑调色板可以用GetCurrentObject(hDC,OBJ_PAL)函数来获取。对于一个新的设备上下文,它的缺省调色板是由GetStockObject(DEFAULT_PALETTE)函数返回的库存缺省调色板。

1.逻辑坐标和设备坐标

3.逻辑坐标与设备坐标的转换方式

 

例如:

上述代码中调用SetMapMode函数将映射模式设置为自定义的,该调用必须位于SetWindowExt
和SetViewportExt调用之前,否则设置将会无效。
上述代码实际上将映射模式设置成逻辑MM_LOENGLISH,若程序员需要设置逻辑MM_LOMETRIC、MM_HIMETRIC、MM_HIENGLISH
或MM_TWIPS,只需修改上述代码中的SetWindowExt的参数,该参数实际上是每英寸所包含的各种映射模式下的单位数。根据表1中各映射模式的参数,可得到表2中每英寸所对应的各逻辑单位的个数。

//定义视口的坐标轴方向及区域、定义域和值域,单位为像素(Pixel)
WINGDIAPI BOOL  WINAPI SetViewportExtEx(HDC, int, int, LPSIZE);
此API函数在MFC中封装为CDC::virtual CSize SetViewportExt(int cx, int
cy);
注意:SetViewportExt(Ex)
只有在映射模式为MM_ANISOTROPIC或MM_ISOTROPIC时才有意义。

其它映射模式则原点不变,只是Y轴会翻转使正向朝上,并且逻辑单位被按比例转换为实际距离大小,而不是像素数。

在看到GDI(GDI Graphic Device
Interface图形设备接口)
映射方式这一节的时候,书中又是逻辑坐标,又是设备坐标,又是视口,窗口,又是视埠什么的,搞得人头都大了。虽然我现在还没有完全读懂,但是我感觉我已经抓住了理解这些东西的主线,下面的东西就当作我的笔记吧:

可以采取以下两个步骤避免转换误差。(1)尽量选择窗口范围和视口范围比可以整除的映射方式,例如基于逻辑英寸的MM_TWIPS其窗口范围和视口范围比1440/96,可简化为15/1,从设备坐标转化为逻辑坐标时没有误差,从消除误差角度看,MM_坐标和几个函数的用法说明 .。TWIPS比其他几个映射模式都要好。(2)窗口范围和视口范围比不能整除时,也尽量将其简化,例如,当采用0.3900mm
中的将1个逻辑单位映射成1/64英寸的映射方式时,其窗口范围和视口范围比值为64/96,可简化为2/3。如果我们将逻辑单位的值都取为2的倍数,设备单位的值都取为3的倍数,转换后就没有精度的丢失了。

MM_LOMETRIC
 0.1 mm                 X轴正方向朝右,Y轴正方向朝上
 
MM_HIMETRIC
 0.01 mm                X轴正方向朝右,Y轴正方向朝上
 
MM_LOENGLISH
 0.01 inch              X轴正方向朝右,Y轴正方向朝上
 
MM_HIENGLISH
 0.001 inch             X轴正方向朝右,Y轴正方向朝上
 
MM_TWIPS
 1/1440 inch            X轴正方向朝右,Y轴正方向朝上
 
MM_ISOTROPIC
 自定义(X=Y)            自定义
 
MM_ANISOTROPIC
 自定义(X!=Y)           自定义
 

其逻辑单位的大小等于视口范围和窗口范围的比值。

要注意的是在上述的5映射模式下,ViewExt/WinExt的比例都是已经确定了,不能更改的,如果要更改两个值,只能在MM_ISOTROPIC和MM_ANISOTROPIC映射模式下使用SetViewportExtEx()和SetWindowExtEx()更改。而且这两个函数在上面5种映射方式下无效。

可以使用Windows提供的两个函数DPtoLP和LPtoDP在设备坐标及逻辑坐标之间互相转换。

设备坐标系分为屏幕坐标系、窗口坐标系和客户区坐标系三种相互独立的坐标系。
1.屏幕坐标系以屏幕左上角为原点,一些与整个屏幕有关的函数均采用屏幕坐标,如GetCursorPos()、SetCursorPos()、CreateWindow()、MoveWindow()。弹出式菜单使用的也是屏幕坐标。
2.窗口坐标系以窗口左上角为坐标原点,它包括窗口标题栏、菜单栏和工具栏等范围。
3.客户区坐标系以窗口客户区左上角为原点,主要用于客户区的绘图输出和窗口消息的处理。鼠标消息的坐标参数使用客户区坐标,CDC类绘图成员函数使用与客户区坐标对应的逻辑坐标。

GDI映射模式总结

         
 图片 2

二、与映射模式有关的问题的解决
实际应用中,程序员会遇到一些与显示模式有关的问题。例如OLEServer中映射模式
的设置、如何减少逻辑坐标与设备坐标间相互转换的误差等。下面,笔者就讨论一下这两个
问题的解决方法。
1.OLEServer中映射模式的设置方法

rect =
CRect(0,0,1,1),当调用ClientToScreen(&rect),rect变成为:左上角(35,101),右上角(36,102),这是也有2个单位的border的作用。

封装GDI调色板,它保存着系统可用的色彩信息,是应用程序和彩色输出设备上下文的接口。

 

4.映射模式的种类

 

默认的映射模式使MM_TEXT,它使以象素为单位的。X轴向左为正,Y轴向下为正。默认的坐标原点在左上角。

好了,下面开始正文:

对于所有映射模式,Windows都用下面两个公式将窗口坐标转换成视口坐标:

映射方法(Mapping Mode)
 逻辑单位               坐标轴方向
MM_TEXT(默认方式)
 1 pixel                X轴正方向朝右,Y轴正方向朝下

对于可变比例的映射模式用户可以自己定义一个逻辑单位代表的大小,其大小可以任意。

首先,不要被这两个名字迷惑了,这两个坐标是跟映射有关的,跟屏幕坐标系,窗口坐标系,客户端坐标系是相对独立的两个知识。

使用GetDeviceCaps函数可得到当前设备的各种能力,其第一个参数nIndex指示要获取信息的类型。当nIndex为HORZSIZE和VERTSIZE时,可得到显示区域的宽度和高度;当nIndex
为HORZRES和VERTRES时,可得到每个水平和垂直方向的像素数即分辨率;当nIndex的值为LOGPIXELSX
和LOGPIXELSY时,可得到水平和垂直方向每逻辑英寸所含像素数。

(4).自定义映射模式

PAINTSTRUCT ps;这个结构就是为了填充无效区域的坐标等等,
这样BeginPaint就可以只画无效区域了,提高了效率。

  1. xWindow=(xViewport-xViewOrg)*(xWinExt/xViewExt)+xWinOrg
  2. yWindow=(yViewport-yViewOrg)*(yWinExt/yViewExt)+yWinOrg

CRect rect;

6、GDI内存泄露问题

(2)屏幕坐标,包括整个屏幕,屏幕的左上角为(0,0)。屏幕坐标用在WM_MOVE消息中(对于非子窗口)以及下面的Windows函数中:CreateWindow和MoveWindow(都对于非子窗口)、GetMessage、GetCursorPos、GetWindowRect、WindowFromPoint和SetBrushOrg中。用函数ClientToScreen和ScreenToClient可以将客户区域坐标转换成屏幕区域坐标,或反之。

     
映射模式确定了在绘制图形时所依据的坐标系,它定义了逻辑单位的实际大小、坐标增长方向,所有映射模式的坐标原点均在设备输出区域(如客户区或打印区)的左上角。此外,对于某些映射模式,用户还可以自定义窗口的长度和宽度,设置视图区的物理范围。
Windows定义了8种映射模式,见下表。
映射模式使得程序员可不必考虑输出设备的具体设备坐标系,而在一个统一的逻辑坐标系中进行图形的绘制。

它们默认的坐标原点都使在左上角。其区别在于每一个逻辑单位对应的物理大小不一样。

//定义坐标映射方式
WINGDIAPI int   WINAPI SetMapMode(HDC, int);
此API函数在MFC中封装为CDC::virtual int SetMapMode(int nMapMode);

由于WM_PAINT消息的优先级很低,这样,由于窗口对象不能及时收到WM_PAINT消息而影响用户对屏幕对象的视觉感觉。为弥补这个缺陷,程序员可以考虑使用函数UpdateWindows(),它在应用程序的消息队列中存在WM_PAINT消息的情况下,强使Windows立即向窗口对象发送WM_PAINT消息.

  1. xViewport = (xWindow – xWinOrg) * (xViewExt / xWinExt) + xViewOrg
  2. yViewport = (yWindow – yWinOrg) * (yViewExt / yWinExt) + yViewOrg

(3).映射模式

11、区域刷新技术,就是将客户区分为若干个区域(一般是矩形就行了,速度快),具体怎么分,分多少个没有定论,理论上,可能需要单独刷新的地方,就分为一个区域,每当这个区域需要刷新的时候,只对这个区域进行绘制,并且BitBlt到屏幕上。具体来说,每一个区域应该对应一个刷新函数(或者一个switch语句的某一个case项),这个函数执行GDI操作,当这个区域需要刷新的时候,调用这个函数将新的东西绘制到一个内存兼容DC上,然后让这个矩形失效,那么windows会在适当的时候,调用OnDraw(或类似的函数),在OnDraw里面,直接用BitBlt对无效区域(就是前面提到的那个矩形区域)进行贴图即可。

例如,要设置逻辑MM_TWIPS,函数SetWindowExt中的参数为应1440。

(2)坐标之间的相互转换
 编程时,有时需要根据当前的具体情况进行三种设备坐标之间或与逻辑坐标的相互转换。
1.MFC提供了两个函数CDC::DPtoLP()和CDC::
LPtoDP()用于设备坐标与逻辑坐标之间的相互转换。
2.MFC提供了两个函数CWnd::ScreenToClient()和CWnd::ClientToScreen()用于屏幕坐标与客户区坐标的相互转换。

UpdateWindow(
)的作用是使窗口立即重绘。调用Invalidate等函数后窗口不会立即重绘,这是由于WM_PAINT消息的优先级很低,它需要等消息队列中的其它消息发送完后才能被处理。调用UpdateWindow函数可使WM_PAINT被直接发送到目标窗口,从而导致窗口立即重绘。UpdateWindow只向窗体发送WM_PAINT消息,在发送之前判断GetUpdateRect(hWnd,NULL,TRUE)看有无可绘制的客户区域,如果没有,则不发送WM_PAINT。

SetMapMode(hDC, MM_ISOTROPIC); 
SetWindowExt(64, 64); 
SetViewportExt(hdc,GetDeviceCaps(hdc,LOGPIXELSX),GetDeviceCaps(hdc, LOGPIXELSY)); 

CRect rect;

饼形图:Pie

逻辑坐标是独立于设备的,它与设备点的大小无关。使用逻辑单位,是实现”所见即所得”的基础。当程序员在调用一个画线的GDI函数LineTo,画出25.4mm(1英寸)
长的线时,他并不需要考虑输出的是何种设备。若设备是VGA显示器,Windows自动将其转化为96个像素点;若设备是一个300dpi的激光打印机,Windows自动将其转化为300个像素点。

    在缺省的模式(MM_TEXT)下,逻辑坐标的方向和单位与设备坐标的方向和单位相同,也是以像素为单位来表示的,X轴向右为正,Y轴向下为正,坐标原点位于窗口的左上角。逻辑坐标和设备坐标即使在缺省模式下其数值也未必一致,除了在以下两种情况下:
  1.   窗口为非滚动窗口
  2.  
窗口为滚动窗口,但垂直滚动条位于滚动边框的最上端,水平滚动条位于最左端,但如果移动了滚动条这两种坐标就不一致了。

2)Create出来的dc要用DeleteDC来释放,Get到的要用ReleaseDC释放.

(3)全窗口坐标,包括一个程序的整个窗口,包括标题条、菜单、滚动条和窗口框,窗口的左上角为(0,0)。使用GetWindowDC得到的窗口设备环境,可以将逻辑单位转换成窗口坐标。

 

CRect rect;

GetClientRect(&rect);

dc.SetMapMode(MM_ISOTROPIC);

dc.SetWindowExt(500,500);//设置窗口范围,将窗口的逻辑尺寸高为500单位×500单位

dc.SetViewportExt(rect.Width(),rect.Height());//设置视口范围

dc.Ellipse(0,0,500,500);

“窗口”依赖于逻辑坐标,可以是像素点、毫米或程序员想要的其他尺度。

   
当绘制的图形需要随着窗口的大小改变而自动改变的时候,一般选择MM_ISOTROPIC和MM_ANISOTROPIC映射方式。它们的唯一区别就是前者的X轴和Y轴的逻辑单位的大小是相同的,单词“isotropic”就是各个方向相等的意思,此映射方式适合绘制圆或正方形。而实际应用中,常常给X轴和Y轴取不同的比例,这时候选择MM_ANISOTROPIC映射方式。单词“anisotropic”就是各个方向相异的意思。

12、元文件

(作者地址:清华大学科技产业楼401室,100084)

View调用GetWindwoRect(&rect),
得到的坐标是:左上角(33,99),右下角(1040,524),这是View相对于屏幕的坐标,当调用ScreenToClient(&rect),rect变成为:左上角(-2,-2),右上角(1007,423),-2,-2是client和window之间的间隔差–border。

传送给CDC输出函数的是逻辑坐标值。

开发OLEServer应用程序时,如果程序员直接调用SetMapMode函数将映射模式设置成度量映射方式中的一种后,在Windows95/98上程序会正常运行,但在WindowsNT上对象显示的大小比边框小。经过笔者研究后,发现WindowsNT上OLEServer应使用基于逻辑英寸的映射方式。在讨论如何设置基于逻辑英寸的映射方式前,我们先介绍一下逻辑英寸的概念。

GetWindowRect(&rect);

固定比例映射模式

Windows应用程序绘制图形时使用的是一种逻辑单位,每个逻辑单位的大小由映射模式决定,
这个逻辑单位既可以与设备单位(屏幕或打印机上的一个像素点)相同,也可以是一种物理单
位(如毫米),还可以是用户自定义的一种单位。在Windows应用程序中,只要与输出有关系,都
要使用映射模式。本文的目的是帮助读者了解映射模式的一些基本知识,并对在使用中经常
出现的一些问题提出解决方案。

ScreentoClient(&rect);

(16)矩形:user32.dll提供的处理RECT结构的函数。

上述映射模式中又可分成以下3类:

Frame调用GetWindowRect(&rect),得到的坐标是:左上角(25,25),右下角(1050,551),这是Frame相对于屏幕的坐标,与View的坐标有差别是因为Frame还有菜单栏,工具栏。

这就是为什么双缓冲是GDI编程中最为基本的技巧了。这样不管你的绘制有多少的复杂,屏幕不会再有闪烁,绘制的复杂只会占用CPU多一点而已。

在介绍了逻辑英寸的知识以后,很容易将OLEServer设置为基于逻辑英寸的映射模式。如果程序员仅仅调用SetMapMode(hdc,MM_LOENGLISH)来设置映射模式,当前的映射模式为物理英寸,而不是逻辑英寸。设置逻辑英寸必须自定义窗口和视口的范围,使xViewExt/xWinExt
=0.01逻辑英寸中水平像素的点数,当xViewExt=LOGPIXELSX,xWinExt=100时,其比值正好满足上述要求。

PatBlt 使用当前被选入设备环境的画刷绘制给定的矩形

Windows定义了表1所列出的8种映射方式。

逻辑字体选到设备上下文后,可用下面几个函数来查询它所影射的物理字体的细节,及当前物理字体实例的度量信息。

综上所述,如果我们能够根据映射模式值的特点,逻辑坐标和设备坐标都取经简化的窗口和视口范围值的倍数,则逻辑坐标和设备坐标间的转化将没有误差。

ExtFloodFill 用当前画刷填充显示平面的某一区域

  1. MM_TEXT映射模式这种映射模式被称为”文本”映射方式,不是因为它对
    于文本最合适,而是轴的方向与读文本的方向一致。Windows提供了函数SetViewportOrg和SetWindowOrg
    用来设置视口和窗口的原点。缺省的窗口原点和视口原点均为(0,0),可以改变;缺省的窗
    口范围和视口范围均为(1,1),不可改变。
  2. 度量映射方式MM_LOMETRIC、MM_HIMETRIC、MM_LOENGLISH、MM_HIENGLISH和MM_TWIPS
    将1个逻辑单位映射为固定的实际单位,其中1twip等于0.0176mm(1/1440英寸)。其他映射模式对应的物理单位参见表1。设置了映射模式以后,Windows自动设置了窗口及视口的范围,范围本身的值并不重要,但范围比是一个固定的值,对于MM_LOMETRIC,Windows计算范围比xViewExt/xWinExt=0.1mm中水平像素的点数。
  3. 自定义映射模式MM_ISOTROPIC和MM_ANISOTROPIC两种映射模式允许程序员设置自己的窗口和视口范围。MM_ISOTROPIC和MM_ANISOTROPIC的区别是所设置的x轴和y轴的的范围必须相同,而MM_ANISOTROPIC所设置的x轴和y轴的的范围可以不同。isotropi的意思是”
    在所有方向相同”,anisotropic的意思正相反。自定义映射模式中窗口和视口的原点和范围都可以改变,程序员可以设置自己需要的映射模式。函数SetWindowExt和SetViewportExt
    用于改变窗口和视口的范围。下面的代码将1个逻辑单位映射成0.396mm(1/64英寸)。

前者中X方向和Y方向具有同一个缩放比例因子,而后者两个方向可以单独缩放;

“视口”依赖于设备坐标(像素点)。通常,视口和客户区域等同。但是,如果程序员用GetWindowDC或CreateDC获取了一个设备环境,则视口也可以指全窗口坐标或屏幕坐标。点(0,0)是客户区域的左上角。x的值向右增加,y的值向上增加。

OPENGL。

2.设备坐标

绘图模式与SetROP2

这两个公式使用了分别指定窗口和视口原点的点:(xWinOrg,yWinOrg)是逻辑坐标的窗口原点;(xViewOrg,yViewOrg)是设备坐标的视口原点。在缺省的设备环境中,这两个点均设置为(0,0),但它们可以改变。此公式意味着,逻辑点(xWinOrg,yWinOrg)总被映射为设备点(xViewOrg,yViewOrg)。

默认的映射模式

表2

GDI坐标变换详细总结

Windows将GDI函数中指定的逻辑坐标映射为设备坐标,在所有的设备坐标系统中,单位以像素点为准,水平值从左到右增大,垂直值从上到下增大。

void * pKernel;

映 射 方 式 逻 辑 单 位 X 轴 增 加 Y 轴 增 加 毫 米
MM_TEXT 像 素 点 与 设 备 有 关
MM_LOMETRIC 0. 1mm 0.1
MM_HIMETRIC 0. 01mm 0.01
MM_LOENGLISH 0. 01英寸 0.254
MM_HIENGLISH 0.001英寸 0.0254
MM_TWIPS 1/1440英寸 0.0176
MM_ISOTROPIC 任 意(x=y) 可 选 可 选 可 设
MM_ANISOTROPIC 任 意(x!=y) 可 选 可 选 可 设

UpdateWindow直接发送一个WM_PAINT消息,其无效区范围就是消息队列中WM_PAINT消息(最多只有一条)的无效区。

创建画笔的第二种方法是构造
一个没有初始化的CPen对象并调用CPen::CreatePen:

简单地说,映射模式是设备描述表的属性,用于确定从逻辑坐标值到设备坐标值的转换方式。

GDI对象表

BitBlt 把由一个矩形中的像素组成的位块从源设备环境转移到目标设备环境中

如果BitBlt不会占满整个客户区,那么很抱歉,有点麻烦了,你要生成一个刚好不包括BitBlt显示区域的区域,然后对DC执行SelectClipRgn函数,请在MSDN里面搜索“Region
Functions”,里面的函数大多你可能都用得上,主要是创建区域,操作区域(比如求AND,求OR,求DIFF等)。

默认映射模式

5、SetTextColor、SetBkColor函数设置新颜色之后,使用完成之后,需要再把旧颜色设置回去(通过SetTextColor、SetBkColor)。此种操作跟SelectObject类似。

13、打印

unsigned short nCount;

(3)剪裁:处理设备上下文可绘制区域的函数。

CBrush brush;

brush.CreateSolidBrush(RGB(255,0,0));

4)关于98下使用CreateCompatibleBitmap
按照msdn的说法,创建出来的size不能超过16m.实际情况是这样吗?非也~!从我自己做的测试结果来看(win98se-sc),这个值在2044*2043和2044*2044之间,然而,后来在另外一个98系统上这个值也不行,后来我干脆把上限给成了2000*2000.很幸运,到现在还没有出问题,但我不能保证这个数字就是正确的.还有一点,假如宽或高有一个超过32768,哪怕另外一个值是1,也会创建失败,有兴趣的可以自己做个测试.如果要想保证这个函数在98下永远成功,可以试试下面的代码:

MM_ISOTROPIC和MM_ANISOTROPIC映射模式的区别

AbortDoc

AbortPath

AddFontMemResourceEx

AddFontResourceA

AddFontResourceExA

AddFontResourceExW

AddFontResourceTracking

AddFontResourceW

AngleArc

AnimatePalette

AnyLinkedFonts

Arc

ArcTo

具体查看:http://www.cnblogs.com/del/archive/2008/03/11/1101291.html

可编程映射模式

SetPixel:在指定坐标处按指定色彩画一点。

(6)pUser指向用户模式数据结构。

unsigned short nProcess;

FillRect:用指定颜色填充矩形且不画边线。

DrawString(const WCHAR *string, INT length, const Font *font, const Rect &layoutRect, const StringFormat *stringFormat, const Brush *brush); 
DrawString(const WCHAR *string, INT length, const Font *font, const PointF &origin, const Brush *brush); 
DrawString(const WCHAR *string, INT length, const Font *font, const PointF &origin, const StringFormat *stringFormat, const Brush *brush);

图案画刷用位图来填充图形。

为什么WINDOWS要提出无效区域的概念呢?这是为了加速。因为BeginPaint和EndPaint用到的设备描述符只会在当前的无效区域内绘画,在有效区域内的绘画会自动被过滤,大家都知道,WIN
GDI的绘画速度是比较慢的,所以能节省一个象素就节省一个,不用吝啬,这样可以有效加快绘画速度。
可见BeginPaint、EndPaint是比较“被动”的,只在窗口新建时和被摧残时才重画。
而GetDC用于主动绘制,只要你指到哪,它就打到哪。它不加判断就都画上去,无效区域跟它没关系。对话框没被覆盖没被摧残,它很健康,系统没要求它重画,但开发者有些情况下需要它主动重画:比如一个定时换外观的窗口,这时候就要在WM_TIMER处理代码用GetDC。这时候再用BeginPaint、EndPaint的话,会因为无效区域为空,所有绘画操作都将被过滤掉。

int StartDoc(HDC hDC,CONST DOCINFO *lpdi);//告诉GDI启动新打印任务

int StartPage(HDC hDC);//打印任务新页开始

int EndPage(HDC hDC);//打印任务一页结束

int EndDoc(HDC hDC);//结束StartDoc开始的打印任务

HDC ResetDC(HDC hDC,const DEVMODE *lpInitData);//改变打印设备上下文设置信息

int AbortDoc(HDC hDC);//用于非正常结束打印任务

int SetAbortProc(HDC hdc,ABORTPROC lpAbortProc);//设置回调例程,GDI周期性用它检查还是放弃打印任务

(10)元文件:处理Windows格式的元文件或增强型元文件的生成和回放的函数。

(12)画图和绘图:负责绘图消息管理和窗口已绘图区域的函数。其中一些函数实际上是从uer32.dll导出的。

三、设备描述表(DC)

LONG SetBitmapBits(HBITMAP hBmp,LONG cBytes,LPVOID lpBits);

32位的GDI句柄由8位未知的高位、1位堆对象标记、7位对象类型信息和高4位是0的16位索引组成。借助7位对象类型信息,可以确定设备上下文、区域、位图、调色板、字体、画刷、加强型图元文件、画笔和扩展画笔。

CreateDIBitmap 从一个设备无关位图中创建设备无关位图, 同时设置该位图的位

圆角矩形:RoundRect

字体:CreateFont、CreateFontIndirect和CreateFontIndirectEx创建GDI逻辑字体对像,将句柄返回给调用者。通过GDI对象句柄,你可以用GetObject得到定义逻辑字体的LOGFONT或ENUMLOGFONTEX结构。与其他GDI对象一样,不需要逻辑字体对象时,应用DeleteObject删除掉。

获取与窗口关联的设备上下文,应用程序不能使用CreateDC创建与一个窗口相关的设备上下文,但是WIN32
API提供了几个获取与窗口相关的设备上下文的函数,包括:

SetDIBitsToDevice
使用从一个特定的设备无关位图中得到的颜色数据来设置与目标环境关联的设备中特定矩形的像素

GDI对象类型列表:

MFC提供了获得和设置这些属性的函数,我们可以用这些函数方便地改变画图时的默认属性。

注意:

BeginPaint和GetDC区别

11、其它绘图函数

还有一些没有文档记载的函数。有一些GDI函数在DDK中说明,还一些没有文档记载但系统DLL使用的函数,另处还有一些函数则没有用过。下面是这些函数的粗略分类:

4、GDI对象类型

在默认情况下,则Rectangle,Ellipse以及其它CDC函数画出的封闭图形填充着白色像素点。通过创建GDI画刷并在画图之前将它选入设备描述表,可以改变图形的填充颜色。

6、输出文本

2、
库存对象(StcokObject)是由操作系统预先创建的,并为所有在系统中运行的过程共享。在应用程序使用完库存对象之后,没有必要删除他们的句柄。但是调用函数DeleteObject来处理库存对象句柄是十分安全的,当函数DeleteObject没有做任何事情时,它将返回TRUE值。

1、 GDI32.DLL导出的函数

二、三种图形输出类型
应用程序可以使用GDI创建三种类型的图形输出:矢量输出、位图图形输出和文本输出。
矢量图形输出
矢量图形输出指的是创建线条和填充图形,包括点、直线、曲线、多边形、扇形和矩形的绘制。
位图输出
位图图形的输出是指位图图形函数对以位图形式存储的数据进行操作,它包括各种位图和图标的输出。
在屏幕上表现为对若干行和列的像素的操作,在打印机上则是若干行和列的点阵的输出。
位图图形输出的优点是速度很快,它是直接从内存到显存的拷贝操作。缺点是需要额外的内存空间。
文本输出
与DOS字符方式下的输出不同,Windows是按图形方式输出的。
这样,在输出文本时,必须以逻辑坐标为单位计算文本的输出位置,而不是象DOS下以文本行为单位输出文本。这比DOS下的文本输出要难一些。
按图形方式输出文本给文本输出带来很大的灵活性。用户可以通过调用各种GDI函数,制造出各种文本输出效果,包括加粗、斜体、设置颜色等。
Windows还提供了一种TrueType(写真字体)。TrueType字体用一组直线和曲线命令及一些参数来描述字体的轮廓。Windows可以通过参数来调整直线的长度和曲线的形状,从而实现对字体的自由缩放。

SetDIBColorTable 设置当前DIB位图颜色表中某一范围的RGB颜色值

所对用的逻辑单位分别为0.1毫米,0.01毫米,0.01英寸,0.001英寸,1/1440英寸(0.0007英寸)。

有了这个字段,GDI就会很容易地检查当前进程标识符是否和GDI对象的nProcess字段一致,目的是强制对象句柄不能在另一个进程中访问的规则。

4、在DIB和DDB间拷贝位图,GDI函数:SetDIBits/GetDIBits;

(5)坐标和变换:处理映射模式、设备坐标映射逻辑和通用变换矩阵的函数。

固定比例映射模式

unsigned short nType;

(5)nType:内部对象类型:nType的低字节通常和HGDIOBJ中的是7位类型信息相同,高位字节通常是零。

(4)nUpper:再次检查句柄:我们发现GDI对象表入口的nUpper字段是4字节GDI对象句柄的高两个字节的完全拷贝——对GDI对象句柄进行错误检查是低成本的冗余校验。

以后所有的GDI操作,比如LineTo等,都只对hFrceDC,当要刷新的时候:

矩形:Rectangle

CreateBitmap 使用宽度、调试和颜色格式创建位图

1、GDI画笔和CPen类

1)Create出来的gdi对象,一定要用DeleteObject来释放,释放顺序是先Create的后释放,后Create的先释放.
这里的Create指的是以它为开头的gdi函数,比如,CreateDIBitmap,CreateFont等等,最后都要调用DeleteObject来释放.

GDI使用一种简单的机制保证在窗口中画图的各程序遵循这个规则。这种机制即为设备描述表(DC);当Windows程序在屏幕、打印机或其它设备上画图时,它并不是将像素直接输出到设备上,而是将图绘制到由设备描述表表示的逻辑意义上的”显示平面”上去。设备描述表是深寓于Windows中的一种数据结构,它包含GDI需要的所有关于显示平面情况的描述字段,包括相连的物理设备和各种各样的状态信息。

GetStretchBltMode 获取当前的伸缩模式

ExtFloodFill:用给定的颜色,利用当前刷子填充表面被一个边线包围的区域,用户可以有选择地填充一个由指定颜色组成的区域。

10、封闭图形

GDI+中的文本输出只有一个函数:DrawString

如果要改变画线方式,则需创建一个GDI画笔,并由CDC::SelectOjbect将它选设备描述表,MFC用类CPen表示GDI画笔。

InvalidateRect使窗口的指定区域无效,Invalidate使窗口全部区域无效,Validate使窗口全部区域有效。如果窗口有区域无效,则Windows发WM_PAINT,参数中指定无效区域的最大矩形,程序中响应此消息,用BeginPaint使无效区域为剪裁区,后面的绘画活动不会超出这个剪裁区,归后由EndPaint使无效区域变为有效,这时WM_PAINT被清除。MFC为窗口类提供了WM_PAINT的消息处理。MFC为窗口类提供了WM_PAINT的消息处理函数OnPaint,OnPaint负责重绘窗口。视图类有一些例外,在视图类的OnPaint函数中调用了OnDraw函数,实际的重绘工作由OnDraw来完成。参数bErase为TRUE时,重绘区域内的背景将被擦除,否则,背景将保持不变。

StretchDIBits 把DIB位图中矩形区域的像素颜色数据复制到确定的目标矩形中

(14)画笔:处理直线绘制属性的函数。

封闭的多边形:Polygon

(9)直线和曲线:绘制直线、椭圆曲线和贝赛尔曲线的函数。

2、GDI画刷和CBrush类

FloodFill:用给定的颜色,利用当前所选的刷子填充显示的底面被一个边线所包围的区域,如多边形区域的填充。

3、
SetDIBitsToDevice函数与StretchDIBits函数都是显示DIB图象,二者区别是SetDIBitsToDevice函数需要的内存很少;SetDIBitsToDevice不处理拉伸,需要自己写拉伸算法;StretchDIBits可以通过SetStretchBltMode/GetStretchBltMode来控制每个设备上下文的拉伸模式。

创建画笔的方法

原文链接地址:

HDC hDC = ::GetDC(m_hWnd);

hFrceDC = CreateCompatibleDC(hDC); //内存兼容DC

hFrceBmp = CreateCompatibleBitmap(hDC, WinWidth, WinHeight); //内存兼容位图

SelectObject(hFrceDC, hFrceBmp); //选入内存兼容DC

::ReleaseDC(m_hWnd, hDC);

(15)打印和打印池:负责将GDI绘图命令发送到硬拷贝设备(如行式打印机和绘图仪)并平滑地管理这些任务的。打印池函数是由Win32打印池提供的,包括几个系统提供的DLL和销售自定义的模块。

Invalidate在消息队列中加入一条WM_PAINT消息,其无效区为整个客户区。

   设备上下文函数(如GetDC、CreateDC、DeleteDC)、
画线函数(如LineTo、Polyline、Arc)、填充画图函数(如Ellipse、FillRect、Pie)、画图属性函数(如SetBkColor、SetBkMode、SetTextColor)、文本、字体函数(如TextOut、GetFontData)、位图函数(如SetPixel、BitBlt、StretchBlt)、坐标函数(如DPtoLP、LPtoDP、ScreenToClient、ClientToScreen)、映射函数(如SetMapMode、SetWindowExtEx、SetViewportExtEx)、元文件函数(如PlayMetaFile、SetWinMetaFileBits)、区域函数(如FillRgn、FrameRgn、InvertRgn)、路径函数(如BeginPath、EndPath、StrokeAndFillPath)、裁剪函数(如SelectClipRgn、SelectClipPath)等。

存取原始的DDB像素阵列:LONG GetBitmapBits(HBITMAP hBmp,LONG
cbBuffer,LPVOID
lpvBits);如何知道需要分配多大的缓冲区?在设置大小为0和缓冲区指针为NULL的情况下,GetBitmapBits函数就返回所需要的缓冲区大小。

图形设备接口(GDI)是一个可执行程序,它接受Windows应用程序的绘图请求(表现为GDI函数调用),并将它们传给相应的设备驱动程序,完成特定于硬件的输出,象打印机输出和屏幕输出。GDI负责Windows的所有图形输出,包括屏幕上输出像素、在打印机上输出硬拷贝以及绘制Windows用户界面。也就是Windows的图形编程。

}GdiTableCell;

线条大约有七种风格,如下:

CreateDIBSection
创建应用程序能够直接写入的设备无关位图,返回一个指向该位图存入位置的指针

14、设备描述表属性修改/设置函数

即是由用户决定一个逻辑单位等于多少个设备单位(cm,mm,m等),而不是由Windows决定。

3)确保释放DC的时候DC中的各gdi对象都不是你自己创建的;确保个gdi对象在释放的时候不被任何dc选中使用.
假如我们要使用gdi函数画图,正确的步骤应该如下:
a.创建一个内存兼容dc(CreateCompatibleDC)
b.创建一个内存兼容bitmap(CreateCompatibleBitmap)
c.关联创建的内存兼容dc和bitmap(SelectObject)
d.画图
e.BitBlt到目的dc上
f.断开内存兼容dc和bitmap关联(SelectObject)
g.销毁内存兼容bitmap
h.销毁内存兼容dc
由于SelectObject在选入一个新的gdi对象的时候会返回一个原来的gdi对象(假如成功的话),所以需要在步骤c的时候保存返回值,在步骤f的时候当作入口参数使用.还有,步骤g和步骤h实际上顺序可以随意,因为他们两个此刻已经没有关系了,但是为了结构清晰,我建议按照”先Create的后释放,后Create的先释放”的原则进行.
关于步骤f,可能会有争议,因为即使省略这一步,步骤g和步骤h看起来照样可以返回一个成功的值.但实际上可能并没有执行成功,至少boundschecker会报告有错,错误信息大致是说,在释放dc的时候还包含有非默认的gdi对象,在释放gdi对象的时候又说这个gdi对象还被一个dc在使用.所以,我建议保留步骤f.

PS_DASH 指定一支绘制虚线的画笔

PS_DASHDOT 指定一支虚线和点交替的画笔

PS_DASHDOTDOT 指定一支虚线和两点交替的画笔

PS_DOT 指定一支点线画笔

PS_INSIDEFRAME 指定一支带有限定形状的画笔, 线的厚度不能延伸出此形状的边界

PS_NULL 指定一支空画笔

PS_SOLID 创建一支实线画笔

GDI绘图之位图处理函数

Arc(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4);

也可以让这个大小随环境改变而改变。有MM_ISOTROPIC,MM_ANISOTROPIC这两种映射模式。

封装了GDI字体对象,用户可以建立一种GDI字体,并使用CFont的成员函数来访问它。字体包括字样(FontFamily)
、风格、尺寸三个主要属性;字体是指宋体、黑体等;风格指字体的粗细、是否倾斜等;FontStyleStrikeout是指删除线风格;可以使用GDI中的Font类,直接构造一个字体对象,如:Font
font(&fontFamily, 12, FontStyleRegular,
UnitPoint);为了与原来的GDI字体兼容,Font的构造函数还有另外一种形式:Font(HDC
hdc, const LOGFONTW
*logfont);hdc是一个窗口的设备环境句柄,logfont是指向LOGFONT(逻辑字体)数据结构的指针。

编程中注意问题:

(2)nCount 是一个部分选择计数器:在Windows
2000下,nCount总是零,就是说未使用。但在Windows NT
4.0中,某些GDI对象用了它。为了理解nCount的意义,我们试着将对象句柄选入和取消一个或多个设备上下文中,观察选入和取消是否能根据nCount值的变化而成功。

主要是建立内存兼容DC和内存兼容位图,如下:

(17)区域:负责用区域GDI对象描述一个点集的函数,并对该点集进行操作。

CBrush brush;

brush.CreateHatchBrush(HS_DIAGCROSS,RGB(255,0,0));

HS_DIAGCROSS是可供选择的六种阴影线样式之一,其它样式请参见MSDN
void CST_CurveCtrl::OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid) 

{

BitBlt(pdc->m_hDC, rcInvalid.left, rcInvalid.top, rcInvalid.Width(), rcInvalid.Height(), hFrceDC, rcInvalid.left, rcInvalid.top, SRCCOPY);

...

}

3、 GDI对象句柄

{

GDI将像素点输出到逻辑平面上时,它不只是简单地输出像素点颜色。相反,它能过一系列的布尔运算将输出像素点的颜色和目标位置上像素点的颜色合成一起。它所使用的逻辑关系由设备描述表当前的绘图模式确定。使用CDC::SetROP2(Set
Raster Operation
To)可更改绘图模式。默认绘图模式为R2_COPYPEN,它将将像素点复制到显示平面上。

BeginPaint可以使无效区域变有效,GetDC不改变区域属性,无效的还是无效,有效的依然是有效。
BeginPaint在WM_PAINT消息里使用,GetDC则可以在所有的消息中使用,一般是非WM_PAINT消息。

DDB显示,GDI函数:BitBlt,StretchBlt

case WM_LBUTTONDOWN:
               MessageBox(hwnd,”mouse clicked”,”message”,0);
               HDC hdc;
              hdc=GetDC(hwnd);
              TextOut(hdc,0,50,”程序员之家”,strlen(“程序员之家”));
             ReleaseDC(hwnd,hdc);
       break;
case WM_PAINT:
           HDC hDC;
PAINTSTRUCT ps;//看出区别了吗?
          hDC=BeginPaint(hwnd,&ps);
         
TextOut(hDC,0,0,””));
        EndPaint(hwnd,&ps);
   break;

CBrush brush(HS_DIAGCROSS,RGB(255,0,0));//阴影索引,COLORREF值

CreateBitmapIndirect 同上,只是参数不一样

(11)多显示监视器:允许在一个系统中使用多个显示监视器的函数。这些函数实际上是从uer32.dll导出的。

CPen pen;

pen.CreatePen(PS_SOLID,1,RGB(255,0,0));

使用GetDCEx可以代替其他的函数。例如,GetDCEx(hWnd,NULL,DCX_WIND|DCX_NORESETATTRS)可以轻松地替代GetWindowDC(hWnd),而GetDCEx(hWnd,NULL,DCX_NORESETATTRS)可以替代GetDC(hWnd)。

设备坐标值是指窗口中相应的像素点位置。

(8)字体和文本:在系统中安装和枚举字体,并用它们绘制文本字符串的函数。

dc.SetMapMode(MM_LOENGLISH);//改变映射模式

TextOut:文字输出。

(6)设备上下文:创建设备上下文,查询、设置其属性,及选择GDI对象的函数。

两者的不同在于前者要求X轴和Y轴的度量单位必须相同,而后者没有这样的限制。

TabbedTextOut:象TextOut一样显示正文,可以使用制表键Tab。

创建画笔的第三种方法是构造一个没有初始化的CPen对象,向LOGPEN结构中填充描述画笔特性的参数,然后调用

用户模式打印机驱动程序。

根据微软的文档,进程终止时,由该进程创建的所有GDI对象会被释放。如果你想知道这是怎样实现的,我们现在有一点线索了。GDI只需搜索GDI对象表并删除有指定进程标识符的对象。

GetDIBits 获取位图的位并使用特定格式把它们复制到一个缓冲区中

SetTextColor(int nColor); 设置前景色。

float factor = 10.f;
while(!bitmap.CreateCompatibleBitmap(&dc ,nWidth*factor ,nHeight*factor))
{
   factor -= 0.01f;  
}

GDI的功能太多了,所以我们需要一种办法对Win32 GDI
API的函数分类,以便理解GDI的结构,MSDN库将GDI
API分成17个领域,清楚地描述了GDI的功能。

HDC GetWindowDC(HWND hWnd)

MaskBlt 使用特定的掩模和光栅操作合并源位图和目的位图的颜色数据

LoadBitmap 从一个模板或可执行文件中装入确定的位图资源

(如果想忽略背景色,可将背景设置为”Transparent”,dc.SetBkMode(TRANSPARENT);)

brush用来指定一个画刷,这个画刷既可以是SolidBrush和HatchBrush,也可以是TextureBrush(纹理画刷),甚至是渐变画刷。
StringFormat类来控制这些格式属性,文本的格式属性通常包括对齐方式、字符间隔以及文本调整等。

3、字体与CFont

四、图形对象

例如:

EUDC。

在用阴影线画刷填充时,除非用CDC::SetBkColor改变设备描述表的当前背景色,或用CDC::SetBkMode把背景模式OPAQUE改成TRANSPARENT,禁止背景填充,否则Windows就以默认的背景色(白色)填充阴影色线间的空白处。

阴影线画刷采用预先定义好的交叉线图案填充图形。

GetTextMetrics(); 返回当前使用字体的尺寸描述,用于进行字体大小计算。

用于记录和回放GDI函数调用。首先通过CMetaFileDC:Create来创建新的对象,如果有文件名,则保存到文件内,如果没有则建立在内存中。完毕后,用Close()关闭。
可以调用PlayMetaFile()来回放元文件。还可以用CopyMetaFile()将文件存盘。当结束元文件的时候,用DeleteMetaFile()从内存中删除。
另外有增强型元文件。

可变比例映射模式(可编程映射模式)

当使用CDC输出函数在屏幕上画图时,输出的某些特性并没有在函数调用过程中规定(采用系统默认的属性画图),但我们可以通过设备描述表自身获得或得新设置属性。

创建单色画刷的方法类似于创建画笔,如下所示:

单色画刷填充的单色。

7、画点

并且一个逻辑单位对应于设备坐标下的一个象素

创建带阴影线画刷

固定比例的映射模式有MM_LOMETRIC、MM_HIMETRIC、MM_LOENGLISH、MM_HIENGLISH、MM_TWIPS种。

CBrush brush(RGB(255,0,0));

图片 3

(4)颜色:调色板管理。

MM_TEXT为默认映射模式,其原点在窗口的左上角,X轴的正向向右,Y轴的正向向下,

(1)位图:处理创建、绘制设备相关位图(DDB)、设备无关位图(DIB)、DIB段、像素和区域填充的函数。

DrawText:在指定矩形种显示正文,可以使用制表键Tab。在格式化矩形时调整正文左对齐、右对齐或居中;还可以在一个词中断开以适应矩形边界。

MoveToEx将直线起点移动到指定坐标处,LineTo从起点开始画直线到终点处。使用的线型由当前所用画笔指定。

一、GDI体系结构

MM_ISOTROPIC和MM_ANISOTROPIC映射模式最常用于根据窗口尺寸按比例自动调节画图的输出大小的场合。

所以被称之为可编程映射模式;

7、InvalidateRec、Invalidate、Validate、UpdateWindow

图形对象类包括CGdiObject、画笔、刷子、字体、位图、调色板、区域等。
CGdiObject是图形对象类的基类,但该类不能直接为应用程序所使用。
要使用GDI对象,必须使用它的派生类:画笔、刷子、字体、位图、区域等等。
使用图形对象要注意两点:
a.同其他MFC对象一样,GDI对象的创建也要分为两步:第一步,是定义一个GDI绘图对象类的实例;第二步调用该对象的创建方法真正创建对象。
b.创建对象:使用该对象,首先要调用CDC::SelectObject()将它选入到设备上下文中,同时保存原来的设置到一个GDI对象指针比如说pOldObject中。在使用完后,再用SelectObject(pOldObject)恢复原来的设置。

映射模式

MM_ISOTROPIC和MM_ANISOTROPIC是由用户决定从逻辑坐标值转换成设备坐标值的方式,

图片 4

typedef struct

8、画线

CMetaFileDC    dc;
BOOL bCreate=dc.CeeateEnblanced(pDC,”metaest,wmf/0”,0,vectosd/0 this is l/0/0”);
if(!bCreated) return;
(具体的绘制内容……)
//关闭文件,并返回文件句柄
HENHMETAFILE    hemf=dc.CloseEnhanced();
//播放文件
pDC->SaveDC();
pDC->SetMapMode();
pDC->PlayMetaFile(hemf,&rc);
pDC->RestoreDC(-1);
//当文件不再需要
if(hemf)
    ::DeleteEnhMetaFile(hemf);

(3)nProcess使得GDI句柄绑定到进程:如果程序想使用另一个进程的GDI对象句柄,Win32
API调用一般会失败。GdiTableCell中的nProcess字段就是这种现象背后的原因。对于库存(stock)对象,如GetStockObject(BLACK_PEN),nProcess被置为零。对于用户进程创建的其他
GDI对象,nProcess是创建进程的进程标识符。

GetBitmapDimensionEx 获取一个位图的大小

MFC的CBrush类封装了GDI画刷。

CPen::CreatePenIndirect生成画笔。

CPen pen;

LOGPEN lp;

lp.lognStyle = PS_SOLID;//样式

lp.lopnWidth.x = 1;//宽度

lp.lopnColor = RGB(255,0,0);//颜色

pen.CreatePenIndirect(&lp);

8、双缓冲绘图

注:窗口的尺寸以逻辑单位计算,视口的尺寸以设备单位或像素计算。

12、调用CreateCompatibleBitmap创建DDB位图。此函数创建位图时用的是系统内核的分页内存,这是稀有资源。CreateDIBSection的好处是,它使用虚拟内存创建位图。这样运行程序的实例数就只限于pagefile和磁盘空间大小了。不过CreateDIBSection比CreateCompatibleBitmap要难调用一些。

最常用来定义设备描述表属性的CDC函数是SelectObject。

dc.Rectangle(0,0,200,100);//默认模式下画图

HDC GetDC(HWND hWnd)

5、GDI函数大致分类

StretchBlt
把位图从源矩形区域复制到目标矩形区域,并在需要时对位图进行拉伸或收缩

在Windows环境中,各程序的输出必须限制在自己的窗口中。

GetDIBColorTable 获取当前DIB位图颜色表中某一范围的RGB颜色值

(2)画刷:处理创建、修改GDI画刷对象的函数。

4、位图

BOOL CXXX::OnEraseBkgnd(CDC* pDC)

{

return TRUE;

}

5、调色板

封装了GDI位图,它提供成员函数装载和操作位图。GDI支持三种位图类型:设备无关位图、设备相关位图、DIB段。

效果很明显,当调用Invalidate之后,屏幕不一定马上更新,因为WM_PAINT消息不一定在队列头部,而调用UpdateWindow会使WM_PAINT消息马上执行的,绕过了消息队列。

画刷有三种基本类型:单色、带阴影和带图案。

编写GDI程序时运行多次后出现异常,除了众所周知的内存泄露以外,gdi资源泄露也是一个很直接的原因。预防gdi资源泄露措施。

SetBitmapDimensionEx 为位图指定预定的大小

是否刷新背景很重要,有些新手将上面的双缓冲用到对话框之上,然后告诉我,还是闪烁!那是因为刷了背景的原因,因为用BitBlt绘制的东西,在windows看来,它不是一个窗口,它不像一个按钮,因为按钮是一个窗口,所以windows在刷背景的时候,会从刷新区域里面减掉按钮所占的区域,那么回到正题,在对话框上BitBlt的时候,如果位图显示占满了对话框的整个客户区,这就简单不过了,直接响应WM_ERASEBKGND消息,然后返回TRUE:

CDC、CclientDC、CpaintDC、CwindowDC、CmetaFileDC含义与区别
CDC是设备上下文类的基类,除了一般的窗口显示外,还用于基于桌面的全屏幕绘制和非屏幕显示的打印机输出。CDC类封装了所有图形输出函数,包括矢量、位图和文本输出。
CClientDC(客户区设备上下文)用于客户区的输出,它在构造函数中封装了GetDC(),在析构函数中封装了ReleaseDC()函数。一般在响应非窗口重画消息(如键盘输入时绘制文本、鼠标绘图)绘图时要用到它。
CClientDC dc(this);//this一般指向本窗口或当前活动视图
dc.TextOut(10,10,str,str.GetLength());
CPaintDC用于响应窗口重绘消息(WM_PAINT)时的绘图输出。CPaintDC在构造函数中调用BeginPaint()取得设备上下文,在析构函数中调用EndPaint()释放设备上下文。EndPaint()除了释放设备上下文外,还负责从消息队列中清除WM_PAINT消息。因此,在处理窗口重画时,必须使用CPaintDC,否则WM_PAINT消息无法从消息队列中清除,将引起不断的窗口重画。CPaintDC也只能用在WM_PAINT消息处理之中。
CWindowDC用于窗口客户区和非客户区(包括窗口边框、标题栏、控制按钮等)的绘制。除非要自己绘制窗口边框和按钮(如一些CD播放程序等),否则一般不用它。
CMetaFileDC专门用于图元文件的绘制。图元文件记录一组GDI命令,可以通过这一组GDI命令重建图形输出。使用CMetaFileDC时,所有的图形输出命令会自动记录到一个与CMetaFileDC相关的图元文件中。

打印相关函数

SetStretchBltMode 在确定的设备环境中设置位图伸缩模式

10、GDI函数调用次数很重要,用上双缓冲和消除刷新背景之后,闪烁的问题可以认为是解决了,下面我们将讨论减少CPU使用率的问题。GDI操作是比较浪费CPU资源的,比如频率的调用GDI函数,势必浪费CPU资源,这其中又特别是字符的打印操作,为此GDI提供PolyTextOut函数,调用它一次,可以输出任意多条的字符,每一条都是独立设置输出位置的,这个函数的使用场合我举个例子,比如你在绘制一个坐标上的刻度值,那么这个函数再好不过了。与此功能类似的还有PolylineTo、Polyline、PolyPolyline、Polygon、PolyPolygon等函数。

其他系统DLL支持。

(13)路径:负责将一系列直线和曲线组成名为路径的GDI对象,并用它来绘制的函数。

GDI提供几百个Windows程序中可以调用的函数。这些函数大多数是从Win32的子系统DLL
GDI32.DLL中导出的。窗口管理模块UER32.DLL是使用GDI函数的大用户,它用GDI函数来绘制菜单、图标、滚动条、标题栏和每个窗口的框架等细节内容。有一些绘图函数从USER32.DLL导出,提供给应用程序。仅Windows2000
GDI32.DLL就导出了543个入口点。与DevStudio一起发行的dumpbin工具是一个简单的工具,能列出模块导出的函数。下面就是由dumpbin
gdi32.dll /export 产生的部分内容:

5)关于在打印机上使用BitBlt
有时候在内存兼容dc里面已经做好图了,但在使用BitBlt的时候却会失败.这个时候,首先确认创建的内存兼容dc和bitmap是不是使用打印机的dc,如果确认无误,还是执行BitBlt失败,那80%可能是内存兼容bitmap太大了,请按如下方法再试试:
创建另外一个内存兼容dc2和一个比较小的内存兼容biimap2,大概是1000*1000吧,我是这样用的,然后把dc里面的内容分成块(1000*1000),把每一块BitBlt到dc2上面,再从dc2里面BitBlt到打印dc上.有人可能会有这样的疑问:那为什么不直接把dc里面的内容分几次BitBlt到打印机上呢?有区别吗?答案是肯定的,如果dc里面的bitmap太大,哪怕你想BitBlt一个10*10的区域到打印机上都会失败.

CPen pen(PS_SOLID,2,RGB(0,192,0));

CPen* pOldPen = dc->SelectObject(&pen);//把新的CDC对象选入设备描述表,同时保存旧的CDC对象

dc->Ellipse(0,0,100,100);

dc.SelectObject(pOldPen);//

CreateCompatibleBitmap 创建与确定的设备环境关联的设备相兼容的位图

CPen pen(PS_SOLID,1,RGB(255,0,0));

创建画笔的最简单的方法是构造一个CPen对象并把定义画笔所用的参数都传送给该对象

(7)填充形状:绘制闭合区域及其周线的函数。

SetPixel 给特定坐标位置处的像素设置确定的颜色

//由于WM_PAINT的优先级别很低(甚至在虚拟按键消息之后,见《windows核心编程》窗口消息一章),它只是改变了消息结构体中的QS_PAINT标志。所以呢,如果是使用函数UpdateWindows(),会直接send一个WM_PAINT消息,那样会直接调用窗口处理函数,比普通的WM_PAINT消息处理的快很多。

CDC::SetTextColor//设置文本颜色

CDC::GetTextColor//获得文本颜色

CDC::SetBkColor//设置背景颜色

CDC::SetBkMode//设置背景模式

CDC::SetMapMode//设置映射模式

CDC::CDC::SetROP2//设置绘图模式

CDC::MoveTo//当前位置

CDCL::SelectObject//当前画笔,当前画刷,当前字体

可编程映射模式

GetPixel 获取当前坐标位置处像素的RGB颜色值

SetBkColor(int nColor); 设置背景色。

其他没有文档记载的函数。

SetPixelV 给特定坐标位置处的像素设置与确定颜色最近似的颜色值

Windows用当前选入设备描述表的画笔绘制直线和曲线,并给用Rectangle,Ellipse以及其他图形生成函数画出的图形镶画边框。默认画笔画出的是一个像素点宽的黑色实线。

dc.Rectangle(0,0,200,-100);//画图

例子:

HDC BeginPaint(HWND hWnd,LPPAINTSTRUCT lpPaint)

特别说明

PlgBlt
把一个平行四边形中的像素颜色数据组成的位块从源设备环境转移到目标设备环境中

9、刷新背景问题

1、
设备上下文5大区域:剪裁区域、元区域,系统区域,API区域,RAO区域。对于由函数BeginPaint、GetDC或者CreateDC所返回的设备上下文来说,其剪裁区域的值是NULL,剪裁区域的值为NULL,也就意味着没有剪裁区域。没有剪裁区域意味着系统区域氛围内的所有图形都将被显示出来,而不是不显示。

void * pUser;

ExtTextOut:在指定的矩形中显示正文。可以用该函数删去超出矩形的正文,用正文背景填充矩形,调整字符间隔。

unsigned short nUpper;

SetDIBits 使用一个特定的设备无关位图中得到的颜色数据来设置位图的像素

图片 5

从以上的画笔定义可以看出,定义画笔需要三个特性:样式,宽度和颜色。画笔的样式说明请参见MSDN。

设备上下文类包括CDC和它的派生类CClientDC、CPaintDC、CWindowDC、CMetaFileDC。

网站地图xml地图