VC++中GDI和GDI+ 的坐标系统介绍

在Windows应用程序中,只要进行绘图,就要使用GDI坐标系统。Windows提供了几种映射方式,每一种映射都对应着一种坐标系。例如,绘制图形时,必须给出图形各个点在客户区的位置,其位置用x 和y两个坐标表示,x 表示横坐标,y表示纵坐标。在所有的GDI绘制函数中,这些坐标使用的是一种“逻辑单位”。当GDI函数将结果输出送到某个物理设备上时,Windows将逻辑坐标转换成设备坐标(如屏幕或打印机的像素点)。本文讨论了图形环境中的各个映射模式,包括它们是什么,怎么工作的,以及它们真正的含义。

  一、基础知识
  

 

 三、更改坐标系统
 
  
正如上面所看到的,默认的坐标系统坐标原点位于窗口的左上角,水平轴的正方向向右,垂直轴的正方向向下。为了进一步说明这一点,让我们来绘制一个半径为50个单位,圆心位于(0,0)点,同时绘制一个连接(0,0)(100,100)两点的直线。
 


void CExoDraw1View::OnPaint() 

{

 CPaintDC dc(this); // device context for painting

 // A circle whose center is at the origin (0, 0)

 dc.Ellipse(-50, -50, 50, 50);

 // A line that starts at (0, 0) and ends at (100, 100)

 dc.MoveTo(0, 0);

 dc.LineTo(100, 100);

}


图四、代码效果图

 

  这种默认的坐标原点在大多数图形操作情况下是适用的,但并不是总适用,有时你需要控制坐标系统的原点,例如,很多CAD(图形辅助设计)应用程序就需要用户来定义坐标系统的原点。
 

  MFC提供了各种函数来处理坐标定位及扩展绘制区域的问题,包括在屏幕上任意位置设置坐标原点的函数。因为你是在一个设备上下文上进行绘图操作,因此,你所需要做的就是调用CDC::SetViewportOrg()函数。这个函数重载了两个版本,这允许你使用X、Y坐标或是一个定义的Point点。这个函数的语法如下:
 


SetViewportOrg(int X, int Y);

SetViewportOrg(CPoint Pt);

 

  调用这个函数时只需要简单地说明哪儿是你想定义的坐标原点,如果使用函数的第二个版本,参数可以是一个POINT结构或是一个MFC提供的Tpoint类。为了演示这个函数的效果,让我们将上例的坐标原点沿X轴正方向移动200个单位,Y轴正方向移动150个单位,这时绘制函数如下:

 


void CExoDraw1View::OnPaint() 

{

 CPaintDC dc(this); //绘图的设备上下文;

 dc.SetViewportOrg(200, 150);

 // 圆心位于坐标原点(0, 0)

 dc.Ellipse(-50, -50, 50, 50);

 // 连接(0, 0) 和 (100, 100)点的直线;

 dc.MoveTo(0, 0);

 dc.LineTo(100, 100);

}

 

 
 图五、代码效果图
 

  需要注意的是,你也可以相对于客户区域来指定坐标原点
 


void CExoDraw1View::OnPaint()  

{

 CPaintDC dc(this); //绘图的设备上下文;

 CRect Recto;

 //获取客户区尺寸;

 GetClientRect(&Recto);

 dc.SetViewportOrg(Recto.Width() / 2, Recto.Height() / 2);

 // A circle whose center is at the origin (0, 0)

 dc.Ellipse(-50, -50, 50, 50);

 // A line that starts at (0, 0) and ends at (100, 100)

 dc.MoveTo(0, 0);

 dc.LineTo(100, 100);

}


  图六、代码效果图 

  现在你已了解了如何设置坐标原点,让我们来将(380,220)点作为坐标原点,并绘制出笛卡尔的坐标轴:
 


void CExoDraw1View::OnPaint()  

{

 CPaintDC dc(this); // device context for painting

 CRect Recto;

 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);

}


 图七、代码效果图 

  正如已经看到的,SetViewportOrg()函数可以更改设备上下文的坐标原点,同时,它也用来规定坐标轴的正方向,即水平轴向右,垂直轴向下:


 图八、坐标轴示意图 

  为了说明这一点,下面来绘制一条黄色的45度角的直线:

 


