閱讀698 返回首頁    go 阿裏雲 go 技術社區[雲棲]


窗口和視口

1.窗口是基於邏輯坐標的.

2.視口是基於設備坐標.

3.設備坐標是以像素為單位的,邏輯坐標是以.cm,m,mm,.....

4.係統最後一定要把邏輯坐標變為設備坐標.

5.設備坐標有3種:

     a.GetDC() 客戶區坐標.

     b.GetWindowDC().窗口坐標.

     c.GetDC(NULL),or CreateDC().屏幕坐標.

 

記住兩個原則:

1.總是由窗口原點映射為視口原點。即無論你窗口的原點和視口的原點怎麼變,窗口的原點總是映射到視口的原點。(記住:映射方向是窗口到視口,而不是視口向窗口映射)

2.不管利用函數(如:SetViewportOrgExt和SetWindowOrgEx)對窗口和視口的原點做怎麼改變,設備點(0,0)始終是客戶區的左上角。

 

繪圖操作隻能在客戶區中進行。除客戶區以外的部分都叫非客戶區
在 Windows的作圖函數中,涉及邏輯坐標到設備坐標的轉換。這裏提到的窗口(window)、視口(viewport)是對應的概念。視口是與設備相關的一個矩形區域,坐標單位是與設備相關的。窗口的坐標是邏輯坐標,與設備無關。窗口坐標的原點與視口坐標的原點始終對應於同一點。窗口原點是與視口原點相同的,(也就是說,位於屏幕上的同一個位置),為了體會不窗口坐標與視口坐標的轉換,先用SetwindowtOrg(100, -300)將窗口移到(100,-300),這相當於把 邏輯點(100,-300)映射到設備點(0,0)(注意:如果改用SetviewportOrg移動原點,則其原點的移動尺寸與映射模式無關,其300個長度單位等於9厘米)

(注:這個設備點也是相對於當前的DC而言,如果當前的DC是整個屏幕,那麼設備點(100,-300)的原點就在屏幕的左上角,如果當前的DC是某個應用程序的客戶區,那麼設備點(100,-300)的原點就是應用程序的客戶區的左上角,)

現在假設當前DC是某個應用程序的客戶區,映射模式為 MM_LOMETRIC
(在這種映射模式下,設備坐標以0.1 毫米為一個長度單位,Y軸的正方向為向上,也就是說,屏幕上的左上角的Y坐標為0,下麵的Y坐標均小於0),當使用語句:

MoveTo(100,-600);
LineTo(100,-800);//Y軸負方向向下,所以用負值

進行畫圖時,它的轉換過程如下:
1)窗口與視口坐標轉換,由於窗口坐標(100,-300 ),映射為設備坐標(0,0),所 以
窗口(100,-600)映射為視口(0,-300)
窗口(100,-800)視映射為視口(0,-500)

2)邏輯與設備坐標轉換
由於是 MM_LOMETRIC 映射模式,設備坐標的單位長度為 0.1mm,而在輸出時用的是視口坐標,所以視口坐標(100,-600)輸出到屏幕上時,該點的位置
距客戶區左邊 0 厘米,距客戶區上邊 3厘米。這就是直線的起點;
再把直線終點的視口坐標(100,-800)輸出到距客戶區左邊0厘米,距客戶區上邊 5厘米處。對於同一個圖形,用窗口坐標係統表達的該區域的長和寬與視口的坐標係統表達的長和寬是不同的。二者就定義了這兩個坐標係統的比例關係。程序作圖時,使用的坐標總是是窗口坐標。而實際的顯示或輸出設備卻各有自己的坐標。例如,有的打印機設備水平和垂直分辨率不同,其象素實際上是長方形。程序編寫畫一個圓,若不經任何坐標轉換,在打印機上輸出的就是個橢圓。下麵程序示範了如何將窗口邏輯坐標的x,y向象素寬度均映射為輸出設備中的 1/64 英寸。
[code]
SetMapMode(hDC, MM_ISOTROPIC);
SetWindowExtEx(hDC, 64, 64, NULL);
SetViewportExtEx(hDC, GetDeviceCaps(hDC, LOGPIXELSX),
GetDeviceCaps(hDC, LOGPIXELSY), NULL);
[/code]
調用SetWindowExt()函數後,緊接著應調用SetViewportExt()函數,它的任務是規定水平及垂直坐標軸的單位。可以這樣認為,SetWindowExt()函數對應著“窗口”,SetViewportExt()函數對應著“視口”。幾點:
1.窗口是基於邏輯坐標的.
2.視口是基於設備坐標.
3.設備坐標是以像素為單位的,邏輯坐標是以.cm,m,mm,.....
4.係統最後一定要把邏輯坐標變為設備坐標.
5.設備坐標有3種:
a.GetDC() 客戶區坐標.
b.GetWindowDC().窗口坐標.
c.GetDC(NULL),or CreateDC() .屏幕坐標.
問題如下:
void CMyView::OnDraw(CDC *pDC)
{
...
(1):pDC->SetMapMode(MM_ISOTROPIC);
(2):pDC->SetWindowExt(4000,3000);
(3):AfxGetMainWnd()->GetClientRect(cRect);
(4):pDC->SetViewportExt(cRect.Width(),-cRect.Height());
(5):pDC->DPtoLP(cRect);
(6):pDC->SetWindowOrg(-(cRect.Width()/2),-(cRect.Height()/2));
(7):pDC->TextOut(-850,1400,"abc");
...
}

