词条 | GDI+坐标系 |
释义 | GDI+坐标系有三个:World(世界坐标系)、Page(逻辑/页面坐标系)、Device(设备坐标系) 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编译初步 |
随便看 |
百科全书收录4421916条中文百科知识,基本涵盖了大多数领域的百科知识,是一部内容开放、自由的电子版百科全书。