void CExoDraw1View::OnPaint() 

{

 CPaintDC dc(this); // device context for painting

 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度位置,而是位于坐标系统的第四象限,造成这种情况的原因是默认的坐标系统。

三、固定映射模式 

  为了控制设备上下文中的坐标轴的方向,可以使用CDC类的SetMapMode()函数,它的语法如下:

 

int SetMapMode(int nMapMode);

 

  这个函数将根据参数的设置的不同做两件事,一是控制坐标轴的方向;二是坐标系统的单位长度。

 

  这个函数的参数是用来定义映射模式的整型常量。它可能的值是:MM_TEXT, MM_LOENGLISH、MM_HIENGLISH、MM_ANISOTROPIC、MM_HIMETRIC, MM_ISOTROPIC、 MM_LOMETRIC, MM_TWIPS。

 

  默认情况下使用MM_TEXT映射模式。换句话说,如果你没有具体的规定某一映射模式,你的应用程序就将使用MM_TEXT映射模式。在这种映射模式下,设备上下文中的度量尺寸将使用默认的像素单位,水平坐标轴正方向向右,垂直坐标轴正方向向下。例如,上面的OnPaint事件可以用下面的代码重写,它将产生同样的效果,仿佛没有使用映射模式。

 


void CExoDraw1View::OnPaint() 

{

 CPaintDC dc(this); // device context for painting

 dc.SetMapMode(MM_TEXT);

 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);

}


图十、代码效果图

  MM_LOENGLISH模式,与其他一些映射模式(不包括MM_TEXT模式)一样,执行两个动作,它改变坐标轴的方向,垂直坐标轴的正方向向上;



图十一、MM_LOENGLISH
映射模式下的坐标系

  

  此外,度量单位改为0.01英寸,这意味着你提供的坐标将除以100,观察上述代码的MM_LOENGLISH映射效果

 


void CExoDraw1View::OnPaint() 

{

 CPaintDC dc(this); // device context for painting

 dc.SetMapMode(MM_LOENGLISH);

 dc.SetViewportOrg(380, 220);

 . . .

}


图十二、代码效果图

  

  正如你所看到的,直线现在位于坐标系的第一象限,同时,直线比以前缩短,圆也比以前的要小。

 

  与MM_LOENGLISH映射模式相似,MM_HIENGLISH映射模式也是垂直坐标轴正向向上,只是它以0.001英寸为坐标单位,下面是它的效果:

 


void CExoDraw1View::OnPaint() 

{

 CPaintDC dc(this); // device context for painting

 dc.SetMapMode(MM_HIENGLISH);

 dc.SetViewportOrg(380, 220);

 . . . Same as previous

}


图十三、代码效果图

  

  MM_LOMETRIC映射模式使用与上两种映射模式相同的坐标轴,不同的是MM_LOMETRIC使用0.1毫米为单位,下面是一个例子:

 


void CExoDraw1View::OnPaint() 

{

 CPaintDC dc(this); // device context for painting 

 dc.SetMapMode(MM_LOMETRIC);

 dc.SetViewportOrg(380, 220);

 . . .

}


图十四、代码效果图

 

  MM_HIMETRIC使用与上述三种映射模式相同的坐标系,但它的坐标单位是0.01毫米,下面例子代码如下:

 


void CExoDraw1View::OnPaint() 

{

 CPaintDC dc(this); // device context for painting

 dc.SetMapMode(MM_HIMETRIC);

 dc.SetViewportOrg(380, 220);

 . . . Same as previous

}


图十五、代码效果图
 

 

  MM_TWIPS映射模式将每个逻辑单位(像素)除以20,实际上一twip等于1/1440 英寸,坐标系统仍然与上面几种映射方式相同。

 


void CExoDraw1View::OnPaint() 

{

 CPaintDC dc(this); // device context for painting

 CRect Recto;

 dc.SetMapMode(MM_TWIPS);

 dc.SetViewportOrg(380, 220);

 . . .

}


图十六、代码效果图

