先理解一下“窗口”与“视区”的概念。“窗口”是逻辑坐标下的矩形区域,“视区”是设备坐标系下的区域。根据“窗口”和“视区”的大小可以确定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