GDI+坐標系

Lin 在Win SetWorldTran

GDI +坐標系
一、GDI+坐標系
GDI+坐標系有三個:World(世界坐標系)、Page(邏輯/頁面坐標系)、Device(設備坐標系)
下面的代碼將在設備上畫一條線:
MoveToEx(hdc,xw1,yw1,NULL);
LineTo(hdc,xw2,yw2,NULL);
(xw1,yw1) 和(xw2,yw2)是世界坐標系中的兩個點,Windows 首先將其轉換為邏輯坐標,然後再轉換為設備坐標,最後畫出一條直線段。
二、世界坐標轉換為邏輯坐標
在Win98下,世界坐標系與邏輯坐標系是重合的,沒有什麼分別。在WinNT下,世界坐標系與邏輯坐標系默認也是重合的,要將世界坐標系與邏輯坐標系分別開來,首先需要SetGraphicsMode(hdc,GM_ADVANCED),然後調用SetWorldTransform設定世界坐標系與邏輯坐標系之間的轉換關係。
SetWorldTransform的原型如下:
BOOL SetWorldTransform(HDC hdc,CONST XFORM *lpXform);
描述轉換關係的結構XFORM,其定義如下:
typedef struct _XFORM
{
FLOAT eM11;
FLOAT eM12;
FLOAT em21;
FLOAT eM22;
FLOAT eDx;
FLOAT eDy;
}XFORM;
各個參數就是變換矩陣中的元素:
(1)
其中,是邏輯坐標,是世界坐標
(1)式展開可得
(2)
設定轉換關係後,可以對轉換關係進行修改:
BOOL ModifyWorldTransform(HDC hdc,CONST XFORM *lpXform,DWORD iMode);
iMode 為 MWT_IDENTITY 表示世界坐標系與邏輯坐標系重合,此時lpXform無用;
iMode 為 MWT_LEFTMULTIPLY 表示增加lpXform變換,
新變換矩陣 = lpXform ×老變換矩陣
iMode 為 MWT_RIGHTMULTIPLY 表示增加lpXform變換,
新變換矩陣 =老變換矩陣×lpXform
還可以對變換進行合併:
BOOL CombineTransform(LPXFORM lpxformResult
,CONST XFORM *lpxform1,CONST XFORM *lpxform2);
lpxformResult = lpxform1×lpxform2
三、邏輯坐標與設備坐標的相互轉換
1、對於 MM_TEXT 而言,邏輯坐標系與設備坐標系僅有平移關係。
假定有一個點在邏輯坐標系下的坐標為,在設備坐標系下的坐標為,則轉換關係為:
代碼里應設定轉換關係:
SetWindowOrgEx(HDC hdc,xL0,yL0,NULL);
SetViewportOrgEx(HDC hdc,xD0,yD0,NULL);
2、對於MM_HIENGLISH、MM_HIMETRIC、MM_LOENGLISH、MM_LOMETRIC、MM_TWIPS而言,邏輯坐標系與設備坐標系有平移、縮放關係。
假定有一個點在邏輯坐標系下的坐標為,在設備坐標系下的坐標為,則轉換關係為:
代碼里應設定轉換關係:
SetWindowOrgEx(HDC hdc,xL0,yL0,NULL);
SetViewportOrgEx(HDC hdc,xD0,yD0,NULL);
表示橫向每毫米的像素數:
表示縱向每毫米的像素數:
Unit的取值因MM_HIENGLISH、MM_HIMETRIC、MM_LOENGLISH、MM_LOMETRIC、MM_TWIPS而異,詳見下表:
3、對於MM_ANISOTROPIC和MM_ISOTROPIC而言
假定:一段平行於x軸的直線段在邏輯坐標系下的長度為LenXL,在設備坐標系下的長度為LenXD;一段平行於y軸的直線段在邏輯坐標系下的長度為LenYL,在設備坐標系下的長度為LenYD;某一點在邏輯坐標系下的坐標為,在設備坐標系下的坐標為,則轉換關係如下:
代碼里應設定轉換關係:
SetWindowExtEx(HDC hdc,LenXL,LenYL,NULL);
SetViewportExtEx(HDC hdc,LenXD,LenYD,NULL);
SetWindowOrgEx(HDC hdc,xL0,yL0,NULL);
SetViewportOrgEx(HDC hdc,xD0,yD0,NULL);
注意:LenYL 與LenYD異號,則邏輯y軸與設備y軸方向將相反;LenXL 與LenXD異號,則邏輯x軸與設備x軸方向將相反。
這裡要特彆強調MM_ISOTROPIC,它的意思是各向同性,即:X、Y軸的比例相同。此時,必須先調用SetWindowExtEx,然後再調用SetViewportExtEx。因為調用後者時Windows會根據前者的設定值修改LenXD或LenYD,使得X、Y軸的比例相同。具體修改如下:
取和的最小值minScale,修改LenXD、LenYD如下:
此時,
這樣,既保證了比例尺相同,又能最大限度的顯示視窗(邏輯坐標系下的一個矩形)內容。
四、文字顯示
請看下面的代碼
void GDITest(HWND hwnd)
{
HDC hDC = GetDC(hwnd);
TCHAR* szStr = _T("GDI測試");
RECT rect;
LOGFONT logFont;
SetGraphicsMode(hDC,GM_ADVANCED); //(3)
SetMapMode(hDC, MM_ISOTROPIC);
SetWindowExtEx(hDC,5000, ±5000,NULL); //(4)
GetClientRect(hwnd,&rect);
SetViewportExtEx(hDC,rect.right,rect.bottom,NULL);
SetViewportOrgEx(hDC,rect.right >> 1,rect.bottom >> 1,NULL);
ZeroMemory(&logFont,sizeof(logFont));
logFont.lfCharSet = GB2312_CHARSET;
logFont.lfHeight = -1000;
lstrcpy(logFont.lfFaceName,_T("宋體"));
HFONT hFont = CreateFontIndirect(&logFont);
SelectObject(hDC,hFont);
TextOut(hDC,0,0,szStr,lstrlen(szStr));
DeleteObject(SelectObject(hDC,GetStockObject(SYSTEM_FONT)));
ReleaseDC(hwnd,hDC);
}
(4)中的±對輸出是有影響的,見下圖。亦即:當邏輯坐標系y軸與設備坐標系y軸方向相反時,會導致字型輸出的上下顛倒。
將(3)改為SetGraphicsMode(hDC,GM_COMPATIBLE);則±對輸出毫無影響。這些說明:
1、對於SetGraphicsMode(hDC,GM_COMPATIBLE)而言,Windows 僅將文字高度和文字參考點(reference point——輸出文字時的基準點)轉換到設備坐標系,然後在設備坐標系下進行文本輸出
2、對於SetGraphicsMode(hDC,GM_ADVANCED)而言,對文本的處理就比較麻煩了:我猜測是將所有文本的輪廓點從世界坐標系轉換至設備坐標系,然後再內部填充處理。這樣的轉換更加徹底、完美!
五、套用實例
下面的代碼將在視窗客戶區畫一個傾斜橢圓
#include <MATH.H>
/*****************************************************************************\
在視窗的客戶區畫傾斜橢圓,橢圓中心在客戶區中心
hwnd &#91;in&#93; 視窗句柄
rotDeg &#91;in&#93; 旋轉角,逆時針旋轉為正,單位:度
a &#91;in&#93; 長半軸,單位:0.1mm
b &#91;in&#93; 短半軸,單位:0.1mm
\*****************************************************************************/
void DrawRotateEllipse(HWND hwnd,float rotDeg,int a,int b)
{
HDC hDC = GetDC(hwnd);
XFORM xForm;
//角度轉換為弧度
rotDeg *= 0.017453292519943295769236907684886f;
{//設定世界坐標系與邏輯坐標系的轉換關係
SetGraphicsMode(hDC, GM_ADVANCED);
xForm.eM11 =
xForm.eM22 = (FLOAT) cos(rotDeg);
xForm.eM12 = (FLOAT) sin(rotDeg);
xForm.eM21 = -xForm.eM12;
xForm.eDx =
xForm.eDy = 0.0f;
SetWorldTransform(hDC, &xForm);
}
SetMapMode(hDC, MM_LOMETRIC);
{//設定視口原點
RECT rect;
GetClientRect(hwnd,&rect);
SetViewportOrgEx(hDC,rect.right >> 1,rect.bottom >> 1,NULL);
}
//畫8cm*4cm的橢圓
#if 0
//橢圓中心邏輯坐標為(0,0)
Ellipse(hDC,-a,b,a,-b);
#else
//橢圓中心坐標為:世界坐標——(400,0) 邏輯坐標——(283,283)
SetWindowOrgEx(hDC,(int)(xForm.eM11 * a),(int)(xForm.eM12 * a),NULL);
Ellipse(hDC,0,b,2 * a,-b);
#endif
ReleaseDC(hwnd,hDC);
}
畫橢圓的時候有兩種方法:
1、世界坐標系下,橢圓中心坐標為(0,0),長軸在 X 軸上,短軸在 Y 軸上。此時,邏輯坐標系下,橢圓中心坐標也為(0,0)。所以只要一行代碼即可:
Ellipse(hDC,-a,b,a,-b);
2、世界坐標系下,橢圓中心坐標為(a,0),長軸在 X 軸上,短軸在 Y 軸上。此時,邏輯坐標系下,橢圓中心坐標為(,)。所以需要兩行代碼:
SetWindowOrgEx(hDC,(int)(xForm.eM11 * a),(int)(xForm.eM12 * a),NULL);
Ellipse(hDC,0,b,2 * a,-b);
這說明:SetWindowOrgEx中的坐標是邏輯坐標,而不是世界坐標。
實際運行結果:在Win98下,無法旋轉;在WinXP下,可以旋轉,見下圖。
上一篇:
道路斷鏈
下一篇:
VC編譯初步

相關詞條

相關搜尋

熱門詞條

聯絡我們