四、自定义坐标系统 

  目前为止,我们使用的映射模式可以允许我们选择坐标轴的方向,但仅仅是Y轴的方向。而且,我们不能更改坐标系统的单位,这是因为各种映射模式(MM_TEXT, MM_HIENGLISH, MM_LOENGLISH, MM_HIMETRIC, MM_LOMETRIC, and MM_TWIPS)有固定的属性集,例如坐标轴的方向和坐标单位等。在CAD应用程序中,如果你需要灵活设置坐标轴方向及坐标单位的话,应该怎么做呢?

 

  仔细研究下面的OnPaint()事件代码,它绘制了一个200X200像素大小的红边、浅绿色背景的正方形,这个正方形的顶点在(-100,-100)处,右底端位于(100,100)处。同时,从坐标原点处绘制一个45度的直线。

 


void CExoDraw1View::OnPaint() 

{

 CPaintDC dc(this); // device context for painting

 CPen PenRed(PS_SOLID, 1, RGB(255, 0, 0));

 CBrush BrushAqua(RGB(0, 255, 255));

 dc.SelectObject(PenRed);

 dc.SelectObject(BrushAqua);

 // Draw a square with a red border and an aqua background

 dc.Rectangle(-100, -100, 100, 100);

 CPen BluePen(PS_SOLID, 1, RGB(0, 0, 255));

 dc.SelectObject(BluePen);

 // Diagonal line at 45 degrees starting at the origin (0, 0)

 dc.MoveTo(0, 0);

 dc.LineTo(200, 200);

}


图十七、代码效果图

  

  正如你所看到的,我们只得到了正方形的右下部分,同时直线指向时钟的三点到六点之间的方向。假定你想将坐标原点设置与窗口中央位置,或者是更精确一点,设置于点(340, 220)处,我们已经知道可以使用CDC::SetViewportOrg()(记住,这个函数只用来更改坐标原点,它并不影响坐标轴的方向及坐标单位。同时,需要注意的是,它使用的坐标单位是像素)函数,下面是一个例子(我们没有规定映射模式,所以程序使用的是默认的MM_TEXT映射模式)。

 


void CExoDraw1View::OnPaint() 

{

 CPaintDC dc(this); // device context for painting

 dc.SetViewportOrg(340, 220);

 CPen PenRed(PS_SOLID, 1, RGB(255, 0, 0));

 CBrush BrushAqua(RGB(0, 255, 255));

 dc.SelectObject(PenRed);

 dc.SelectObject(BrushAqua);

 // Draw a square with a red border and an aqua background

 dc.Rectangle(-100, -100, 100, 100);

 CPen BluePen(PS_SOLID, 1, RGB(0, 0, 255));

 dc.SelectObject(BluePen);

 // Diagonal line at 45 degrees starting at the origin (0, 0)

 dc.MoveTo(0, 0);

 dc.LineTo(200, 200);

}


图十八、代码效果图
 

 

  为了控制你自己应用程序中的坐标系统单位,坐标轴的方向,可以使用MM_ISOTROPIC 或MM_ANISOTROPIC映射模式。第一件事是调用CDC::SetMapMode()函数,并在两个常量中选择一个(MM_ISOTROPIC或 MM_ANISOTROPIC)。下面是例子代码:


void CExoDraw1View::OnPaint() 

{

 CPaintDC dc(this); // device context for painting

 dc.SetMapMode(MM_ISOTROPIC);

 dc.SetViewportOrg(340, 220);

 CPen PenRed(PS_SOLID, 1, RGB(255, 0, 0));

 CBrush BrushAqua(RGB(0, 255, 255));

 dc.SelectObject(PenRed);

 dc.SelectObject(BrushAqua);

 // Draw a square with a red border and an aqua background

 dc.Rectangle(-100, -100, 100, 100);

 CPen BluePen(PS_SOLID, 1, RGB(0, 0, 255));

 dc.SelectObject(BluePen);

 // Diagonal line at 45 degrees starting at the origin (0, 0)

 dc.MoveTo(0, 0);

 dc.LineTo(200, 200);

}


图十九、代码效果图
 

 

  先抛开上面的图片。当调用CDC::SetMapMode(),并使用MM_ISOTROPIC或 MM_ANISOTROPIC作为参数后,并没有结束,这两种映射方式允许我们改变坐标轴的正方向及坐标单位。这两种映射方式的区别在于:MM_ISOTROPIC映射方式中水平、垂直坐标轴的单位相等,MM_ANISOTROPIC映射方式可以随意控制水平及垂直方向的坐标单位长度。

 

  所以,在调用SetMapMode()函数并规定了MM_ISOTROPIC或MM_ANISOTROPIC映射模式后,你必须调用CDC:SetWindowExt()函数,这个函数用来计算老的或默认的坐标系中一个单位的长度。这个函数有两个版本:

 


