1:
//这个函数用来设置坐标轴的朝向以及单位
int SetMapMode(
HDC hdc, // handle to device context
int fnMapMode // new mapping mode
);
MM_ANISOTROPIC:
MM_HIENGLISH:1逻辑单位是1/1000英寸,X是朝右,Y朝上
MM_HIMETRIC:1逻辑单位是1/100微米,X是朝右,Y朝上
MM_LOENGLISH:1逻辑单位是1/100英寸,X是朝右,Y朝上
MM_LOMETRIC:1逻辑单位是1/10微米,X是朝右,Y朝上
MM_TEXT:1逻辑单位是1设备像素,X是朝右,Y朝下(设备不同时,单位也不同)
MM_TWIPS:1逻辑单位是1打印点的1/20,即(1/1440英寸),X是朝右,Y朝上。
所谓的逻辑坐标就是指以具体的物理单位为实际的单位,从而大小跟具体的设备无关。所谓的设备坐标单位就是指像素了,windows对所有的消息,所有的GDI函数,甚至对一些GDI函数,永远使用设备坐标。
视端口是依据设备坐标,也就是像素
窗口时是依据逻辑坐标,可以使相似,毫米,英寸等。
逻辑点和设备点之间的相互转换
DPtoLP(hdc,pPoints,iNumber);//设备点转换为逻辑点
LPtoDP(hdc,pPoints,iNumber);//逻辑点转换为设备点
坐标原点的改变
SetViewportQrgEx(HDC hdc,int x,int y,LPPOINT lpPoint);//(x,y是设备单位,像素)
SetWindowOrgEx(HDC hdc,int x,int y,LPPOINT lpPoint);//(x,y是逻辑单位)
两个函数的lpPoint 参数是函数返回的原来坐标原点的值。
坐标原点获取
GetViewportOrgEx(HDC hdc,LPPOINT lpPoint);
GetWindowOrgEx(HDC hdc,LPPOINT lpPoint);
细节:
SetViewportOrgEx(hdc,cxClient/2,cyClient/2,&pt);
TextOut(hdc,0,10,TEXT("I Love You"),10);//显示在中间,pt为(0,0)
//SetViewportOrgEx(hdc,cxClient/2,cyClient/2,NULL);//还是显示在中间
SetViewportOrgEx(hdc,pt.x,pt.y,&pt);
TextOut(hdc,0,0,TEXT("What is it AB"),13);//显示在左上角
由以上程序的第3行可以看出,当第2次调用SetViewportOrgEx()时,里面的坐标点还是依据最初始的坐标原点,而不是相对于改变后的坐标原点。其实想想这样是非常合理的,如果要改变原点的次数很多的话,那么每次都要依靠前后两次的相对距离,这样会相当复杂的。(根据原书解释,不管视口和窗口怎么变换,设备点(0,0)始终为显示区域的左上角)
SetWindowOrgEx(hdc,-cxClient/2,-cyClient/2,&pt);
TextOut(hdc,0,0,TEXT("I Love You"),10);//此时显示在文本在中间
SetWindowOrgEx(hdc,pt.x,pt.y,NULL);
TextOut(hdc,0,0,TEXT("What is it AB"),13);//此时文本显示在左上角
由以上程序第1行可以看出对于做到相同的显示效果,视口坐标系和窗口坐标系所做的活动室完全相反的,这是因为当视口坐标系进行变换时,这个时候相当于移动的是内容,但是窗口不变。当窗口坐标系进行变换时,此时实际上是内容不动,但是窗口发生移动,从而造成内容产生对窗口的相对移动。此时还有一个问题就是在上面的TextOut(hdc,0,0,TEXT("I Love You"),10);中,坐标(0,0)到底指的什么坐标,它是相对于什么来讲的,如果说是窗口原点的话,这明显是不可能的,所以这种坐标应该是相对于视口原点来讲的。
所以说当移动视口时,设备点(0,0)是不会发生变化的,原因是窗口没有发生变化,但是当移动窗口时,设备点(0,0)发生了变化,不管怎样,设备点(0,0)都要保持和窗口左上端点重合。然后一些画图函数,文字函数等GDI函数,他们的坐标点都是相对于视口原点来说的,而不是相对于窗口。至于其单位当然可以使逻辑单位或者像素了。
再说说连续两次调用SetWindowOrgEx(……),我们再想想第二次调用SetWindowOrgEx(hdc,0,0,NULL)
时,这里的(0,0)坐标又是相对谁的,很明显,这样调用后显示的文字又是显示在左上顶端,说明了(0,0)就是之前的原点,所以对视口坐标系和窗口坐标系来讲,他们都保留一个最原始的坐标体系,
设置坐标原点的位置都是相对于这个最原始的坐标系来讲,这很明显能够避免复杂性。
GetWindowExtEx(hdc,&sz1);
GetViewportExtEx(hdc,&sz2);
SetWindowExtEx(hdc,nXExtent,nYExtent,lpsize);
SetViewportExtEx(hdc,nXExtent,nYExtent,lpsize);
这四个函数是用来改变视口坐标系以及窗口坐标系的坐标轴的范围,仅仅在MM_ISOTROPIC以及MM_ANISOTROPIC的模式下起作用。其他的六种模式下这四个函数将会被忽略,如果用
GetWindowExtEx(hdc,&sz1);
GetViewportExtEx(hdc,&sz2);来获取坐标范围,那么得到的值将是1.
**********************************************************************************************
1:Ellipse(hdc,-200,-200,200,200);
2:SetViewportOrgEx(hdc,200,200,NULL);
Ellipse(hdc,-200,-200,200,200);
3:
SetWindowOrgEx(hdc,-200,-200,NULL);
Ellipse(hdc,-200,-200,200,200);
这三个图充分说明了这些GDI函数里面的坐标是相对于视口坐标系而讲的,而不是针对设备坐标点以及窗口坐标系.
4:
SetMapMode(hdc,MM_ANISOTROPIC);
SetWindowExtEx(hdc,400,400,NULL);
SetViewportExtEx(hdc,rc.right,rc.bottom,NULL);
SetWindowOrgEx(hdc,-200,-200,NULL);
Ellipse(hdc,-200,-200,200,200);
从上图明显可以看出,此时的圆明显变形了,原因就是在我们将窗口坐标设置为(0-400),(0-400),并且将其和设备区域的x,y轴的像素相对应,由于x,y轴不等长,所以其像素个数也不相等,但是其对应的逻辑长度都相等,这样就造成对于同一单位的X,Y,其占有的像素不等,因此就让圆扁了.
注意:在windows中,GDI函数对应的坐标长度单位是逻辑值,也就是实际的物理量度单位,但是最终在设备上显示的肯定都是以像素的形式表达出来,这里面就要涉及到逻辑值和像素值的对应关系。
对于SetWindowExtEx(hdc,x,y,lpsize);SetViewportExtEx(hdc,x,y,lpsize)的再深一步探讨。
对于SetWindowExtEx(hdc,x,y,lpsize);这里的x,y到底表达的是神马东西,的确会让人产生误解,x,y其实表达的就是窗口中x,y的逻辑值,
SetViewportExtEx(hdc,x,y,lpsize)中的x,y表达的就是相应的像素值,当x,y对应的逻辑长度明确时(Length),此时对于SetWindowExtEx(hdc,x,y,lpsize),表达的意思就是这个Length长度内的逻辑值,这样Length内分别对于相应的像素值,对于相应的逻辑值,从而产生了逻辑值和像素值的比对关系。
在上面的这些东西都理解的情况下,咱们再来看windows程序设计中一段代码:
SetMapMode (hdc, MM_ISOTROPIC) ;
SetWindowExtEx (hdc, 16, 16, NULL) ;
SetViewportExtEx (hdc, GetDeviceCaps (hdc, LOGPIXELSX),
GetDeviceCaps (hdc, LOGPIXELSY), NULL) ;
解释:因为函数GetDeviceCaps (hdc, LOGPIXELSX)的意思即是1英寸内对应的像素值,所以SetWindowExtEx (hdc, 16, 16, NULL) ,这里的16表示的就是1英寸内对于的逻辑值,逻辑值=16,那么逻辑单位就是1/16英寸,从而做到了利用这两个函数来改变逻辑单位值的目的。因为我们的几乎所有GDI函数都是以逻辑单位为单位,那么当SetWindowExtEx (hdc, 16, 16, NULL) 中的16变得很大时,对应的画出来的图就越来越小了。
当SetWindowExtEx()或者SetViewportExtEx()中出现(x,y)中的一个为负值时,此时会造成坐标轴的翻转。如:
SetWindowExtEx (hdc, 276, -72, NULL)
SetViewportExtEx (hdc, cxClient, cyClient, NULL)
或者
SetWindowExtEx (hdc, 276, 72, NULL)
SetViewportExtEx (hdc, cxClient, -cyClient, NULL)
会造成:
原本的程序:
SetWindowExtEx (hdc, 276, 72, NULL)
SetViewportExtEx (hdc, cxClient, cyClient, NULL)
若再把程序改成这样
SetWindowExtEx (hdc, 276,-72, NULL)
SetViewportExtEx (hdc, cxClient, -cyClient, NULL)