窗口、視口、屏幕顯示詳解--計算機繪圖基本功

窗口、視口、屏幕顯示詳解--計算機繪圖基本功  

 

 

      窗口: 邏輯環境中的一小部分,是一個矩形框;世界座標系是邏輯座標,SetWindowOrg(X,Y )設置窗口的邏輯座標點(X,Y)映射爲的設備環境的設備點(0,0)。

      設備環境:顯示器、打印機等等。座標系爲設備座標系,正Y軸向下,正X軸向右,原點在左上角,固定不變,不可修改!其X、Y的負半軸爲虛設,無法顯示或無法打印圖形。 

      視口: 設備環境中的一部分,一個矩形框;座標系同設備環境。SetViewportOrg(X,Y )設置視口矩形框的座標原點,默認值爲設備座標原點。視口是窗口的按比例的映像(投影)。

 

     1. 窗口和視口以及設備環境出現的緣起 

     窗口和視口實際上描述的是同一個客觀場景,它們之間的區別僅僅在於兩者座標系的單位不同。設備環境就是顯示設備。窗口和視口中的場景必須通過映射模式正確的顯示到設備環境中。 默認情況下,Windows GDI 繪圖中的窗口座標原點、視口座標原點以及設備點重合在一起都是在設備點(0,0)處。Windows GDI的設備環境原點是位於屏幕的左上角,設備點始終位於(0,0)點,x軸向右,y軸向上;OpenGL使用的窗口座標和WindowsGDI使用的窗口座標是不一樣的。OpengL默認世界窗口座標原點位於屏幕中心,x軸向右,y軸向上。視口原點在屏幕左下角。

 

       視口與窗口表徵的是同一個實際幾何物體,所以有窗口必定有視口,有視口必定有窗口窗口或視口可以理解爲用眼睛看到的物體實體,例如一個球,設備環境顯示區域(顯示屏)可以理解爲由於計算機屏幕長寬比不同,實際物體在計算機屏幕上看到的最終顯示結果,例如顯示屏中的球看起來變成一個橢球。幾乎在所有的GDI函數中使用的座標值都是採用的邏輯單位,也就是物體實際多大就是多大。Windows必須將邏輯單位轉換爲“設備單位”---像素,也就是物體在設備環境(顯示屏或打印機)顯示後看起來應該多大。這種轉換是由映射方式、窗口和視口的原點以及窗口和視口的範圍所控制的。 通過不同的映射方式可以在邏輯單位下改變窗口原點的位置而將物體現實在設備環境中,也可以在以像素爲單位的視口座標系下移動視口將物體顯示在顯示屏上,不管對窗口和視口原點作什麼改變,設備點(0,0)始終是客戶區的左上角,兩者都是爲了讓物體在顯示屏上現實。調整窗口原點和調整視口原點可以達到相同的效果。

 

       視口是與設備相關的一個矩形區域,座標單位是與設備相關的,直觀的視口原點座標的移動就是dc的移動窗口的座標是邏輯座標,與設備無關。窗口座標的原點與視口座標的原點始終對應於同一點。對於同一個圖形,用窗口座標系統表達的該區域的長和寬與視口的座標系統表達的長和寬是不同的,因單位不同。二者就定義了這兩個座標系統的比例關係程序作圖時,使用的座標總是是窗口座標,而實際的顯示或輸出設備卻各有自己的座標。例如,有的打印機設備水平和垂直分辨率不同,其象素實際上是長方形。程序編寫畫一個圓,若不經任何座標轉換,在打印機上輸出的就是個橢圓。

   在MFC 中ONDRAW 之前已經調用了ONPREPAREDC 函數爲你做好映射模式。默認情況下,其映射模式爲 MM_TEXT模式,即1:1模式。 要改變默認映射模式應重載OnPrepareDC重新設置自己的映射模式。爲了提高繪圖精度,經常需要改變映射模式。

 

    2. 窗口和視口理解要點

    窗口與視口一向是初學者比較難以理解的難點,本人以前也是糊里糊塗的,不過最近有時間去深入研究之後,才徹底弄明白,擺脫了以前很多錯誤的觀念。弄清楚了這些纔會更好的使用不同的座標影射模式,更靈活的爲自己的繪圖帶來便利:

 

    首先要清楚窗口和視口的座標原點始終是同一個點,窗口和視口中的內容是同樣的內容。設備座標(顯示器)中則會根據視口或窗口原點的改變而顯示出不同的內容。在MM_TEXT映射模式下世界座標系第三象限的內容就是窗口座標系下第一象限的內容,所以有SetViewportOrg(x, y)與SetWindowOrg(-x,-y)具有相同的效果。這個很難理解但是這是事實。“設備環境”如顯示屏始終是隻有左手座標系xoy第一象限的區域。開始時窗口座標原點、視口座標原點以及設備點重合在一起都是在客戶區的左上角,爲了將物體顯示在顯示屏上,通過SetWindowOrg(x,y)將"畫布"(世界座標系原點)向屏幕的左邊移動x個單位,向屏幕的上方移動y個單位。SetViewportOrg(x,y)是將"畫筆"(視口原點)向屏幕的右邊移動x個單位,向屏幕的下方移動y個單位。

 

     現將其關鍵點歸納如下:

 

     1、 視口與顯示設備有關,視口等同客戶區,使用設備座標。視口是和窗口等同的一塊矩形區域,它的x軸向右和y軸向下。

 

     2、 窗口與顯示設備無關,窗口與視口爲同一區域,但使用邏輯座標,它的x軸向右,y軸向上。

 

     3、 窗口與視口使用不同的座標系,但是兩套座標系的原點始終爲同一點。但該點座標(不管是視口座標原點還是窗口座標原點)不一定爲(0,0)。窗口就是視口,去吧僅僅在於兩者建立的座標系不同,移動窗口的同時就是移動視口,反之亦然。

     4、 視口原點的位置(就是畫筆dc的初始位置)僅僅由SetViewportOrgEx (x,y) 函數來移動。(x,y)是相對於客戶區左上角的設備座標,即像素),而SetWindowOrg(X,Y )設置窗口的邏輯座標點(X,Y)映射爲的設備環境的設備點(0,0)

            It's easy to get SetViewportOrg and SetWindowOrg confused, but the distinction between them is actually quite clear. Changing the viewport origin to (x,y) with SetViewportOrg tells Windows to map the logical point (0,0) to the device point (x,y). Changing the window origin to (x,y) with SetWindowOrg does essentially the reverse, telling Windows to map the logical point (x,y) to the device point (0,0)the upper left corner of the display surface. In the MM_TEXT mapping mode, the only real difference between the two functions is the signs of x and y. In other mapping modes, there's more to it than that because SetViewportOrg deals in device coordinates and SetWindowOrg deals in logical coordinates. <<Programing windows with MFC>>中這樣來描述

 

     dc始終開始從世界座標系的原點開始畫,畫筆的原點就是畫布的原點就是世界座標系的原點。在MM_TEXT下通過SetViewportOrg(X,Y)和SetWindowOrg(-X,-Y)將世界座標系的原點移動到設備環境座標系下的(X,Y)點。SetWindowOrg(-X,-Y)指設備點(0,0)處對應dc繪圖的邏輯點(-X,-Y),邏輯點(0,0)在MM_TEXT映射模式下位於設備座標系的(X,Y)點。

 

 

     1)  移動視口原點好比移動畫筆dc,如果將視口原點設置爲(xViewOrg,yViewOrg),相當於dc畫筆的移動,則邏輯點(0,0)就會被映射爲設備點(xViewOrg,yViewOrg),初始dc繪圖在邏輯點(0,0)下筆,圖形將以客戶區(設備點)的(xViewOrg,yViewOrg)爲中心進行繪製。在MM_TEXT映射模式下SetViewportOrg(150,100);dc繪圖座標原點從(0,0)向右下方移動到(150,100)。可以看出SetViewportOrg()函數可以更改設備上下文dc的座標原點。

 

  1. void CDemoView::OnPaint()   
  2. {  
  3.  CPaintDC dc(this); // device context for painting  
  4.  dc.SetViewportOrg(150, 100);  
  5.  CPen PenBlue;  
  6.  PenBlue.CreatePen(PS_SOLID,1,RGB(0,12,255));  
  7.  dc.SelectObject(&PenBlue);  
  8.  dc.Ellipse(-100,-100,100,100);  
  9. }  

 

   2) 移動窗口原點好比畫布的移動,如果將窗口原點改變爲(xWinOrg,yWinOrg),則邏輯點(xWinOrg,yWinOrg)將會被映射爲設備點(0,0),SetWindowOrg(150,100);邏輯點(150,100)對應於設備點(0,0);不管對窗口和視口原點作什麼改變,應該銘記第一設備點(0,0)始終是客戶區的左上角,第二窗口和視口原點是同一個點。

 

  1. void CDemoView::OnPaint()   
  2. {  
  3.  CPaintDC dc(this); // device context for painting  
  4.  dc.SetWindowOrg(-150,-100);  
  5.  CPen PenBlue;  
  6.  PenBlue.CreatePen(PS_SOLID,1,RGB(0,12,255));  
  7.  dc.SelectObject(&PenBlue);  
  8.  dc.Ellipse(-100,-100,100,100);  
  9.  //OnDraw(&dc);  
  10. }  


     5、 理解窗口與視口的座標轉換公式:

       Xviewport=(Xwindow-Xwinorg)* Xviewext / Xwinext + Xvieworg;

       Yviewport=(Ywindow-Ywinorg)* Yviewext / Ywinext + Yvieworg;

       此公式初看上去不好理解,變形如下:

       (Xviewport-Xvieworg)/(Xwindow-Xwinorg)= Xviewext / Xwinext;

       (Yviewport-Yvieworg)/(Ywindow-Xwinorg)= Yviewext / Ywinext;

        如此就很好理解了:邏輯座標單位與設備座標單位的比,即比例因子。   

     6、 對於定義的客戶區域大於程序所創建的窗口時,就需要有滾動條來滾動顯示,才能顯示完整的客戶區域。

     7 處理滾動窗口:假設未滾動窗口時,客戶區左上角對應窗口和視口的原點座標,且默認均爲(0,0), 如果滾動窗口時,水平方向向右滾動了X個像素,垂直方向向下滾動了Y個像素,則應該認爲客戶區域的左上角爲窗口原點(同時也是視口原點)一起滾動,並且窗口原點(同時也是視口的原點)的座標始終爲(0,0)不變,變的只是窗口原點在設備環境座標系中的位置。當前看到的場景就是設備環境(顯示屏)中顯示的內容,設備環境的座標系永遠不會移動。通過SetViewportOrgEx (X,Y)實現將世界座標系的原點移到顯示屏的(X,Y)位置。這兩種移動有個相同的結果就是窗口座標的原點與視口座標的原點始終對應於同一點(世界座標系的原點),而新的世界座標系的原點位置恰好是設備環境中的(X,Y)。通過調試MFC中CSrollView類函數發現,它就是通過SetViewportOrgEx (X,Y)函數移動世界座標系的原點從而實現“窗口的滾動”。

    8、座標原點(不論是視口還是窗口)不等於座標零點即設備點(0,0)始終是客戶區的左上角(必需明白)。

    9、視口的座標原點可以任意移動,但其零點始終在客戶區左上角。

    10、窗口原點可任意移動,窗口類似於單張照片,僅僅是當前看到的景物。視口類似於電影膠捲,記錄了以前到現在的所有信息。

 

    爲了能夠在計算機中更好的描述真實的世界,必須設定一種與真實世界相符的邏輯座標系統。當然Windows爲我們提供了設備座標與邏輯座標進行映射的接口。

 

     Windows提供的座標系統映射的接口有:

     SetMapMode(int nMapMode)      設置映射模式,Windows提供了8種映射模式

     SetWindowOrg(int x, int y) / SetWindowOrg(POINT point)   設置與視口座標原點相對應的窗口座標原點,windows要求視口的座標原點必須與窗口的座標原點相對應,x, y和point的單位爲邏輯單位

     SetViewportOrg(int x, int y) / SetViewportOrg(POINT point)   設置與窗口座標原點相對應的視口座標原點,windows要求視口的座標原點必須與窗口的座標原點相對應,x, y和point的單位爲設備單位,即像素

     SetWindowExt(int cx, int cy) / SetWindowExt(SIZE size)   設置邏輯座標系統中窗口的大小範圍,cx, cy和size的單位爲邏輯單位

     SetViewportExt(int cx, int cy) / SetViewportExt(SIZE size)   設置設備座標系統中視口的大小範圍,cx, cy和size的單位爲設備單位,即像素

 

  11、孫鑫老師關於圖形錯位的說明

  當我們在窗口中點擊鼠標左鍵的時候,得到的是設備座標(680,390),在MM_TEXT的映射模式下,邏輯座標和設備座標是相等的,所以我們利用集合類保存的這個點的座標是以象素爲單位,座標值爲(680,390)。在調用OnDraw函數前,在OnPaint函數中調用了OnPrepareDC函數,OnPrepareDC函數內調用SetViewportOrgEx ()調整了顯示上下文的屬性,將視口的原點設置爲了(0,-150),這樣的話,窗口的原點,也就是邏輯座標(0,0)將被映射爲設備座標(0,-150),在畫線的時候,因爲GDI的函數使用的是邏輯座標,而圖形在顯示的時候,Windows需要將邏輯座標轉化爲設備座標,因此,原先保存的座標點(680,390)(在GDI函數中,作爲邏輯座標使用),根據轉換公式

 

    xViewport = xWindow-xWinOrg+xViewOrg 

 