CSize SetWindowExt(int cx, int cy);

CSize SetWindowExt(SIZE size);

 

  如果使用第一版本,第一个参数CX说明了水平坐标轴上按照新的逻辑单位代表的长度,CY代表了垂直坐标轴上按照新的逻辑单位代表的长度。

 

  如果你知道按照新的坐标单位计算需要的逻辑尺寸的话,可以使用第二个版本的函数,例子代码如下:

 


void CExoDraw1View::OnPaint() 

{

 CPaintDC dc(this); // device context for painting

 dc.SetMapMode(MM_ISOTROPIC);

 dc.SetViewportOrg(340, 220);

 dc.SetWindowExt(480, 480);

 CPen PenRed(PS_SOLID, 1, RGB(255, 0, 0));

 CBrush BrushAqua(RGB(0, 255, 255));

 dc.SelectObject(PenRed);

 dc.SelectObject(BrushAqua);

 // Draw a square with a red border and an aqua background

 dc.Rectangle(-100, -100, 100, 100);

 CPen BluePen(PS_SOLID, 1, RGB(0, 0, 255));

 dc.SelectObject(BluePen);

 // Diagonal line at 45 degrees starting at the origin (0, 0)

 dc.MoveTo(0, 0);

 dc.LineTo(200, 200);

}


图二十、代码效果图

  

  调用SetWindowExt()函数后,紧接着应调用SetViewportExt()函数,它的任务是规定水平及垂直坐标轴的单位。我们可以这样认为,SetWindowExt()函数对应着“窗口”,SetViewportExt()函数对应着“视口”。SetViewportExt()函数有两个版本:

 


CSize SetViewportExt(int cx, int cy);

CSize SetViewportExt(SIZE size);

 

  上述两个函数中的参数与“窗口”中的尺寸是相互对应的,它的单位是像素。为了进一步说明这两个函数的使用,我对这两个函数进行了重新说明:

 


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范围的比值得到两个方向的单位刻度值不同,那么将会以较小的那个为准。

 

  下面是一个例子:

 


void CExoDraw1View::OnPaint() 

{

 CPaintDC dc(this); // device context for painting

 dc.SetMapMode(MM_ISOTROPIC);

 dc.SetViewportOrg(340, 220);

 dc.SetWindowExt(480, 480);

 dc.SetViewportExt(440, -680);

 CPen PenRed(PS_SOLID, 1, RGB(255, 0, 0));

 CBrush BrushAqua(RGB(0, 255, 255));

 dc.SelectObject(PenRed);

 dc.SelectObject(BrushAqua);

 // Draw a square with a red border and an aqua background

 dc.Rectangle(-100, -100, 100, 100);

 CPen BluePen(PS_SOLID, 1, RGB(0, 0, 255));

 dc.SelectObject(BluePen);

 // Diagonal line at 45 degrees starting at the origin (0, 0)

 dc.MoveTo(0, 0);

 dc.LineTo(200, 200);

}


图二十一、代码效果图

五、实例代码
 

  为了灵活使用逻辑坐标系,下面给出了几个例子代码:

 

  例1:绘制带箭头的坐标轴

 


void CExoDraw1View::OnPaint() 

{

 CPaintDC dc(this); // device context for painting

 CBrush bgBrush(BLACK_BRUSH);

 dc.SelectObject(bgBrush);

 dc.Rectangle(Recto);

 dc.SetMapMode(MM_ISOTROPIC);

 dc.SetViewportOrg(0, 440);

 dc.SetWindowExt(480, 480);

 dc.SetViewportExt(440, -680);

 CPen PenWhite(PS_SOLID, 1, RGB(255, 255, 255));

 dc.SelectObject(PenWhite);

 dc.MoveTo(21, 20);

 dc.LineTo(21, 75);

 // Up arrow

 dc.MoveTo(16, 75);

 dc.LineTo(21, 90);

 dc.LineTo(26, 75);

 dc.LineTo(16, 75);

 dc.MoveTo(21, 22);

 dc.LineTo(75, 22);

 // Right arrow

 dc.MoveTo(75, 17); 

 dc.LineTo(90, 22);

 dc.LineTo(75, 27);

 dc.LineTo(75, 17);

 

 dc.SetBkMode(TRANSPARENT);

 dc.SetTextColor(RGB(255, 255, 255));

 dc.TextOut(16, 114, ’Y’);

 dc.TextOut(100, 32, ’X’);

 dc.Rectangle(15, 15, 30, 30);

}