第一句:定義邏輯單位和具體的設備單位的映射模式
第二句:設置與設備上下文關聯的窗口的X和Y坐標的範圍。其中4000和3000是邏輯坐標。此時窗口大小為(4000,3000),視口大小未知。
第三句:得到客戶區尺寸。此尺寸就是當前視口尺寸。如果(800,600),在最大化時此尺寸為(800,5XX).
第四句:設置視口的範圍,第二個參數為負數,表示視口坐標往下移了cRect高度的一半。
第五句:將設備坐標轉換成邏輯坐標。因為GetClientRect得到的設備坐標
第六句:設置設備上下文窗口的起始坐標。
最後一句:在第六句的基礎上,在邏輯坐標位置輸出文本abc. 記住兩個原則:
1.總是由窗口原點映射為視口原點。即無論你窗口的原點和視口的原點怎麼變,窗口的原點總是映射到視口的原點。(記住:映射方向是窗口到視口,而不是視口向窗口映射)
2.不管利用函數(如:SetViewportOrgExt和SetWindowOrgEx)對窗口和視口的原點做怎麼改變,設備點(0,0)始終是客戶區的左上角。
現將其關鍵點歸納如下:
1、 視口等同客戶區,使用設備坐標。
2、 窗口與視口為同一區域,但使用邏輯坐標。
3、 窗口與視口使用不同的坐標係,但是兩套坐標係的原點始終為同一點。但該點坐標(不管是視口坐標原點還是窗口坐標原點)不一定為(0,0)。
4、 坐標點的位置(相對於客戶區左上角)僅僅由SetViewportOrgEx (x,y) 函數來移動(x,y相對於客戶區左上角的設備坐標,即像素),而SetWindowOrgEx(x,y)僅僅設置該原點作為窗口坐標原點時所對應的邏輯坐標為(x,y),而不會改變其相對於客戶區左上角的位置。
5、 理解窗口與視口的坐標 轉換公式:
        Xviewport=(Xwindow-Xwinorg)* Xviewext / Xwinext + Xvieworg;
        Yviewport=(Ywindow-Xwinorg)* Yviewext / Ywinext + Yvieworg;
      此公式初看上去不好理解,變形如下:
      (Xviewport-Xvieworg)/(Xwindow-Xwinorg)= Xviewext / Xwinext;
      (Yviewport-Yvieworg)/(Ywindow-Xwinorg)= Yviewext / Ywinext;
       如此就很好理解了:基於上述1、2、3點,在這個區域的任意一點到原點的距離之比,就是其邏輯坐標單位與設備坐標單位的比,即比例因子。  
