先理解一下“窗口”与“视区”的概念。“窗口”是逻辑坐标下的矩形区域,“视区”是设备坐标系下的区域。根据“窗口”和“视区”的大小可以确定x方向和y方向的比例因子。

    例子如下:

VOID OnPaint(HWND hwnd,WPARAM wParam,LPARAM lParam)
{
    PAINTSTRUCT     ps; 
    RECT rtClient;
    GetClientRect(hwnd,&rtClient);
    HDC hdc = BeginPaint(hwnd, &ps);  
    HDC hMemDC = CreateCompatibleDC(hdc);
    HBITMAP hBitmap = CreateCompatibleBitmap(hdc, rtClient.right - rtClient.left, rtClient.bottom - rtClient.top);//rt为RECT变量;
    SelectObject(hMemDC, hBitmap);
    FillRect(hMemDC, &rtClient,WHITE_BRUSH);

    int OldMapMode =SetMapMode(hMemDC,MM_ANISOTROPIC);
    //SetViewportOrgEx(hMemDC,0,rtClient.bottom,NULL);
    POINT point = {100,100};
    DPtoLP(hMemDC,&point,1);
    SetWindowOrgEx(hMemDC,-point.x,-point.y,NULL);
    RECT rt = {-100,-100,0,0};
    HBRUSH hBrush = CreateSolidBrush(RGB(0,0,255));
    FillRect(hMemDC,&rt,hBrush);

    //SetViewportOrgEx(hMemDC,0,0,NULL);
    SetWindowOrgEx(hMemDC,0,0,NULL);
    SetMapMode(hMemDC,OldMapMode);
    BitBlt(hdc, 0, 0,rtClient.right - rtClient.left, rtClient.bottom - rtClient.top, 
       hMemDC, 0, 0, SRCCOPY);
    DeleteDC(hMemDC);
    DeleteObject(hBitmap);
    EndPaint(hwnd,&ps);
}

注:最后最好把设置都改回来(SetViewportOrgEx(hMemDC,0,0,NULL)或者SetWindowOrgEx(hMemDC,0,0,NULL))。

如果设置SetViewportOrgEx则比较简单,直接把逻辑坐标平移就好。

如何设置SetWindowOrgEx则比较麻烦,比如我想平移到100,100这个点,则先要调用DPtoLP进行转换,然后参数是转换后值的取反。(如果不调用DPtoLP函数,则逻辑坐标与设备坐标方向一样,如果不一样,则不需要取反)

   比如居中的两种方法,直接写上书中的例子吧(理解就好,MFC版):

      设置x轴正方向向右,y轴正方向向上,客户区中心为坐标系为原点。

  (1)、设置视口

       pDC->SetWindowExt(rc.Width(),rc.Height());

  pDC->SetViewportExt(rc.Width(),-rc.Height());

      pDC->SetViewportOrg(rc.Width()/2,rc.Heigth()/2);

     (2)、设置窗口

  pDC->SetWindowExt(rc.Width(),-rc.Height());

  pDC->SetViewportExt(rc.Width(),rc.Height());

      pDC->SetWindowOrg(-rc.Width()/2,rc.Heigth()/2);

     分析第二种:由于设备与逻辑坐标比率是1比1,所以不需要转换坐标,准备偏移点为(rc.Width()/2,rc.Heigth()/2),由于逻辑与设备坐标系x方向相同,y方向不同,所以x取反,y不需要取反,结果为(-rc.Width()/2,rc.Heigth()/2);

  第二种亦可以换成类似:(win32资料,这是我自己的测试代码)

    SetWindowExtEx(hMemDC,rtClient.right,-2*rtClient.bottom,NULL);
    SetViewportExtEx(hMemDC,rtClient.right,rtClient.bottom,NULL);
    POINT point = {rtClient.right/2,rtClient.bottom/2};
    DPtoLP(hMemDC,&point,1);
    SetWindowOrgEx(hMemDC,-point.x,-point.y,NULL);

     

 

通过设置原点变成极坐标,然后可以方便计算。比如计算机图形学基础教程有一道题。

把一个半径为R的圆40等份,以每个等分点为圆心,以r为半径画圆。

  

  RECT rtClient;
    GetClientRect(hwnd,&rtClient);
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd,&ps);
    SetMapMode(hdc,MM_ANISOTROPIC);
    SetViewportExtEx(hdc,rtClient.right,rtClient.bottom,NULL);
    SetWindowExtEx(hdc,rtClient.right,-rtClient.bottom,NULL);
    POINT pt = {rtClient.right/2,rtClient.bottom/2};
    DPtoLP(hdc,&pt,1);
    SetWindowOrgEx(hdc,-pt.x,-pt.y,NULL);
    HBRUSH hBrush = (HBRUSH)GetStockObject(NULL_BRUSH);
    HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc,hBrush);
    int bigR = 100;
    int smallR = 150;
    for(int i=0;i<40;++i)
    {
        int x = (int)(cos(0.0+9*i)*bigR);
        int y = (int)(sin(0.0+9*i)*bigR);
        Ellipse(hdc,x-smallR,y-smallR,x+smallR,y+smallR);
    }
    SetWindowOrgEx(hdc,0,0,NULL);
    SelectObject(hdc,hOldBrush);
    
    EndPaint(hwnd,&ps);

效果图:

  

注:参考资料:http://www.cppblog.com/dragon/archive/2012/09/07/64005.html