图二十二、代码效果图
 

    例2:绘制网格 


void CExoDraw1View::OnPaint() 

{

 CPaintDC dc(this); // device context for painting

 CRect Recto;

 GetClientRect(&Recto);

 CBrush bgBrush(BLACK_BRUSH);

 dc.SelectObject(bgBrush);

 dc.Rectangle(Recto);

 CPen PenBlue(PS_SOLID, 1, RGB(0, 0, 255));

 dc.SelectObject(PenBlue);

 for(int x = 0; x < Recto.Width(); x += 20)

 {

  dc.MoveTo(x, 0);

  dc.LineTo(x, Recto.Height()); 

 }

 

 for(int y = 0; y < Recto.Height(); y += 20)

 { 

  dc.MoveTo(0, y);

  dc.LineTo(Recto.Width(), y); 

 }

 

}


 图二十三、代码效果图

 

  例3:点状网格

 


void CExoDraw1View::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));

  } 

 }

}


图二十四、代码效果
 

 

  例4:正弦图形

 


void CExoView::OnPaint() 

{

 CPaintDC dc(this); // device context for painting

 // TODO: Add your message handler code here

 dc.SetMapMode(MM_ANISOTROPIC);

 dc.SetViewportOrg(340, 220);

 dc.SetWindowExt(1440, 1440);

 dc.SetViewportExt(-1440, -220);

 CPen PenBlue(PS_SOLID, 1, RGB(0, 0, 255));

 

 dc.SelectObject(PenBlue);

 // Axes

 dc.MoveTo(-300, 0);

 dc.LineTo( 300, 0);

 dc.MoveTo( 0, -1400);

 dc.LineTo( 0, 1400);

 // I am exaggerating with the PI value here but why not?

 const double PI = 3.141592653589793238462643383279;

 // The following two values were chosen randomly by me.

 // You can chose other values you like

 

 const int MultiplyEachUnitOnX = 50;

 const int MultiplyEachUnitOnY = 250;

 for(double i = -280; i < 280; i += 0.01)

 {

  double j = sin(PI / MultiplyEachUnitOnX * i) * MultiplyEachUnitOnY; 

  dc.SetPixel(i, j, RGB(255, 0, 0));

 }

 

 // Do not call CView::OnPaint() for painting messages

 

}


图二十五、代码效果图

  (一)逻辑坐标。逻辑坐标与设备无关,缺省地,一个逻辑单位等于设备中的一个象素。它是实现“所见即所得”的基础。例如,当程序员调用LineTo函数绘制25.4mm(1 英 寸) 长的直线时,他只要使用合适的映射模式,那么就并不需要考虑输出的是何种设备。若设备是VGA显示器,Windows自动将其转化为96个像素点;若设备是一个300dpi的激光打印机,Windows自动将其转化为300 个像素点。 

 

  (二)设备坐标。图形输出时,Windows将GDI函数中指定的逻辑坐标映射为设备坐标,在所有的设备坐标系统中,单位以像素点为准,水平值从左到右增大(正方向向右),垂直值从上到下增大(正方向向下)。Windows中包括以下3 种设备坐标,以满足各种不同需要: 

 

  1、客户区域坐标,包括应用程序的客户区域,客户区域的左上角为(0, 0)。 

 

  2、屏幕坐标,包括整个屏幕,屏幕的左上角为(0, 0)。屏幕坐标用在WM_MOVE消息中(对于非子窗口)以及下面的Windows 函数中:CreateWindow 和MoveWindow(都对于非子窗口)、GetMessage、GetCursorPos、GetWindowRect、WindowFromPoint 和SetBrushOrg 中。 用函数ClientToScreen 和ScreenToClient可以将客户区域坐标转换成屏幕区域坐标,或反之。 
 
  3、全窗口坐标,包括一个程序的整个窗口,包括标题条、菜单、滚动条和窗口框,窗口的左上角为(0,0)。使用GetWindowDC得到的窗口设备环境,可以将逻辑单位转换成窗口”坐标。  

  (三)映射。映射方式定义了Windows如何将GDI函数中指定的逻辑坐标映射为设备坐标。在下文中我们将介绍常用的映射方式。

 

  此外,习惯上,我们将逻辑坐标所在的坐标系称为“窗口”;将设备坐标所在的坐标系称为“视口”。“窗口”依赖于逻辑坐标,可以是像素点、毫米或其他尺度。这一点请牢记,这对于下面的有关内容的理解至关重要。

 

  二、默认的坐标系统
 

  当在微软的窗口中进行绘图时,绘图的坐标原点在屏幕的左上角,任何物体在屏幕上定位都要参考这个坐标原点。在笛卡尔坐标系统中这个点被定义为坐标原点(0,0),水平坐标轴的正方向是从该点出发向右延伸,垂直坐标轴的正方向是从该点出发向下延伸。


