MFC中的GDI繪圖

MFC中的GDI繪圖

原鏈接 http://www.cnblogs.com/weiqubo/archive/2009/12/24/1930029.html

 一.關於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::EndPaintEndPaint()除了釋放設備環境外,還負責從消息隊列中清除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 缺失,待補)


源鏈接 http://www.cnblogs.com/weiqubo/archive/2009/12/24/1930029.html

發佈了20 篇原創文章 · 獲贊 10 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章