当前位置首页 > 百科> 正文

GDI+坐标系

2019-10-18 15:21:49 百科

GDI+坐标系

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 [in] 视窗句柄
rotDeg [in] 旋转角,逆时针旋转为正,单位:度
a [in] 长半轴,单位:0.1mm
b [in] 短半轴,单位: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编译初步
声明:此文信息来源于网络,登载此文只为提供信息参考,并不用于任何商业目的。如有侵权,请及时联系我们:baisebaisebaise@yeah.net