图一、笛卡尔坐标系

  这个坐标原点只是操作系统默认的坐标原点,所以如果你调用Ellipse(-100, -100, 100, 100)函数来绘制图形的话,你将得到一个圆,它的圆心位于屏幕的左上角,仅仅只有圆的四分之一部分(270度到360度的部分)显示在屏幕上。代码及效果图如下 


void CExoDraw1View::OnPaint() 

{

 CPaintDC dc(this); // 绘图的设备厂上下文

 CPen PenBlue;

 // 兰色画笔

 PenBlue.CreatePen(PS_SOLID, 1, RGB(0, 12, 255));

 dc.SelectObject(&pPen);

 dc.Ellipse(-100, -100, 100, 100);

}


  图二、代码效果图 

  按照同样的原理,你可以使用CpaintDC的方法或按照你的要求创建函数来绘制任何几何或非几何图形。例如,下面的代码绘制了两条相互垂直的直线,垂点位与窗口的中心:
 


void CExoDraw1View::OnPaint()  

{

 CPaintDC dc(this); // 绘图的设备上下文

 CRect Recto;

 CPen PenBlue;

 PenBlue.CreatePen(PS_SOLID, 1, RGB(0, 12, 255));

 dc.SelectObject(&PenBlue);

 dc.Ellipse(-100, -100, 100, 100);

 CPen PenBlack;

 PenBlack.CreatePen(PS_SOLID, 1, BLACK_PEN);

 dc.SelectObject(&PenBlack);

 // 得到客户区域的尺寸;

 GetClientRect(&Recto);

 dc.MoveTo(Recto.Width() / 2, 0);

 dc.LineTo(Recto.Width() / 2, Recto.Height());

 dc.MoveTo(0, Recto.Height() / 2);

 dc.LineTo(Recto.Width(), Recto.Height() / 2);

}

 
时间: 2024-10-08 03:40:48

VC++中GDI和GDI+ 的坐标系统介绍的相关文章

Win32中GDI+应用(五)--GDI与GDI+编程模型的区别

在GDI里面,你要想开始自己的绘图工作,必须先获取一个device context handle,然后把这个handle作为绘图复方法的一个参数,才能完成任务.同时,device context handle是同一定的绘图属性绑定在一起的,诸如画笔.话刷等等,你必须在画线之前创建自己的画笔,然后使用selectObject方法把这个画笔同已经获取的device context handle绑定,才能使用LineTo等方法开始画线.不然,你画出来的线使用的是默认的属性:宽度(1),颜色(黑色).但

【vs2013】如何在VS的MFC中配置使用GDI+?

摘自:http://bbs.csdn.net/topics/330171017 http://www.cnblogs.com/it20120227/archive/2011/12/31/2370903.html 首先,VS2010中已经有GDI+SDK包的,不需要额外下载1:在stdafx.h文件中加入下面3行代码,添加相应的头文件和库  #pragma comment( lib, "gdiplus.lib" )   #include "gdiplus.h"   u

深度解析VC中的消息传递机制