和yViewport = yWindow-yWinOrg+yViewOrg,

 

得到設備點的x座標爲680-0+0=680,設備點的y座標爲390-0+(-150)=240,於是我們看到圖形在原先顯示地方的上方出現了。 

 

    關於解決方法的說明

 

    首先我們在繪製圖形之後,在保存座標點之前,調用OnPrepareDC函數,調整顯示上下文的屬性,將視口的原點設置爲(0,-150),這樣的話,窗口的原點,也就是邏輯座標(0,0)將被映射爲設備座標(0,-150),然後我們調用DPtoLP函數將設備座標(680,390)轉換爲邏輯座標,根據設備座標轉換爲邏輯座標的公式:

     xWindow = xViewport-xViewOrg+xWinOrg,

     yWindow = yViewport-yViewOrg+yWinOrg,得到邏輯點的x座標爲680-0+0=680,y座標爲390-(-150)+0=540,將邏輯座標(680,540)保存起來,在窗口重繪時,會先調用OnPrepareDC函數,調整顯示上下文的屬性,將視口的原點設置爲了(0,-150),然後GDI函數用邏輯座標點(680,540)繪製圖形,被Windows轉換爲設備座標點(680,390),和原先顯示圖形時的設備點是一樣的,當然圖形就還在原先的地方顯示出來。

 

    從Windows的鼠標消息可以獲得鼠標指針的當前座標值(point.x和point.y)此座標值是設備座標。

    很多MFC庫函數尤其是CRect的成員函數只能工作在設備座標下。

    還有我們有時需要利用物理座標,物理座標的概念就是現實世界的實際尺寸。

    設備座標-邏輯座標-物理座標之間如何進行轉換便成爲我們要考慮的一個問題,物理座標和邏輯座標是完全要我們自己來做的,但WINDOWS提供了函數來幫助我們轉換邏輯座標和設備座標。

 

    CDC的LPtoDP函數可以將邏輯座標轉換成設備座標

    CDC的DPtoLP函數可以將設備座標轉換成邏輯座標

 

    下面列出我們應該在什麼時候使用什麼樣的座標系一定要記住:

 

◎CDC的所有成員函數都以邏輯座標爲參數

◎CWnd的所有成員函數都以設備座標爲參數

◎區域的定義採用設備座標

◎所有的選中測試操作應考慮使用設備座標。

◎需要長時間使用的值用邏輯座標或物理座標來保存。因設備座標會因窗口的滾動變化而改變。


本文轉載自:

http://blog.csdn.net/lishuhuakai/article/details/18462045

發佈了12 篇原創文章 · 獲贊 1 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章