MFC中的GDI绘图
MFC中的GDI繪圖
?
?
目錄(?)[+]
一.關于GDI的基本概念
什么是GDI?
Windows繪圖的實質就是利用Windows提供的圖形設備接口GDI(Graphics Device Interface)將圖形繪制在顯示器上。
?
在Windows操作系統中,動態鏈接庫C:\WINDOWS\system32\gdi32.dll(GDI Client DLL)中定義了GDI函數,實現與設備無關的包括屏幕上輸出像素、在打印機上輸出硬拷貝以及繪制Windows用戶界面功能。在Visual C++6.0中的頭文件C:\Program Files\Microsoft Visual Studio\VC98\Include\wingdi.h和Visual Studio 2005中的頭文件C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\Include\WinGDI.h是訪問gdi32.dll庫文件的鑰匙。下面我們大致瀏覽一下wingdi.h(included in Windows.h)頭文件:
?
/* Bitmap Header Definition */定義了BITMAP位圖結構
?
/* Mapping Modes */定義了DC中的坐標映射方式,包括以下常用函數:
SetMapMode、SetViewportExtEx、SetViewportOrgEx、?SetWindowExtEx?、SetWindowOrgEx。
?
/* Stock Logical Objects */系統預定義的堆(STOCK)對象,包括BRUSH、PEN和FONT對象
?
/* Brush Styles */定義了畫刷格式,包括SOLID、HOLLOW、HATCHED等格式
?
/* Hatch Styles */定義了畫刷陰影格式,包括:
HS_VERTICAL??? /* ||||| */
HS_FDIAGONAL?/* \\\\\ */
HS_BDIAGONAL?/* / */
HS_CROSS?????? /* +++++ */
HS_DIAGCROSS?/* xxxxx */
?
/* Pen Styles */定義了畫筆格式,包括SOLID、DASH、DOT等格式
什么是DC?
設備環境DC(Device Context),也稱為設備描述表或設備上下文。
?
設備環境保存了繪圖操作中一些共同需要設置的信息,如當前的畫筆、畫刷、字體和位圖等圖形對象及屬性,以及坐標映射、顏色和背景等影響圖形輸出的繪圖模式。形象的說,一個設備環境提供了一張畫布和一些繪畫的工具,我們可以使用不同格式、顏色的繪畫工具在上面涂鴉。這里,設備環境中的“設備”是指任何類型的顯示器或打印機等輸出設備,繪圖時,我們不必關心所使用設備的編程的原理和方法,所有的繪制操作必須通過設備環境進行間接的處理,Windows會自動將設備環境所描述的結構映射到相應的物理設備上。
?
從根本上來說,DC它是Windows內部使用的數據結構,它存儲著向設備輸出時說需要的信息,應用程序利用它定義圖形對象及其屬性,并實現應用程序、設備驅動程序和輸出設備之間繪圖命令的轉換。要想調用GDI函數向某個區域輸出文字或繪制圖形,必須先取得或建立設備環境句柄,應用程序每一次繪圖操作均按照設備環境中的設置的繪圖屬性進行。
?
設備環境不像其他Windows結構,在程序中不能直接存取設備環境結構,只能通過系統提供的一系列函數或使用設備環境的句柄HDC來間接地獲取或設置設備環境結構中的各項屬性,這些屬性包括顯示器高度和寬度、支持的顏色數和分辨率等。
MFC中與GDI有關的類
為了支持GDI繪圖,MFC提供了兩種重要的類:設備環境DC(Device Context)類,用于設置繪圖屬性和繪制圖形;繪圖對象類,封裝了各種GDI繪圖對象,包括畫筆、刷子、字體、位圖、調色板和區域。
?
在MFC中,CDC是設備環境類的基類,除了一般的窗口顯示外,還用于基于桌面的全屏幕繪制和非屏幕顯示的打印機輸出。CDC類封裝了所有圖形輸出函數,包括矢量、光柵和文本輸出。CDC的派生類包括CClientDC、CPaintDC、WindowDC、CMetaFileDC。
??
??
(1)CPaintDC類是一個來自CDC的設備環境類。它在構造期間執行CWnd::BeginPaint,在析構期間執行CWnd::EndPaint,EndPaint()除了釋放設備環境外,還負責從消息隊列中清除WM_PAINT消息。一個CPaintDC對象只在響應一個窗口重繪消息(WM_PAINT)的時候被使用,通常是在你的OnPaint消息處理成員函數中。因此,在處理窗口重畫時,必須使用CPaintDC,否則WM_PAINT消息無法從消息隊列中清除,將引起不斷的窗口重畫。
?
CPaintDC類成員:
?
數據成員
m_ps:包含了用于畫客戶區的PAINTSTRUCT?
m_hWnd: CPaintDC對象所附著的HWND?
?
構造函數CPaintDC:構造一個連接到指定的CWnd上的CPaintDC對象
?
(2)CClientDC(窗口客戶區設備環境)類用于管理窗口用戶區對應的顯示上下文,它在構造時調用了Windows函數GetDC,在析構時調用了ReleaseDC。這意味著和CClientDC對象相關的設備上下文是窗口的客戶區。一般在響應非窗口重畫消息(如鍵盤輸入時繪制文本、鼠標繪圖)繪圖時要用到它。
?
CClientDC類的成員:
?
構造函數CClientDC:構造一個連接到CWnd上的CClientDC對象數據成員
數據成員m_hWnd:所在的有效窗口的HWND
?
(3)CWindowDC(窗口設備環境)類用于管理與整個窗口對應的顯示上下文,包括它的結構和控件。它在構造的時候調用Windows函數GetWindowDC,在銷毀的時候調用ReleaseDC。這意味著CWindowDC對象可以訪問CWnd的全部屏幕區域(包括客戶區和非客戶區)。它用于窗口(包括窗口邊框、標題欄、控制按鈕等)的繪制,除非要自己繪制窗口邊框和按鈕(如一些CD播放程序等),否則一般不用它。
?
?CWindowDC類成員:
?構造函數CWindowDC:構造一個CWindowDC對象
?數據成員?m_hWnd:與這個CWindowDC相關聯的HWND句柄
?
(4)CMetaFileDC專門用于圖元文件的繪制。圖元文件記錄一組GDI命令,可以通過這一組GDI命令重建圖形輸出。使用CMetaFileDC時,所有的圖形輸出命令會自動記錄到一個與CMetaFileDC相關的圖元文件中。
?
(5)此外我們還可以利用Windows內存DC進行繪圖,此時涉及到屏幕DC和內存DC。把所要繪制的一切先在內存DC中進行繪制,之后全部搬到屏幕DC中,從而把所有繁瑣的繪制過程都在內存DC中完成了,我們在屏幕上看到的是一幅完整的圖畫,所以不可能出現閃爍的情況。
?二.MFC中GDI繪圖
??? GDI繪圖包括以下步驟:獲取設備環境,設置坐標映射,創建繪圖工具,調用DC繪圖函數繪圖。
1、獲取設備環境
(1)在SDK編程中,獲取設備環境的方法有兩種:
?
<1>通過API函數BeginPaint。應用程序響應WM_PAINT消息進行圖形刷新時主要通過BeginPaint函數獲取設備環境,在消息處理函數返回前調用API函數EndPaint釋放設備環境。
?
函數原型為:
WINUSERAPI HDC WINAPI BeginPaint( HWND hWnd,LPPAINTSTRUCT lpPaint);
?
??? //以下為Win API示例::BeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint);
?
???? case WM_PAINT://窗口客戶區需要重繪
???? {
???????? char szText[]="Hello World";
???????? PAINTSTRUCT ps;
???????? HDC hdc=::BeginPaint(hWnd,&ps);
???????? ::TextOut(hdc,10,10,szText,strlen(szText));
???????? ::EndPaint(hWnd,&ps);
???????? return 0;
}
?
MFC對BeginPaint進行了封裝:
CDC* CWnd::BeginPaint(LPPAINTSTRUCT lpPaint);
等價于::BeginPaint(CWnd::m_hWnd, LPPAINTSTRUCT lpPaint);
?
<2>通過API函數GetDC。在非WM_PAINT消息處理函數中,需要調用GetDC來獲取設備環境,調用API函數ReleaseDC來釋放設備環境。
?
函數原型為:WINUSERAPI HDC WINAPI GetDC( HWND hWnd);
?
(2)在MFC中,MFC提供了不同類型的DC類,每一個類都封裝了DC句柄,并且它們的構造函數自動調用獲取DC的API函數,析構函數自動調用釋放DC的API函數。因此,在程序中通過聲明一個MFC設備環境類的對象就自動獲取了一個DC,而當該對象被銷毀時就自動釋放了獲取的DC。MFC AppWizard應用程序向導創建的OnDraw()函數自動支持所獲取的DC。?
?
<1>?CPaintDC構造函數:CPaintDC(CWnd* pWnd);?構造一個CPaintDC對象(pWnd指向一個CPaintDC對象所屬的CWnd對象),準備用于繪畫的應用程序窗口。
?
// BeginPaint
void CView::OnPaint()
{
???????? CPaintDC dc(this); // device context for painting
???????? // TODO: Add your message handler code here
???????? OnPrepareDC(&dc);
???????? OnDraw(&dc)
}
當我們改變了窗口尺寸、移動窗口或恢復了先前被覆蓋的部分,應用程序窗口就會收到一個Windows系統發送來的WM_PAINT消息,然后調用基類Cview的OnPaint函數或我們自己添加的消息處理函數OnPaint。我們可以在OnPaint函數中重繪窗口中重新可見的部分,但簡單的處理辦法是重繪整個窗口。上面的代碼中,由于基類Cview的OnPaint函數調用了OnDraw虛函數,因此應用程序經常在OnDraw函數中繪制視圖。
?
<2>CClientDC構造函數:CClientDC(CWnd* pWnd);?構造一個CClientDC對象,它將存取pWnd指向的CWnd的客戶區。
?
//?鼠標左鍵事件處理
void CExView::OnLButtonDown(UINT nFlags, CPoint point)
{
????????? // TODO:?在此添加消息處理程序代碼和/或調用默認值
????????? CClientDC dc(this);//定義客戶區設備環境
????????? dc.LineTo(point);//繪制線段
}
CClientDC代表了窗口客戶區對應的顯示上下文,它在構造時調用了API函數GetDC,并將當前窗口的句柄m_hWnd作為函數參數;在析構時調用了API函數ReleaseDC。當在客戶去繪圖時,需要利用CClientDC類定義一個客戶區設備環境句柄。
?
有時候需要訪問與一個客戶設備環境相關聯的窗口對象,可以通過Attach函數把這個CClientDC的成員m_hWnd句柄傳遞給一個窗口對象,該窗口就是與客戶區設備環境相關聯的窗口。
?
CWnd::Attach,BOOL Attach( HWND hWndNew );
說明:將一個Windows窗口與CWnd對象相連接。
返回值:如果成功,則返回非零值;否則返回0。
參數:hWndNew指定了Windows窗口的句柄
?
<3>CWindowDC構造函數:CWindowDC( CWnd* pWnd );構造一個CWindowDC對象,它可以訪問pWnd指向的CWnd對象的整個屏幕區域(包括客戶區和非客戶區)。比如我們在做屏幕保護程序時,一般以整個屏幕區域作為繪制區域。
2、設置坐標映射
(1)Windows坐標系統
?
Windows坐標系分為邏輯坐標系和設備坐標系兩種,GDI支持這兩種坐標系。一般而言,GDI的文本和圖形輸出函數使用邏輯坐標,而在客戶區移動或按下鼠標的鼠標位置是采用設備坐標。
?
<1>邏輯坐標系是面向DC的坐標系,這種坐標不考慮具體的設備類型,在繪圖時,Windows會根據當前設置的映射模式將邏輯坐標轉換為設備坐標。
?
<2>設備坐標系是面向物理設備的坐標系,這種坐標以像素或設備所能表示的最小長度單位為單位,X軸方向向右,Y軸方向向下。設備坐標系的原點位置(0, 0)不限定在設備顯示區域的左上角。
?
設備坐標系分為屏幕坐標系、窗口坐標系和客戶區坐標系三種相互獨立的坐標系。
?
屏幕坐標系以屏幕左上角為原點,一些與整個屏幕有關的函數均采用屏幕坐標,如GetCursorPos()、SetCursorPos()、CreateWindow()、MoveWindow()。彈出式菜單使用的也是屏幕坐標。
?
窗口坐標系以窗口左上角為坐標原點,它包括窗口標題欄、菜單欄和工具欄等范圍。
?
客戶區坐標系以窗口客戶區左上角為原點,主要用于客戶區的繪圖輸出和窗口消息的處理。鼠標消息的坐標參數使用客戶區坐標,CDC類繪圖成員函數使用與客戶區坐標對應的邏輯坐標。
?
(2)坐標之間的相互轉換
?
編程時,有時需要根據當前的具體情況進行三種設備坐標之間或與邏輯坐標的相互轉換。
?
MFC提供了兩個函數CDC::DPtoLP()和CDC::LPtoDP()用于設備坐標與邏輯坐標之間的相互轉換。
?
MFC提供了兩個函數CWnd::ScreenToClient()和CWnd::ClientToScreen()用于屏幕坐標與客戶區坐標的相互轉換。
?
(3)映射模式
?
映射模式確定了在繪制圖形時所依據的坐標系,它定義了邏輯單位的實際大小、坐標增長方向,所有映射模式的坐標原點均在設備輸出區域(如客戶區或打印區)的左上角。此外,對于某些映射模式,用戶還可以自定義窗口的長度和寬度,設置視圖區的物理范圍。
?
Windows定義了8種映射模式,見下表。
?
?
?
映射模式使得程序員可不必考慮輸出設備的具體設備坐標系,而在一個統一的邏輯坐標系中進行圖形的繪制。
當繪制的圖形需要隨著窗口的大小改變而自動改變的時候,一般選擇MM_ISOTROPIC和MM_ANISOTROPIC映射方式。它們的唯一區別就是前者的X軸和Y軸的邏輯單位的大小是相同的,單詞“isotropic”就是各個方向相等的意思,此映射方式適合繪制圓或正方形。而實際應用中,常常給X軸和Y軸取不同的比例,這時候選擇MM_ANISOTROPIC映射方式。單詞“anisotropic”就是各個方向相異的意思。
?
(4)自定義映射模式
?
“窗口”和“視口”的概念:
?
窗口(Window):對應邏輯坐標系上程序員設定的區域
?
視口(Viewport):對應實際輸出設備上程序員設定的區域
?
窗口原點是指邏輯窗口坐標系的原點在視口(設備)坐標系中的位置,視口原點是指設備實際輸出區域的原點。
?
除了映射模式,窗口和視口也是決定一個點的邏輯坐標如何轉換為設備坐標的一個因素。一個點的邏輯坐標按照如下式子轉換為設備坐標:
?
設備(視口)坐標?=?邏輯坐標?–窗口原點坐標?+?視口原點坐標
?
//定義坐標映射方式
WINGDIAPI int?? WINAPI SetMapMode(HDC, int);
此API函數在MFC中封裝為CDC::virtual int SetMapMode(int nMapMode);
?
//定義邏輯窗口區域,單位為邏輯單位(Logical)
WINGDIAPI BOOL?WINAPI SetWindowExtEx (HDC, int, int, LPSIZE);
此API函數在MFC中封裝為CDC::virtual CSize SetWindowExt(int cx, int cy);
?
//設置邏輯窗口的原點坐標,缺省原點為(0,0)。
WINGDIAPI BOOL?WINAPI SetWindowOrgEx(HDC, int, int, LPPOINT);
此API函數在MFC中封裝為CDC::CPoint SetWindowOrg(int x, int y);
?
注意:SetWindowOrg(Ex)?只有在映射模式為MM_ANISOTROPIC或MM_ISOTROPIC時才有意義。
?
//定義視口的坐標軸方向及區域、定義域和值域,單位為像素(Pixel)
WINGDIAPI BOOL?WINAPI SetViewportExtEx(HDC, int, int, LPSIZE);
此API函數在MFC中封裝為CDC::virtual CSize SetViewportExt(int cx, int cy);
?
注意:SetViewportExt(Ex)?只有在映射模式為MM_ANISOTROPIC或MM_ISOTROPIC時才有意義。
?
//設置視口的原點坐標,缺省原點為(0,0)。
WINGDIAPI BOOL?WINAPI SetViewportOrgEx(HDC, int, int, LPPOINT);
此API函數在MFC中封裝為CDC:: virtual CPoint SetViewportOrg(int x, int y);
?
參考:《GDI中的坐標映射問題》http://dev.csdn.net/article/12/12013.shtm
3、創建繪圖工具并選入DC
有了畫布,要繪圖我們必須有畫筆畫刷。在Windows中有HPEN、HBRUSH等GDI對象,MFC對GDI對象進行了很好的封裝,提供了封裝GDI對象的類,如CPen、CBrush、CFont、CBitmap和CPalette等,這些類都是GDI對象類CGdiObject的派生類。
?
????一般先創建畫筆(刷),然后調用CDC::SelectObject函數將畫筆(刷)選入設備環境最為當前繪圖工具,繪圖完畢恢復設備環境以前的畫筆(刷)對象,最后調用CGdiObject::DeleteObject函數刪除畫筆(刷)對象。
?
????這里需要注意的是,CGdiObject::DeleteObject函數徹底刪除底層GDI對象(CPen和CBrush類的基類)。在MFC中,當對象銷毀時會調用對象的析構函數自動刪除對象,一般不必調用CGdiObject::DeleteObject刪除GDI對象,因為如果設備環境還在使用一個GDI對象時,將引起應用程序崩潰或出現難以理解的運行錯誤。
?
???(1)創建畫筆
BOOL CPen::CreatePen( int nPenStyle, int nWidth, COLORREF cfColor );
?
nPenStyle?指定畫筆的風格。其可能取值的列表,請參見CPen構造函數中的nPenStyle參數。
?
nWidth?指定畫筆的寬度。如果這個值為0,則不管是什么映射模式,以設備單位表示的寬度總是一個像素。
?
crColor?包含畫筆的一個RGB顏色,為COLORREF結構。
?
此外,可通過CDC::SelectStockObject函數來調用系統預定義的庫存筆對應的CGdiObject對象。
?
pOldPen = (Cpen*)pDC->SelectStockObject(BLACK_PEN);
?
(2)創建畫刷
?
BOOL CBrush::CreateSolidBrush ( COLORREF crColor );
?
BOOL CBrush::CreateHatchBrush( int nIndex, COLORREF crColor );
?
參數:?nIndex?指定畫刷的陰影線風格。可取的值如下:
?
HS_HORIZONTAL?? /* ==== */
?
HS_VERTICAL??? /* ||||| */
?
HS_FDIAGONAL?/* \\\\\ */
?
HS_BDIAGONAL?/* / */
?
HS_CROSS?????? /* +++++ */
?
HS_DIAGCROSS?/* xxxxx */
?
返回值:調用成功時返回非零值,否則為0。
?
此外,可通過CDC::SelectStockObject函數來調用系統預定義的庫存畫刷對應的CGdiObject對象。
?
pOldBrush = (CBrush*)pDC->SelectStockObject(BLACK_BRUSH);
?
(3)將畫筆(刷)選入設備環境。
?
以下為MFC中默認映射方式下的GDI繪圖的模塊:
?
//先獲取設備環境pDC
?
??? CPen *pOldPen,newPen;
?
??? CBrush *pOldBrush,newBrush1,newBrush2;
?
??? //創建寬度為pixel的白色實線畫筆
?
??? newPen.CreatePen(PS_SOLID,1,RGB(0,0,0));
?
??? //創建紅色實線畫刷
?
??? newBrush1.CreateSolidBrush(RGB(255,0,0));
?
??? //創建紅色實線度的向下(從右到左)影線的陰影畫刷
?
??? newBrush2.CreateHatchBrush(HS_BDIAGONAL,RGB(255,0,0));
?
??? //將newPen畫筆和newBrush1畫刷對象選入設備環境
?
??? pOldPen = pDC->SelectObject(&newPen);
?
??? pOldBrush = pDC->SelectObject(&newBrush1);
?
??? //調用DC繪圖函數繪圖
?
??? //……
?
??? //繪圖完畢,恢復原來畫筆、畫刷
?
??? pDC->SelectObject(pOldPen);
?
pDC->SelectObject(pOldBrush);
?
//刪除創建的畫筆、畫刷
?
// newPen.DeleteObject();
?
// newBrush1.DeleteObject();
?
// newBrush2.DeleteObject();
?
???(4)當繪制文本Text時,一般可以通過調用CDC::SetBkColor函數來設置背景顏色,調用CDC::SetTextColor函數來設置文字顏色,調用CDC::SetTextAlign函數設置文本對齊標記。
4、調用DC繪圖函數繪圖
GDI為提供了繪制基本圖形的成員函數,在MFC中這些函數封裝在CDC類中。
?
注意:繪圖函數使用的坐標都是邏輯坐標。
?
常用CDC繪圖函數
三.坐標映射實例
(1)建立單文檔MFC項目Draw:New—>Projects—>MFC AppWizard(EXE)—>Single Document。
?
(2)找到CMainFrame::PreCreateWindow函數,在其中設置默認窗口大小為400 pixel*300 pixel。
?
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
?
{
??? if( !CFrameWnd::PreCreateWindow(cs) )
??????? return FALSE;
?
??? // TODO: Modify the Window class or styles here by modifying
??? // the CREATESTRUCT cs
??? cs.cx=400;
??? cs.cy=300;
?
??? return TRUE;
}
(3)添加OnPaint事件
?
資源管理器—>ClassView—>右擊CDrawView?選擇Add Windows Message Handler
?
—>WM_PAINT—> Add Handler
?
???? void CDrawView::OnPaint()
???? {
????????? CPaintDC dc(this); // device context for painting??
????????? // TODO: Add your message handler code here
????????? CRect cr;//矩形結構
????????? GetClientRect(&cr);//獲得客戶區窗口?
????????? int cx=cr.right;//右
??????????? int cy=cr.bottom;//底
????????? dc.SetMapMode(MM_ISOTROPIC);//X=Y??
?????? dc.SetWindowExt(1000,1000);//設置邏輯窗口,默認窗口原點為(0,0)
?????? dc.SetViewportExt(cx,-cy);//定義輸出視口,X右Y上為正
?
?????? dc.SetViewportOrg(cx/2,cy/2);//定義視口原點為客戶區中心
?????? dc.Ellipse(-200,200,200,-200);//繪制橢圓與客戶區外切的橢圓
?????? //繪制水平垂直的四條半徑
?????? dc.MoveTo(0,0);???? dc.LineTo(200,0);
?????? dc.MoveTo(0,0);???? dc.LineTo(-200,0);
?????? dc.MoveTo(0,0);???? dc.LineTo(0,200);
?????? dc.MoveTo(0,0);???? dc.LineTo(0,-200);
?????? //執行F5進行Debug,在底端Output窗口中可以觀察ClientRect
????? TRACE( "ClientRect.x = %d, ClientRect.y = %d\n", cx, cy );
?}
?
運行結果如圖1左。當改變窗口大小時,圖中圓形狀始終不變。
?
<1>將上面代碼的第9行改為:dc.SetMapMode(MM_ANISOTROPIC);//X!=Y運行結果如圖1右。
?
???????????????????????圖1
我們發現,盡管上面代碼的第13行dc.Ellipse(-200,200,200,-200);中定義的橢圓外接矩形邏輯上為正方形,但是顯示的并不是圓,而是橢圓。
當我們改變窗口大小時,圖中橢圓變形,甚至可能變為圓形。具體為:
保持窗口寬度不變時,減小高度,橢圓變得更扁;保持窗口高度不變時,減小寬度,橢圓變得更圓,當拉伸到客戶區為正方形時,我們發現橢圓變成了圓!
<2>將上面代碼的第9行改回dc.SetMapMode(MM_ISOTROPIC);//X=Y,第15行改為dc.LineTo(500,0);?第18行改為dc.LineTo(0,-500);?運行結果如圖2左。
?
保持窗口高度不變,減小窗口寬度,使窗口寬度<窗口高度,運行結果如圖2右。
??????????圖2
<3>在將<2>中代碼的第9行改回dc.SetMapMode(MM_ANISOTROPIC);//X!=Y,
?
運行結果如圖3:
????????????????????????????圖3
當我們改變窗口大小時,dc.LineTo(500,0); dc.LineTo(0,-500);都是由原點(客戶區中心)到客戶區右端中心、底端中心的直線。
<4>將原代碼中第10行dc.SetWindowExt(1000,1000);//設置邏輯窗口后添加dc.SetWindowOrg(100,100);設置邏輯窗口的原點為(100,100)。觀察運行結果可知,圖1中的圖形整體向左向下分別移動了100個邏輯單位:
(-200,200,200,-200)——>(-200-100,200-100,200-100,-200-100)
?
若需要保持圖1中的圖形,則需要將涉及到的每個點加上(100,100),即:
?
???? dc.Ellipse(-200+100,200+100,200+100,-200+100);
???? //繪制水平垂直的四條半徑
???? dc.MoveTo(0+100,0+100);???? dc.LineTo(200+100,0+100);
???? dc.MoveTo(0+100,0+100);???? dc.LineTo(-200+100,0+100);
???? dc.MoveTo(0+100,0+100);???? dc.LineTo(0+100,200+100);
???? dc.MoveTo(0+100,0+100);???? dc.LineTo(0+100,-200+100)
?
<1>邏輯窗口原點映射為視口原點
<2>邏輯窗口寬度和高度映射為視口寬度和高度
<3>當映射方式為MM_ISOTROPIC時,WindowExt.Width=WindowExt.Height,有效繪圖區域為以視口寬高中的最小邊為邊長的正方形區域。比例因子為:
?
scaleX=scaleY=min{ViewportExt.Width, ViewportExt.Height }/WindowExt.Width
當映射方式為MM_ANISOTROPIC時,有效繪圖區域為整個視口(這里為客戶區)。比例因子為:
?
scaleX=ViewportExt.Width/WindowExt.Width
scaleY=ViewportExt.Height /WindowExt.Height。見圖4.
<4>設備(視口)坐標?= (邏輯坐標–邏輯窗口原點坐標)×比例因子+視口原點坐標
?????????????圖4
以下分析中客戶區大小為ClientRect=(388,200),邏輯窗口原點為WindowOrg=(100,100),基于(3)<4>中修改后的代碼。
在上圖4左中,nMapMode=MM_ISOTROPIC,橢圓外接矩形左上角邏輯坐標(-100,300)映射為客戶區的以Pixel為單位的坐標為:
left_top_X= (-100-100)×(200/1000)+388/2=154 pixel
left_top_Y= (300-100)×(200/1000)+200/2=140 pixel
?
依此次方法可計算出右下角邏輯坐標(300,-100)映射為客戶區的以Pixel為單位的坐標為:
right_bottom_X=234?pixel;right_bottom_Y=60 pixel
?
我們若在第9行dc.SetMapMode(MM_ISOTROPIC);//X=Y前添加CreatePen(PS_SOLID,2,RGB(255,0,0));dc.Ellipse(154,140,234,60);則可以發現,這個以2個像素寬的紅色畫筆繪制的(橢)圓剛好和設置映射模式后繪制的(橢)圓重合。但是我們改變窗口大小時,發現設置映射模式后繪制的(橢)圓按比例拉伸,但紅色圓始終在原地且大小保持不變,這也說明了默認映射方式MM_TEXT是以X軸正方向朝右,Y軸正方向朝下的坐標系和1 pixel為單位進行繪制的。
?
同理,我們可以分析上圖4右中,nMapMode=MM_ANISOTROPIC的情況下,CRect(116,140,272,60);為等效橢圓外接矩形。
?
(4)總結邏輯窗口坐標到設備視口坐標的映射方法:(#add 缺失,待補)
總結
以上是生活随笔為你收集整理的MFC中的GDI绘图的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 对SetViewportOrg和SetW
- 下一篇: VC里的集合类、链表类、映射类