摘要:Windows编程和Dos编程,一个很大的区别就是,Windows编程是事件驱动,消息传递的.所以,要学好Windows编程,必须 对消息机制有一个清楚的认识,本文希望能够对消息的传递做一个全面的分析. 一.什么是消息? 消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉.一个消息,是系统定义的一个32位的值,他唯一的定 义了一个事件,向Windows发出一个通知,告诉应用程序某个事情发生了.例如,单击鼠标.改变窗口尺寸.按下键盘上的一个键 都会使Windows发送一个

深度解析VC中的消息(转发)

http://blog.csdn.net/chenlycly/article/details/7586067 这篇转发的文章总结的比较好,但是没有告诉我为什么ON_MESSAGE的返回值必须是LRESULT 摘要: Windows编程和Dos编程,一个很大的区别就是,windows编程是事件驱动,消息传递的.所以,要做好windows编程,必须对消息机制有一个清楚的认识,本文希望能够对消息的传递做一个全面的论述,由于小生初学VC,里面可能有一些错误的地方,还往各位大虾批评.指正. 注意:有些消息

VC中常见的108个问题

(1) 如何通过代码获得应用程序主窗口的 指针? 主窗口的 指针保存在CWinThread::m_pMainWnd中,调用AfxGetMainWnd实现. AfxGetMainWnd() ->ShowWindow(SW_SHOWMAXMIZED) //使程序最大化. (2) 确定应用程序的路径 Use GetModuleFileName 获得应用程序的路径,然后去掉可执行文件名. Example: TCHAR exeFullPath[MAX_PATH] // MAX_PATH在API中定义了吧,

在VC中显示和处理图片的方法

落鹤生 发布于 2011-10-21 09:12 点击:344次  来自:blog.csdn.net/mengaim_cn 几种用GDI画图的方法介绍. TAG: GDI   法1:这个方法其实用的是一本经典vc图像处理的书上的有关读取位图的函数库, 当没有这个函数库时,就没有太多的实用价值.这种方法直接用的是读取和显示bmp图片的函数库首先要得到要显示区域的位置:  CWnd* pWnd=GetDlgItem(IDC_BMP);  RECT rect;  pWnd->GetClientRect

GDI+与GDI的区别简介

一.GDI GDI是位于应用程序与不同硬件之间的中间层,这种结构让程序员从直接处理不同硬件的工作中解放出来,把硬件间的差异交给了GDI处理.GDI通过将应用程序与不同输出设备特性相隔离,使Windows应用程序能够毫无障碍地在Windows支持的任何图形输出设备上运行.例如,我们可以在不改变程序的前提下,让能在Epson点式打印机上工作的程序也能在激光打印机上工作.它把windows系统中的图形输出转换成硬件命令然后发送给硬件设备.GDI是以文件的形式存储在系统中,系统需要输出图形时把它载入内存

GDI+ 和GDI

GDI:Graphics Device Interface,即图形设备接口,是Windows API的一个重要组成部分.它是Windows图形显示程序与实际物理设备之间的桥梁,GDI使得用户无需关心具体设备的细节,而只需在一个虚拟的环境(即逻辑设备)中进行操作.它的桥梁作用体现在: (1)用户通过调用GDI函数将逻辑空间的操作转化为具体针对设备驱动程序的调用. 为实现图形设备无关性,Windows的绘图操作在一个设备描述表上进行.用户拥有自己的"逻辑坐标"系统,它独立于实际的物理设备,

GDI+ 填充背景时,非常多时候不起作用,GDI、GDI+配合运用

在ONDRAW中运行GDI+ 填充背景时,不起作用,不知道什么原因 [cpp] view plaincopy Graphics graphics(pDC->GetSafeHdc()); Bitmap bmp(m_imgRec.Width(),m_imgRec.Height()); //第一步 创建与屏幕等大小的内存位图 Graphics grbmp(&bmp); SolidBrush backBrush(RGB(255,255,255)); grbmp.FillRectangle(&

浅谈VC++中预编译的头文件放那里的问题分析

用C++写程序,肯定要用预编译头文件,就是那个stdafx.h.不过我一直以为只要在.cpp文件中包含stdafx.h 就使用了预编译头文件,其实不对.在VC++中,预编译头文件是指放到stdafx.h中的头文件才会有效果.如下: file: stdafx.h // stdafx.h : include file for standard system include files, // or project specific include files that are used freque