6、 對於定義的客戶區域大於程序所創建的窗口時,就需要有滾動條來滾動顯示,才能顯示完整的客戶區域。
7、 處理滾動窗口:假設未滾動窗口時,客戶區左上角對應 窗口和視口的原點坐標,且默認均為(0,0), 如果滾動窗口時,水平方向向右滾動了X個像素,垂直方向向下滾動了Y個像素,則應該認為客戶區域的左上角為窗口原點(同時也是視口原點)一起滾動,並且窗口原點的坐標始終為(0,0)不變,但是視口的原點所影射的坐標應該發生變化,且為(-X,-Y)。可以通過SetViewportOrgEx (-X,-Y)。通過調試MFC中CSrollView類函數,發現,它就是通過SetViewportOrgEx (X,Y)函數改變視口的坐標原點來實現窗口的滾動。
8、   坐標原點(不論是視口還是窗口)不等於坐標零點(必需明白)。
9、視口的坐標原點可以任意移動,但其零點始終在客戶區左上角。
10、窗口原點和零點均可任意移動。
不正確之處,歡迎各位高手指正!

 

 

 

 

在   Windows   的作圖函數中,涉及邏輯坐標到設備坐標的轉換。這裏提到的窗口(window)、視口(viewport)是對應的概念。視口是與設備相關的一個矩形區域,坐標單位是與設備相關的。窗口的坐標是邏輯坐標,與設備無關。窗口坐標的原點與視口坐標的原點始終對應於同一點。對於同一個圖形,用窗口坐標係統表達的該區域的長和寬與視口的坐標係統表達的長和寬是不同的。二者就定義了這兩個坐標係統的比例關係。程序作圖時,使用的坐標總是是窗口坐標。而實際的顯示或輸出設備卻各有自己的坐標。例如,有的打印機設備水平和垂直分辨率不同,其象素實際上是長方形。程序編寫畫一個圓,若不經任何坐標轉換,在打印機上輸出的就是個橢圓。下麵程序示範了如何將窗口邏輯坐標的x,y向象素寬度均映射為輸出設備中的   1/64   英寸。    
  SetMapMode(hDC,   MM_ISOTROPIC);    
  SetWindowExtEx(hDC,   64,   64,   NULL);    
  SetViewportExtEx(hDC,   GetDeviceCaps(hDC,   LOGPIXELSX),    
  GetDeviceCaps(hDC,   LOGPIXELSY),   NULL);

 

 

 

 

 

 

 

 

 

 

所謂視口代表設備,比如屏幕。  
  窗口代表我們的思維。  
   
  我們對windows說在(5,6)處畫個點(調用GDI函數)。windows認為是在我們的思維的(5,6)   處畫了個點。(也就是說5,6是邏輯坐標,GDI函數中的大部分都是邏輯坐標)  
   
  那麼,要把它映射到屏幕上,必須作一些解釋。  
   
  解釋包括:  
  原點在哪裏?5,6代表什麼?注意,解釋5,6時不光是距離問題,還有方向呢!  
   
  SetViewportOrgEx和SetWindowOrgEx是管第一個問題的。  
  設置X軸Y軸方向(SetMapMode),還有SetViewportExtEx和SetWindowExtEx是管第二個問題的。  
   
  其實想象一下也可以知道,5和6應該是乘上一個比率來變成像素單位。而後根據原點並按照X軸Y軸方向去畫就可以了。  
  SetViewportExtEx和SetWindowExtEx就是決定比率的函數。(他們的第二個和第三個參數分別相除來代表橫縱坐標的比率。)  
   
  句個例子:我希望使用笛卡爾坐標係(也就是X向右增加,Y向上增加),並且坐標單位是厘米,那麼此時就應該有某個比率來把5,6厘米變成是多少個像素(在 windows提供的某種映射模式中確實有這個比率)。再去畫。(此時你也不能亂改比率,畢竟一厘米多少像素是個常數,你設置了映射模式後也就決定了比率)  
   
  在我們的例子中,  
   
  SetWindowExtEx(hDC,   64,   64,   NULL);    
  SetViewportExtEx(hDC,   GetDeviceCaps(hDC,   LOGPIXELSX),    
  GetDeviceCaps(hDC,   LOGPIXELSY),   NULL);  
   
  這樣之後,5代表5*   (64/GetDeviceCaps(hDC,   LOGPIXELSX)這麼多個像素。  
  6代表6*   (64/GetDeviceCaps(hDC,   LOGPIXELSX)這多個像素。  
  我們修改了比率,由此可知映射模式也是我們自定義的。(各向同性,各向異性,英文是啥記不得了)

最後更新:2017-04-02 06:51:27

  上一篇:go VC++技術內幕(第四版)筆記--SetWindowExt和SetViewportExt
  下一篇:go magento -- 列表頁顯示產品屬性值的幾種調用方式