|舉報|字號 訂閱
在Windows應用程序中,只要進行繪圖,就要使用GDI座標系統。Windows提供了幾種映射方式,每一種映射都對應着一種座標系。例如,繪製圖形時,必須給出圖形各個點在客戶區的位置,其位置用x 和y兩個座標表示,x表示橫座標,y表示縱座標。在所有的GDI繪製函數中,這些座標使用的是一種“邏輯單位”。當GDI函數將結果輸出送到某個物理設備上時,Windows將邏輯座標轉換成設備座標(如屏幕或打印機的像素點)。本文討論了圖形環境中的各個映射模式,包括它們是什麼,怎麼工作的,以及它們真正的含義。
一、窗口、視口以及映射模式基本概念
強調一個網上和教科書上沒有將清楚但是至關重要的概念:窗口和視口其實是同一塊矩形區域,兩者座標系的原點是同一個點。窗口和視口的區別僅僅是單位不同。窗口和視口都不是指顯示屏或打印機上的區域,我們看到顯示屏上的物體實際上是顯示在“設備環境”上的,由於視口(也就是窗口)區域與設備環境的左手座標系第一象限xoy平面有交集,我們才能看到視口中的物體。窗口的變換和視口的變換的目的是一樣的,都是爲了將物體顯示在設備環境中,只不過由於視口中使用像素作爲單位也就是顯示屏的設備座標,所以往往在視口中進行調整dc比較直觀一些。
(一)邏輯座標。邏輯座標即是世界座標系下的座標。邏輯座標與設備無關,客觀世界中的場景可以由世界窗口或視口中進行描述,在窗口中進行描述時使用世界座標系中的座標單位也即邏輯座標,在視口中進行描述使用的是設備座標。
(二)設備座標。圖形輸出時,Windows將GDI函數中指定的邏輯座標映射爲設備座標。在屏幕顯示的設備座標系統中,單位以像素點爲準,水平值從左到右增大(正方向向右),垂直值從上到下增大(正方向向下)。注意設備座標系的原點永遠不會移動,它們僅僅與物理設備有關。
設備空間的範圍實際顯示設備上的矩形區域,通常有三種範圍:窗口的客戶區(使用BeginPaint 或 GetDC 獲取)全窗口(使用GetWindowDC 獲取)全屏幕(使用GetDc(0)/ CreateDC 獲取);還有一些特殊的設備空間,如內存設備空間(使用CreateCompatibleDC獲取),打印機設備空間,元文件設備空間即(CreateMetaFile)Windows中包括以下3種設備座標,以滿足各種不同需要:
1、客戶區域座標,包括應用程序的客戶區域,客戶區域的左上角爲(0, 0)。
2、屏幕座標,包括整個屏幕,屏幕的左上角爲(0, 0)。屏幕座標用在WM_MOVE消息中(對於非子窗口)以及下面的Windows 函數中:CreateWindow 和MoveWindow(都對於非子窗口)、GetMessage、GetCursorPos、GetWindowRect、WindowFromPoint 和SetBrushOrg 中。用函數ClientToScreen和ScreenToClient可以將客戶區域座標轉換成屏幕區域座標,或反之。
3、全窗口座標,包括一個程序的整個窗口,包括標題條、菜單、滾動條和窗口框,窗口的左上角爲(0,0)。使用GetWindowDC得到的窗口設備環境,可以將邏輯單位轉換成窗口”座標。
在MM_LOMETRIC、MM_HIMETRIC、MM_LOENGLISH、MM_HIENGLISH、MM_TWIPS的映射模式下,窗口座標系的x軸向右,y軸向上。所以在窗口的第一象限繪圖一般圖像不會顯示在設備環境上(如窗口的客戶區),因爲此時視口(窗口)的第一象限與設備環境的第一象限的交集爲空。
映射模式 |
邏輯單位 |
增加值 |
|
x值 |
y值 |
||
MM_TEXT |
像素 |
右 |
下 |
MM_LOMETRIC |
0.01 cm |
右 |
上 |
MM_HIMETRIC |
0.001 cm |
右 |
上 |
MM_LOENGLISH |
0.01 in. |
右 |
上 |
MM_HIENGLISH |
0.001 in. |
右 |
上 |
MM_TWIPS |
1/1440 in. |
右 |
上 |
MM_ISOTROPIC |
任意(x = y) |
可選 |
可選 |
MM_ANISOTROPIC |
任意(x != y) |
可選 |
可選 |
如果您將顯示器的像素尺寸設定爲1024×768,下表就是Windows NT報告的視口和窗口範圍的值。
映像方式 |
視口範圍(x,y) |
窗口範圍(x,y) |
MM_LOMETRIC | (1024, -768) | (3,200, 2,400) |
MM_HIMETRIC | (1024, -768) | (32,000, 24,000) |
MM_LOENGLISH | (1024, -768) | (1,260, 945) |
MM_HIENGLISH | (1024, -768) | (12,598, 9,449) |
MM_TWIPS | (1024, -768) | (18,142, 13,606) |
映像方式 |
邏輯單位 |
英寸 |
毫米 |
MM_LOENGLISH | 0.01 in. | 0.01 | 0.254 |
MM_LOMETRIC | 0.1 mm. | 0.00394 | 0.1 |
MM_HIENGLISH | 0.001 in. | 0.001 | 0.0254 |
MM_TWIPS | 1/1400 in. | 0.000694 | 0.0176 |
MM_HIMETRIC | 0.01 mm. | 0.000394 | 0.01 |
/*--------------------------插入GetDeviceCaps的使用說明Begin-----------------------------
//所有像素數
int pagecx=dc.GetDeviceCaps(HORZRES);
int pagecy=dc.GetDeviceCaps(VERTRES);
//即每英寸點數
short cxInch = dc.GetDeviceCaps(LOGPIXELSX);
short cyInch = dc.GetDeviceCaps(LOGPIXELSY);
// 計算一個設備單位等於多少0.1mm
double scaleX = 254.0 / (double)GetDeviceCaps(dc.m_hAttribDC,LOGPIXELSX);
double scaleY = 254.0 / (double)GetDeviceCaps(dc.m_hAttribDC, LOGPIXELSY);
說明:
主要用到的參數見例子中的:HORZRES,VERTRES,LOGPIXELSX,LOGPIXELSY.總的來說是爲了方便控制打印或重畫時的控制,如爲了定製打印時,一般依據的是物理的長度,而不是像素,而DC一般是用像素的映射模式,所以需要一下轉換,上面這個函數就爲這種轉換設計的.
以上三者的關係通常滿足:HORZSIZE = 25.4 * HORZRES/LOGPIXELSX
HORZSIZE爲屏幕水平尺寸(定爲度量尺寸,以mm計),HORZRES爲水平的像素總數(定爲像素大小,平時所說的屏幕分辨率,但在這不這麼稱呼。這裏,分辨率定爲“每英寸的像素數”),LOGPIXELSX爲邏輯像素(假設的每英寸的像素數,並不是剛纔所說的實際的“分辨率”)。因此HORZSIZE也稱爲邏輯寬度。
當我們選擇“顯示”屬性裏的大字體時,LOGPIXELSX(通常分爲96dpi與120dpi)變大了,這樣假設原來的字體爲10磅,則原來的字體橫向所佔像素(實際所佔的像素數)爲10*(1/72)*LOGPIXELSX,現在LOGPIXELSX變大了,則字體所佔像素也大了,因此看起來字體大了。如果HORZRES不變的話,則HORZSIZE應該變小。然後這是和Windows有關的,在16位OS中,HORZSIZE值是固定的。
在XP系統上驗證了一下,發現HORZSIZE值與LOGPIXELSX的值也是不變的,如果改變HORZRES的話,則HORZSIZE會發生相應變化,但LOGPIXELSX不變,一直是96。
/*--------------------------插入GetDeviceCaps的使用說明End-----------------------------
這些窗口範圍表示包含顯示器全部寬度和高度的邏輯單位元數值。320毫米寬的屏幕(MM_HIMETRIC下屏幕寬度的邏輯單位爲0.01mm,共32000個間隔,故1024像素實際中對應32000*0.01=320mm)也爲1260 MM_LOENGLISH單位或12.6英寸(320除以25.4毫米/英寸)。
首先要明確圖形只能在設備環境的座標系中顯示。其次要注意視口範圍中,視口座標系的y軸前面的負號表示改變了畫筆dc移動時y軸的方向。對於這五種映像方式,視口範圍相當於顯示屏沿着x軸向屏幕上方摺疊後位於實際顯示屏上方的矩形區域,想象一下當前顯示屏上面放置了一個同樣的大小的虛擬顯示屏,dc的y值隨dc的上升而增加,dc向上移動然後然後繪製圖形將顯示在虛擬屏幕中,這個事實有一個有趣的結果。此時要想在設備環境的顯示區域顯示任何東西,必須使用負的y值才能將畫筆移動到真實的顯示屏上。
例如下面的程序代碼:
void CDemoView::OnPaint()
{
CPaintDC dc(this); // device context for painting
dc.SetMapMode(MM_LOMETRIC);
//dc.TextOut(400,400,"Hello"); //看不到屏幕上的Hello,此時dc畫到顯示屏上方的虛擬顯示屏上了。
dc.TextOut(400,-400,"Hello"); //可以看出屏幕上的Hello
}
爲了使自己保持頭腦清醒,您可能想避免這樣做。
1)一種解決辦法是將邏輯的(0,0)點設爲顯示區域的左下角,您可以通過呼叫SetViewportOrgEx (hdc, 0, cyClient, NULL) ;(假設cyClient是以像素爲單位的顯示區域的高度)。此時的座標系是直角座標系的右上象限。這相當於將視口的原點從設備環境的左上角移動到左下角,將當前顯示屏正上方那個虛擬顯示屏向下移動,覆蓋掉當前的顯示屏區域,此時視口的區域和設備環境顯示區域重合,所以視口中的物體能顯示到計算機屏幕上。例如下面的程序代碼:
void CDemoView::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect Recto;
GetClientRect(&Recto);
dc.SetMapMode(MM_LOMETRIC);
dc.SetViewportOrg(0,Recto.Height());
dc.TextOut(400,400,"Hello"); //結果與題設的結果相同!
}
2)另一種方法是將邏輯(0,0)點設爲顯示區域的中心:
SetViewportOrgEx (hdc, cxClient / 2, cyClient / 2, NULL) ;代碼如下
void CDemoView::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect Recto;
GetClientRect(&Recto);
dc.SetMapMode(MM_LOMETRIC);
dc.SetViewportOrg(Recto.Width()/2,Recto.Height()/2);
dc.TextOut(400,400,"Hello");
} 現在,我們有了一個真正的4象限笛卡爾座標系,在x軸和y軸上有相等的按英寸、毫米或twip計算的邏輯單位。
3)您還可以使用SetWindowOrgEx函數來改變邏輯(0,0)點,但是這稍微困難一些,因爲SetWindowOrgEx的參數必須使用邏輯單位,先要將(cxClient,cyClient)用DPtoLP函數轉換爲邏輯座標。假設變量pt是型態爲POINT的結構,下面的代碼將邏輯(0,0)點改變到顯示區域的中央:
pt.x = cxClient ;
pt.y = cyClient ;
DptoLP (hdc, &pt, 1) ;
SetWindowOrgEx (hdc, -pt.x / 2, -pt.y / 2, NULL) ;代碼如下
void CDemoView::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect Recto;
CPoint pt;
GetClientRect(&Recto);
dc.SetMapMode(MM_LOMETRIC);
pt.x = Recto.Width()/2;
pt.y = Recto.Height()/2;
DPtoLP(dc,&pt,1);
dc.SetWindowOrg(-pt.x,-pt.y);
dc.TextOut(400,400,"Hello"); //結果與2)相同
}
二、MM_ISOTROPIC和MM_ANISOTROPIC映射模式
1)Isotropic的意思是“同方向性”。 如果想要在使用任意的軸時都保證兩個軸上的邏輯單位相同,邏輯座標系中的圓形在設備環境中現實爲圓形,則MM_ISOTROPIC映射方式就是理想的映射方式。這時具有相同邏輯寬度和高度的矩形顯示爲正方形,具有相同邏輯寬度和高度的橢圓顯示爲圓。當您剛開始將映射方式設定爲MM_ISOTROPIC時,Windows使用與MM_LOMETRIC同樣的窗口和視口範圍(但是,不要對此有所依賴)。區別在於,您現在可以呼叫SetWindowExt和SetViewportExt來根據自己的偏好改變範圍了,然後,Windows將調整範圍的值,以便兩條軸上的邏輯單位有相同的實際距離。
SetWindowExt(int Lwidth, int Lheight) //參數的單位爲邏輯單位(Logical);
SetViewportExt(int Pwidth, int Pheight) //參數的單位爲像素(Pixel);
以x軸爲例(y軸類似),邏輯座標系中的x軸的單位刻度=| Pwidth | / | Lwidth |表示x軸上一個邏輯單位等於多少個像素。比如我們先通過GetDeviceCap(LOGPIXELSX)獲得在我們的顯示器上每英寸等於多少個像素,設爲p,然後我們將它賦給Pwidth,將Lwidth賦成2,即Pwidth / Lwidth=p / 2。那麼此時邏輯座標系x軸上的單位刻度就是p / 2個像素;又由於p個像素是代表一個英寸的,所以此時的邏輯座標系x軸上的單位刻度同時也是半個英寸。還有一點要注意的是,如果Lwidth與Pwidth同號,邏輯座標的x軸方向與設備座標系中的x軸方向相同,否則相反。此外,當使用MM_ISOTROPIC模式時,如果通過計算window與viewport範圍的比值得到兩個方向的單位刻度值不同,那麼將會以較小的那個爲準。
例如,假設您想要一個「傳統的」單象限虛擬座標系,其中(0,0)在顯示區域的左下角,寬度和高度的範圍都是從0到2000,並且希望x和y軸的單位具有同樣的實際尺寸。以下就是所需的程序:
SetMapMode (hdc, MM_ISOTROPIC) ;
SetWindowExtEx (hdc, 2000, 2000, NULL) ;
SetViewportExtEx (hdc, cxClient, -cyClient, NULL) ;
SetViewportOrgEx (hdc, 0, cyClient, NULL) ;
如果其後用GetWindowExtEx和GetViewportExtEx函數獲得了窗口和視端口的範圍,可以發現,它們並不是先前指定的值。Windows將根據顯示設備的縱橫比來調整範圍,以便兩條軸上的邏輯單位表示相同的實際尺寸。如果顯示區域的寬度大於高度(以實際尺寸爲準),Windows將調整x的範圍,以便邏輯窗口比顯示區域視口窄。這樣,邏輯窗口將放置在顯示區域的左邊。代碼如下:
void CDemoView::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect Recto;
GetClientRect(&Recto);
dc.SetMapMode(MM_ISOTROPIC);
dc.SetWindowExt(2000,2000); //現實中的邏輯座標系的x軸有2000個單位,y軸也有2000個單位。
dc.SetViewportExt(Recto.Width(),-Recto.Height()); // Lwidth與Pwidth異號,所以邏輯座標系的y軸垂直向上
dc.SetViewportOrg(0,Recto.Height());//將視口原點區域下移到設備環境的左下方,此時窗口中的物體可以顯示到設備環境上(顯示屏)
dc.Ellipse(-2000,-2000,2000,2000);
dc.MoveTo(0,0);
dc.LineTo(2000,2000);
}
前面給出的程序代碼等價的用SetWindowOrg改爲:
SetMapMode (MM_ISOTROPIC) ;
SetWindowExtEx (hdc, 2000, 2000, NULL) ;
SetViewportExtEx (hdc, cxClient, -cyClient, NULL) ;
SetWindowOrgEx (hdc, 0, 2000, NULL) ;
在呼叫SetWindowOrgEx中,我們將邏輯點(0, 2000)映像爲設備點(0,0)。程序代碼如下
void CDemoView::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect Recto;
GetClientRect(&Recto);
dc.SetMapMode(MM_ISOTROPIC);
dc.SetWindowExt(2000,2000); //現實中的邏輯座標系的x軸有500個單位,y軸也有500個單位。
dc.SetViewportExt(Recto.Width(),-Recto.Height()); // Lwidth與Pwidth異號,所以邏輯座標系的y軸垂直向上,與設備環境的y軸相反
dc.SetWindowOrg(0,2000); //將邏輯點(0, 2000)映射爲設備點(0,0),邏輯點(0,0)恰好在設備點(0,cyClient)
dc.Ellipse(-2000,-2000,2000,2000);
dc.MoveTo(0,0);
dc.LineTo(2000,2000);
}
您也許想要使用一個四象限的笛卡爾座標系,四個方向的座標尺度可以任意指定,(0,0) 必須居於顯示區域的中央。如果您想要每條軸的範圍從0到1000,則可以使用以下程序代碼:
SetMapMode (hdc, MM_ISOTROPIC) ;
SetWindowExtEx (hdc, 1000, 1000, NULL) ;
SetViewportExtEx (hdc, cxClient / 2, -cyClient / 2, NULL) ;
SetViewportOrgEx (hdc, cxClient / 2, cyClient / 2, NULL) ;
void CDemoView::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect Recto;
GetClientRect(&Recto);
dc.SetMapMode(MM_ISOTROPIC);
dc.SetWindowExt(2000,2000); //現實中的邏輯座標系的x軸有2000個單位,y軸也有2000個單位。
dc.SetViewportExt(Recto.Width(),-Recto.Height()); // Lwidth與Pwidth異號,所以邏輯座標系的y軸垂直向上,與設備環境的y軸相反
dc.SetViewportOrg(Recto.Width()/2,Recto.Height()/2);
dc.Ellipse(-1000,-1000,1000,1000);
dc.MoveTo(0,0);
dc.LineTo(2000,2000);
}
在MM_ISOTROPIC映像方式下,可以使邏輯單位大於像素。例如,假設您想要一種映像方式,使點(0,0)顯示在屏幕的左上角,y的值向下增長(和MM_TEXT相似),但是邏輯座標單位爲1/16英寸。以下是一種方法:
SetMapMode (hdc, MM_ISOTROPIC) ;
SetWindowExtEx (hdc, 16, 16, NULL) ;
SetViewportExtEx (hdc, GetDeviceCaps (hdc, LOGPIXELSX),
GetDeviceCaps (hdc, LOGPIXELSY), NULL) ;
SetWindowExtEx函數的參數指出了每一英寸中邏輯單位數。SetViewportExtEx函數的參數指出了每一英寸中實際單位數(像素)。
然而,這種方法與Windows NT中的度量映像方式不一致。這些映射方式使用顯示器的像素大小和公制大小。要與度量映像方式保持一致,可以這樣做:
SetMapMode (hdc, MM_ISOTROPIC) ;
SetWindowExtEx (hdc, 160 * GetDeviceCaps (hdc, HORZSIZE) / 254,
160 * GetDeviceCaps (hdc, VERTSIZE) / 254, NULL) ;
SetViewportExtEx (hdc, GetDeviceCaps (hdc, HORZRES),
GetDeviceCaps (hdc, VERTRES), NULL) ;
在這個程序代碼中,視埠範圍設定爲按像素計算的整個屏幕的大小,窗口範圍則必須設定爲以1/16英寸爲單位的整個屏幕的大小。GetDeviceCaps以HORZRES和VERTRES爲參數,傳回以毫米爲單位的設備尺寸。如果我們使用浮點數,將把毫米數除以25.4,轉換爲英寸,然後,再乘以16以轉換爲l/16英寸。但是,由於我們使用的是整數,所以先乘以160,再除以254。
當然,這種座標系會使邏輯單位大於實際單位。在設備上輸出的所有東西都將映像爲按1/16英寸增量的座標值。當然,這樣就不能畫兩條間隔l/32英寸的水平直線,因爲這樣將需要小數邏輯座標。
2)在MM_ANISOTROPIC映射方式下,Windows不對您所設定的值進行調整,這就是說,MM_ANISOTROPIC不需要維持正確的縱橫比。使用MM_ANISOTROPIC的一種方法是對顯示區域使用任意座標,就像我們對MM_ISOTROPIC所做的一樣。下面的程序代碼將點(0,0)設定爲顯示區域的左下角,x軸和y軸都從0到2000:
SetMapMode (hdc, MM_ANISOTROPIC) ;
SetWindowExtEx (hdc, 2000, 2000, NULL) ;
SetViewportExtEx (hdc, cxClient, -cyClient, NULL) ;
SetViewportOrgEx (hdc, 0, cyClient, NULL) ;
在MM_ISOTROPIC方式下,相似的程序代碼導致顯示區域的一部分在軸的範圍之外。但是對於MM_ANISOTROPIC,不論其尺度多大,顯示區域的右上角總是(2000, 2000)。如果顯示區域不是正方形的,則邏輯x和y的單位具有不同的實際尺度。代碼如下:
void CDemoView::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect Recto;
GetClientRect(&Recto);
dc.SetMapMode(MM_ANISOTROPIC); //和MM_ISOTROPIC進行對比便清楚
dc.SetWindowExt(2000,2000); //現實中的邏輯座標系的x軸有2000個單位,y軸也有2000個單位。
dc.SetViewportExt(Recto.Width(),-Recto.Height()); // Lwidth與Pwidth異號,所以邏輯座標系的y軸垂直向上
dc.SetViewportOrg(0,Recto.Height());//將視口原點區域下移到設備環境的左下方,此時窗口中的物體可以顯示到設備環境上(顯示屏)
dc.Ellipse(-2000,-2000,2000,2000);
dc.MoveTo(0,0);
dc.LineTo(2000,2000);
}
另一種使用MM_ANISOTROPIC的方法是將x和y軸的單位固定,但其值不相等。例如,如果有一個只顯示文字的程序,您可能想根據單個字符的高度和寬度設定一種粗刻度的座標:
SetMapMode (hdc, MM_ANISOTROPIC) ;
SetWindowExtEx (hdc, 1, 1, NULL) ;
SetViewportExtEx (hdc, cxChar, cyChar, NULL) ;
當然,這裏假設cxChar和cyChar分別是那種字體的字符寬度和高度。現在,您可以按字符行和列指定座標。下面的敘述在距離顯示區域左邊三個字符,上邊二個字符處顯示文字:
TextOut (hdc, 3, 2, TEXT ("Hello"), 5) ;
三、窗口、視口以及映射模式的意義
MFC缺省地使用MM_TEXT映射模式,一個邏輯單位等於設備中的一個象素。它是實現“所見即所得”的基礎。其實大部分情況下使用像素進行工作是很合適的,不需要使用除了MM_TEXT方式外的任何映射方式。但是如果你需要進行類似CAD這樣的繪圖時,需要以英寸或者釐米尺寸顯示圖像,就需要使用其他映射方式,以方便編程。因爲只要你把映射方式確定後,你只需要在邏輯空間使用邏輯單位繪製圖像就好了,無需擔心怎樣顯示在真實的屏幕或打印機上,這些繁瑣的工作Windows會做好的。
這時可能會有人問,要是使用cm作爲邏輯單位,Windows是如何確定1cm的真實長度呢?我悄悄地告訴你,“Windows 它根本不知道!”,別驚訝,聽我接着說,Windows它確實不知道,但是Windows知道像素,這個很重要,它會以像素爲依據計算得出1cm的長度(不一定是真實長度)。當程序需要繪製了一個10cm×10cm的矩形時,如果需要顯示在打印機上,那麼Windows首先通過GetDeviceCaps獲取打印機相關信息如每英寸顯示320像素(點),而1英寸≈2.54釐米,那麼Windows就可以計算出10cm其實就是(320/2.54)*10≈1259.8點。10cm×10cm矩形=1259.8點×1259.8點矩形,打印機上完美的顯示了10cm×10cm的矩形沒有任何問題,10cm的長度絕對正確,不信你可以用尺子量!
打印機上可以這樣做,但是顯示器上是不行的,此時Windows給出了一個辦法,當然它也是根據人眼的視覺經驗,即讓顯示器每英寸顯示96點(正常字體),此時96/2.54≈37.8點,人眼看着很好,但是1cm的長度可能是不真實的。這些不重要,重要的是打印出來絕對正確就好了。
四、VC++中窗口、視口以及映射模式的應用舉例
例1. 驗證世界窗口的原點和視口的原點位於同一個點,初始時它們與設備座標系的原點重合均在客戶區的左上角。
void CDemoView::OnPaint()
{
CPaintDC dc(this); // 繪圖的設備上下文
CRect Recto;
CPen PenBlue;
PenBlue.CreatePen(PS_SOLID, 1, RGB(0, 12, 255));
//dc.SetViewportOrg(100,100); // Step 1: 僅加上這一句後可以看出,視口原點的下移和客戶區的下移
//dc.SetWindowOrg(-100,-100); // Setp 2: 將Step 1註釋掉,加上此句可以得到和Step 1相同的結果
dc.SelectObject(&PenBlue);
dc.Ellipse(-100, -100, 100, 100); //圓心位於屏幕的左上角,僅僅只有圓的四分之一部分(270度到360度的部分)顯示在屏幕上。
dc.MoveTo(0,0);
dc.LineTo(100,100);
CPen PenBlack;
PenBlack.CreatePen(PS_SOLID, 1, BLACK_PEN);
dc.SelectObject(&PenBlack);
GetClientRect(&Recto); // 得到客戶區域的尺寸,我以前沒看出客戶區的左上角就是視口的原點,而誤以爲是設備座標的原點了。
dc.MoveTo(Recto.Width() / 2, 0); //dc的移動是參考視口原點畫筆的相對移動,注意這裏視口的原點(也是窗口原點)並沒有移動
dc.LineTo(Recto.Width() / 2, Recto.Height());
dc.MoveTo(0, Recto.Height() / 2);
dc.LineTo(Recto.Width(), Recto.Height() / 2);
}
例2. 驗證世界窗口的原點和視口的原點位於同一個點,默認MM_TEXT映射模式下x軸水平向右,y軸垂直向下。其他固定映射模式的使用
void CDemoView::OnPaint()
{
CPaintDC dc(this); // device context for painting
dc.SetMapMode(MM_TEXT); //讀者可以將MM_TEXT換爲MM_LOMETRIC等其他的固定映射模式
dc.SetViewportOrg(380, 220);
// Use a red pen
CPen PenRed(PS_SOLID, 1, RGB(255, 0, 0));
dc.SelectObject(PenRed);
// A circle whose center is at the origin (0, 0)
dc.Ellipse(-100, -100, 100, 100);
// Use a blue pen
CPen PenBlue(PS_SOLID, 1, RGB(0, 0, 255));
dc.SelectObject(PenBlue);
// Horizontal axis
dc.MoveTo(-380, 0);
dc.LineTo(380, 0);
// Vertical axis
dc.MoveTo(0, -220);
dc.LineTo(0, 220);
// An orange pen
CPen PenOrange(PS_SOLID, 1, RGB(255, 128, 0));
dc.SelectObject(PenOrange);
// A diagonal line at 45 degrees
dc.MoveTo(0, 0);
dc.LineTo(120, 120); //直線沒有在笛卡爾座標系的45度位置,而是位於笛卡爾座標系統的第四象限
}
例3. 自定義映射模式MM_ISOTROPIC的使用
void CDemoView::OnPaint()
{
CPaintDC dc(this); // device context for painting
dc.SetMapMode(MM_ISOTROPIC);
dc.SetWindowExt(10240,7680);
dc.SetViewportExt(1024,768);
dc.Ellipse(-100,-100,100,100); //以客戶區左上角(0,0)畫出一個半徑爲10pixels的圓
}其本質就是,X方向,每個邏輯單位有1024/10240個象素,Y方向每個邏輯單位有768/7680個象素。因此以下代碼有相同的作用。
void CDemoView::OnPaint()
{
CPaintDC dc(this); // device context for painting
dc.SetMapMode(MM_ISOTROPIC);
dc.SetWindowExt(102400,76800);
dc.SetViewportExt(10240,7680);
dc.Ellipse(-100,-100,100,100);
}
例4. 自定義映射模式MM_ANISOTROPIC的使用以左上角爲原點,X軸和Y軸爲1000的座標
void CDemoView::OnDraw(CDC* pDC)
{
CDemoDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
CRect rect;
GetClientRect(&rect);
pDC->SetMapMode(MM_ANISOTROPIC);
//pDC->SetViewportOrg(0,0); //可以註釋掉
pDC->SetViewportExt(rect.right,rect.bottom);
pDC->SetWindowOrg(0,0);
pDC->SetWindowExt(1000,1000);
pDC->MoveTo(50,50);
pDC->LineTo(50,950);
pDC->LineTo(950,950);
pDC->LineTo(50,50);
}
代碼分析:
1. GetClientRect(&rect); 取得客戶區矩形區域,將其存放在rect中
2. 用pDC->SetMapMode(MM_ANISOTROPIC); 設置映射模式
3. 通過pDC->SetViewportOrg(0,0);設置邏輯座標的原點。
4. 通過pDC->SetViewportExt(rect.right,rect.bottom);和
pDC->SetWindowExt(1000,1000);來確定邏輯座標下和設備座標下的尺寸對應關係
5. 在MM_ANISOTROPIC模式下,X軸單位和Y軸單位可以不相同
6. 座標方向的確定方法是如果邏輯窗範圍和視口範圍符號相同,則邏輯座標的方向和視口的方向相同,即X軸向右爲正,Y軸向下爲正。
7. 如果將顯示模式改爲MM_ISOTROPIC,那麼X軸單位和Y軸單位一定相同,感興趣的讀者可以自己使一下。
例5.繪製點狀網格
void CDemoView::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect Recto;
GetClientRect(&Recto);
CBrush bgBrush(BLACK_BRUSH);
dc.SelectObject(bgBrush);
dc.Rectangle(Recto);
for(int x = 0; x < Recto.Width(); x += 20)
{
for(int y = 0; y < Recto.Height(); y += 20)
{
dc.SetPixel(x, y, RGB(255, 255, 255));
}
}
}注意屏幕閃的厲害時,可以添加如下代碼
BOOL CDemoView::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
}
歷史上的今天
- 【轉載】末法衆生,根機淺薄,匪仗佛力,決難了脫。欲仗修自力了2013-04-28 17:14:46
- 窄帶信號和寬帶信號的區別和聯繫2012-04-28 13:14:34
- 恭迎釋迦牟尼佛誕辰日2012-04-28 07:14:49
評論