mfc CDC

繪圖一般在視圖類的(屏幕/打印機)繪圖消息響應函數OnDraw中進行,例如:
void CTestView::OnDraw(CDC* ) {
         CTestDoc* pDoc = GetDocument();
         ASSERT_VALID(pDoc);
         if (!pDoc)
                return;
         // TODO: 在此處爲本機數據添加繪製代碼

}
每次需要重繪窗口時(程序啓動/窗口大小改變/全部或部分窗口重現/程序員調用RedrawWindow或UpdateWindow),應用程序框架都會調用該CWnd的消息響應成員函數(的覆蓋)來繪製窗口客戶區。
在Windows中,繪圖一般在視圖窗口的客戶區進行,使用的是MFC的設備上下文(DC = Device-Context)類CDC中各種繪圖函數。
在繪圖前,必須先得到客戶區大小和DC、設置繪圖顏色,然後再根據文檔數據或用戶操作來繪製圖形。
1 幾何對象的結構和類
爲了使用繪圖函數,應該先了解繪圖所用到的幾種表示幾何對象的結構和類。這些結構和類分別定義在頭文件windef.h和afxwin.h中。
1.點
1)點結構POINT
點數據結構POINT用來表示一點的x、y座標:
typedef struct tagPOINT { 
      LONG x; 
      LONG y; 
} POINT;
2)點類CPoint
點類CPoint爲一個沒有基類的獨立類,封裝了POINT結構,有成員變量x和y,其構造函數有5種:
CPoint( );
CPoint( int initX, int initY );
CPoint( POINT initPt );
CPoint( SIZE initSize );
CPoint( LPARAM dwPoint ); // 低字設爲x、高字設爲y
CPoint類還定義了4個平移和設置函數:
void Offset(int xOffset, int yOffset);
void Offset(POINT point);
void Offset(SIZE size);
void SetPoint(int X, int Y);
CPoint類還重載了+、-、+=、-=、==、!=等運算符來支持CPoint對象和CPoint、POINT、SIZE對象之間的運算。
2.大小
1)大小結構SIZE
大小(size尺寸)結構SIZE用來表示矩形的寬cx和高cy:
typedef struct tagSIZE {
      LONG cx;
      LONG cy;
} SIZE;
2)大小類CSize
大小類CSize也爲一個沒有基類的獨立類,封裝了SIZE結構,有成員變量cx和cy,其構造函數也有5種:
CSize( );
CSize( int initCX, int initCY );
CSize( SIZE initSize );
CSize( POINT initPt );
CSize( DWORD dwSize ); // 低字設爲cx、高字設爲cy
CSizet類也重載了+、-、+=、-=、==、!=等運算符來支持CSize對象和CSize、POINT、SIZE、RECT對象之間的運算。
3.矩形
1)矩形結構RECT
矩形結構RECT定義了矩形的左上角與右下角的座標:
typedef struct tagRECT {
     LONG left;
     LONG top;
     LONG right;
     LONG bottom;
} RECT;
2)矩形類CRect
矩形類CRect也爲一個沒有基類的獨立類,封裝了RECT結構,有成員變量left、top、right和bottom,其構造函數有6種:
CRect( );
CRect( int l, int t, int r, int b );
CRect( const RECT& srcRect );
CRect( LPCRECT lpSrcRect );
CRect( POINT point, SIZE size );
CRect( POINT topLeft, POINT bottomRight );
CRect類重載了=,+、-,+=、-=,==、!=,&、|,&=、|=等運算符來支持CRect對象和CRect、POINT、SIZE、RECT對象之間的運算。還定義了轉換符LPCRECT和LPRECT來自動完成CRect對象到矩形結構和類指針LPCRECT和LPRECT的轉換。
CRect類中常用的屬性和成員函數有:
int Width( ) const;
int Height( ) const;
CSize Size( ) const;
CPoint& TopLeft( );
CPoint& BottomRight( );
CPoint CenterPoint( ) const;
void SwapLeftRight();
BOOL IsRectEmpty( ) const;
BOOL PtInRect( POINT point ) const;
void SetRect( int x1, int y1, int x2, int y2 );
void SetRect(POINT topLeft, POINT bottomRight);
void OffsetRect(int x, int y);
void MoveToXY(int x, int y);
3) 判斷點是否在矩形中
有時需要判斷某點(如鼠標位置)是否在某一矩形區域中,這可以調用CRect類的PtInRect函數來做:
BOOL PtInRect( POINT point ) const;
該函數當點point在其矩形區域內時,返回真。注意,該矩形區域不包括矩形的右邊界和底邊界。例如:
CRect rect( 10, 10, 371, 267 );
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) 
{
         // TODO: Add your message handler code here and/or call default
         if ( rect.PtInRect( point ) ) {
... ...
         }
... ...
         CView::OnLButtonUp(nFlags, point);
}

2 客戶區大小和DC
在繪圖前,必須先得到客戶區大小和設備上下文DC。
1.獲得客戶區
繪圖一般都是在視圖窗口的客戶區進行,而客戶區的大小在運行時可由用戶改變,爲了使繪製的圖形能隨窗口大小自動改變,必須先得到當前客戶區大小的數據(寬w和高h)。
獲取客戶區大小的方法有如下兩種:
1)在消息響應函數OnSize中獲得
利用屬性窗口的信息頁,在視圖類中添加WM_SIZE消息的響應函數OnSize。該函數在窗口第一次顯示或窗口大小被改變時會被Windows系統調用。其輸入參數中的cx和cy就是客戶區大小的寬和高,可將它們賦值給類變量(如m_iW和m_iH)供繪圖時使用。例如
void CDrawView::OnSize(UINT nType, int cx, int cy) {
        CView::OnSize(nType, cx, cy);

        // TODO: 在此處添加消息處理程序代碼
        m_iW = cx;    m_iH = cy;
}
其中,nType的值爲:
SIZE_MAXIMIZED(窗口已被最大化)
SIZE_MINIMIZED(窗口已被最小化)
SIZE_RESTORED(窗口已被改變大小)
SIZE_MAXHIDE(其他窗口被最大化)
SIZE_MAXSHOW(其他窗口從最大化還原)
2)調用成員函數GetClientRect得到
可在繪圖前,定義一個矩形變量rect,然後再調用CWnd類的成員函數GetClientRect:
void GetClientRect( LPRECT lpRect ) const;
得到當前客戶區矩形的數據,其中的右(right)與底(bottom)就是客戶區的寬與高(其左left與頂top都爲0)。例如:
         RECT rect;
         GetClientRect(&rect);
         int w = rect.right, h = rect.bottom;
2.DC
在Windows中,繪圖使用的是MFC的DC(Device-Context, 設備上下文)類CDC中各種繪圖函數。
0)CDC類
CDC是CObject的直接派生類,CDC類自己也有若干派生類,其中包括窗口客戶區DC所對應的CClientDC類、OnPaint和OnDraw消息響應函數的輸入參數中使用的CPaintDC類、圖元文件對應的CMetaFileDC類和整個窗口所對應的CWindowDC類。
CDC類中有許多成員函數,可以用來設置各種繪圖環境、屬性和參數,以及繪製各種圖形和圖像等,將在後面陸續加以介紹。
1)獲得DC
可以從OnDraw函數的輸入參數pDC或調用CWnd的成員函數GetDC:
CDC* GetDC( );
來獲得DC的指針。
2)釋放DC
因爲Windows限制可用DC的數量,所以DC屬於稀缺的公用資源。因此,對每次獲得的DC,在使用完成後必須立即釋放。
從OnDraw函數的輸入參數pDC獲得的DC,在該函數運行結束後,系統會自動釋放。但由GetDC所獲得的DC,必須自己來釋放,這可以通過調用CWnd的成員函數ReleaseDC來完成:
int ReleaseDC( CDC* pDC ); // 成功返回非0
例如:
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) 
{
         ReleaseCapture();
         if (m_bLButtonDown) {
                CDC* pDC = GetDC();
                pDC->SelectObject(new CPen(PS_SOLID, 0, RGB(255, 0, 0)));
                pDC->SelectStockObject(NULL_BRUSH);
                pDC-> Ellipse (rect);
                ReleaseDC(pDC);
                m_bLButtonDown = FALSE;
         }
         CView::OnLButtonUp(nFlags, point);
}
3)類DC
每次從OnDraw函數的輸入參數或調用GetDC所獲得的DC,都是一個全新的臨時缺省DC。它不能用類變量來長期保存,而且原來選入的各種GDI對象全都被作廢,必須從頭再來。
爲了使選入的各種GDI對象一直有效,必須在視圖類的PreCreateWindow函數中調用CWnd類的成員函數AfxRegisterWndClass:
LPCTSTR AFXAPI AfxRegisterWndClass( UINT nClassStyle, HCURSOR hCursor = 0,
HBRUSH hbrBackground = 0, HICON hIcon = 0 );
來修改窗口類的風格屬性中的DC爲類DC:CS_CLASSDC。如
BOOL CDrawView::PreCreateWindow(CREATESTRUCT& cs) {
      cs.lpszClass = AfxRegisterWndClass(CS_DBLCLKS | CS_HREDRAW | 
CS_VREDRAW | CS_CLASSDC, 0, 
::CreateSolidBrush(RGB(255, 255, 255)));
         return CView::PreCreateWindow(cs);
}
4)安全DC句柄
也可以用CDC類的成員函數:
HDC GetSafeHdc();
來獲取CD所對應窗口(如客戶區)的安全DC句柄,該句柄在窗口存在期間一直是有效的。例如,可先定義類變量HDC m_hDC;,再在適當的地方給它賦值m_hDC = GetDC()->GetSafeHdc();,然後就可以放心地使用了。例如,可以使用CDC類的成員函數
BOOL Attach(HDC hDC); // 成功返回非0
來將CDC對象與DC句柄連接在一起。

3 設置繪圖顏色
1.顏色
Windows中的顏色一般用4個字節表示(0BGR(整數) = R G B 0(字節序) [Intel CPU低位字節在前]),Win32 API中定義了一個專門表示顏色索引值的變量類型COLORREF:(windef.h)
typedef DWORD COLORREF; // 0x00bbggrr
和一個由紅綠藍三原色構造顏色值的宏RGB:(wingdi.h)
#define RGB(r,g,b) ((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)))
其中,r、g、b爲字節變量,取值範圍爲0~255。其函數說明爲:
COLORREF RGB(
BYTE bRed,      // red component of color
BYTE bGreen, // green component of color
BYTE bBlue      // blue component of color
);
例如:
                COLORREF red, gray;
                red = RGB(255, 0, 0);
                gray = RGB(128, 128,128);
在API中還定義了由COLORREF變量獲取各個顏色分量的宏Get?Value:(wingdi.h)
#define GetRValue(rgb) (LOBYTE(rgb))
#define GetGValue(rgb) (LOBYTE(((WORD)(rgb)) >> 8))
#define GetBValue(rgb) (LOBYTE((rgb)>>16))
其中:
typedef unsigned long ULONG_PTR;
typedef ULONG_PTR DWORD_PTR;
#define LOBYTE(w)    ((BYTE)((DWORD_PTR)(w) & 0xff))
它們對應的函數說明爲:
BYTE GetRValue(DWORD rgb); // DWORD rgb ~ COLORREF col
BYTE GetGValue(DWORD rgb);
BYTE GetBValue(DWORD rgb);
2.點色(像素)
在Windows中,像素(pixel)的顏色是直接由設備上下文類CDC的成員函數SetPixel來設置的,該函數的原型爲:
COLORREF SetPixel( int x, int y, COLORREF crColor );
COLORREF SetPixel( POINT point, COLORREF crColor );
其中,x與y分別爲像素點的橫座標與縱座標,crColor爲像素的顏色值。例如:
pDC->SetPixel(10, 10, RGB(0, 255, 0));
         另外,也可以用CDC的成員函數
COLORREF GetPixel( int x, int y ) const;
COLORREF GetPixel( POINT point ) const;
來獲得指定點(x, y)或point的顏色。例如:
      COLORREF col;
      col = pDC->GetPixel(10, 10);
3.線色(筆)
在Windows中,線狀圖必須用筆(pen)來畫,所以線的顏色就由筆色來確定。在MFC中,筆的屬性和功能由CPen類提供(CPen是CGDIObject的派生類)。
筆的創建與使用的步驟爲:
創建筆對象:創建筆類CPen對象的方法有如下兩種:
使用構造函數CPen
CPen( int nPenStyle, int nWidth, COLORREF crColor );
其中:
nPenStyle爲筆的風格,可取值:
PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOT, PSDASHDOTDOT

注意:1~4號筆風格只是在筆寬=0或1時有效,筆寬>1時總爲實心的。
nWidth爲筆寬,與映射模式有關,使用缺省映射時爲像素數,若nWidth = 0,則不論什麼映射模式,筆寬都爲一個像素;
crColor爲筆的顏色值。
例如
CPen* pGrayPen = new CPen(PS_SOLID, 0, RGB(128, 128, 128));
CPen grayPen(PS_SOLID, 0, RGB(128, 128, 128));
使用成員函數CreatePen
BOOL CreatePen( int nPenStyle, int nWidth, COLORREF crColor );
如:
CPen grayPen;
grayPen.CreatePen(PS_SOLID, 0, RGB(128, 128, 128));
缺省的筆爲單像素寬的實心黑色筆
將筆對象選入設備上下文:爲了能使用我們所創建的筆對象,必須先將它選入設備上下文,這可以調用設備上下文類CDC的成員函數SelectObject來完成:
CPen* SelectObject( CPen* pPen );
返回值爲指向原來筆對象的指針(一般將其保存下來,供下次再裝入時使用)。如
pOldPen = pDC->SelectObject(&pen);
另外,Windows中有一些預定義的筆對象,可用CDC的另一成員函數SelectStockObject將其選入DC,其函數原型爲:
virtual CGdiObject* SelectStockObject( int nIndex );
預定義的筆對象有BLACK_PEN(黑色筆)、WHITE_PEN (白色筆)、NULL_PEN(空筆/無色筆)。例如:
pDC->SelectStockObject(BLACK_PEN);
使用設備上下文畫線狀圖:畫線狀圖以及面狀圖的邊線,所使用的是當前設備上下文中的筆對象。線狀圖有直線、折線、矩形、(橢)圓(弧)等)
將筆對象從設備上下文中放出:爲了能刪除使用過的筆對象,必須先將它從設備上下文中釋放出來後,然後才能刪除。釋放的方法是裝入其他的筆對象(一般是重新裝入原來的筆對象)。例如
pDC->SelectObject(pOldPen);
刪除筆對象:爲了能刪除筆對象,必須先將其從設備上下文中釋放。刪除方法有如下幾種:
調用筆類CDC的成員函數DeleteObject刪除筆的當前內容(但是未刪除筆對象,以後可再用成員函數CreatePen在筆對象中繼續創建新的筆內容)。如
pen.DeleteObject();
使用刪除運算符delete將筆對象徹底刪除,如delete pen;
自動刪除:若筆對象爲局部變量,則在離開其作用域時,會被系統自動刪除
下面爲一段較完整地創建與使用筆的例子代碼:
         CPen pen, *pOldPen;
         for (int j = 0; j <= 255; j++) {
                HSLtoRGB(m_hue, m_sat, 255 - j, r, g, b); // 自定義的函數
                pen.CreatePen(PS_SOLID, 0, RGB(r, g, b));
                pOldPen = pDC->SelectObject(&pen);
                pDC->MoveTo(0, j); pDC->LineTo(40, j);
                pDC->SelectObject(pOldPen);
                pen.DeleteObject();
         }

面色(刷)
在Windows中,面狀圖必須用刷(brush)來填充,所以面色是由刷色來確定的。MFC中的刷類爲CBrush(它也是CGDIObject的派生類),刷的創建與使用的步驟與筆的相似。
構造函數有4個:
CBrush( ); // 創建一個刷的空對象
CBrush( COLORREF crColor ); // 創建顏色爲crColor的實心刷
CBrush( int nIndex, COLORREF crColor ); // 創建風格由nIndex指定且顏色爲crColor的條紋(hatch孵化)刷,其中nIndex可取條紋風格(Hatch Styles)值:
符號常量 數字常量 風格
HS_HORIZONTAL 0 水平線
HS_VERTICAL 1 垂直線
HS_FDIAGONAL 2 正斜線
HS_BDIAGONAL 3 反斜線
HS_CROSS 4 十字線(正網格)
HS_DIAGCROSS 5 斜十字線(斜網格)

CBrush( CBitmap* pBitmap ); // 創建位圖爲pBitmap的圖案刷
如:pDC->FillRect( &rect, new CBrush( RGB(r, g, b) ) );
與構造函數相對應,有多個創建不同類型刷的成員函數:
BOOL CreateSolidBrush( COLORREF crColor );
BOOL CreateHatchBrush( int nIndex, COLORREF crColor );
BOOL CreatePatternBrush( CBitmap* pBitmap );
BOOL CreateDIBPatternBrush( HGLOBAL hPackedDIB, UINT nUsage );
BOOL CreateDIBPatternBrush( const void* lpPackedDIB, UINT nUsage );
BOOL CreateBrushIndirect( const LOGBRUSH* lpLogBrush );
BOOL CreateSysColorBrush( int nIndex );
預定義的刷對象有BLACK_BRUSH(黑刷)、DKGRAY_BRUSH(暗灰刷)、GRAY_BRUSH(灰刷)、HOLLOW_BRUSH(空刷)、LTGRAY_BRUSH(亮灰刷)、NULL_BRUSH(空刷)、WHITE_BRUSH(白刷)
缺省的刷爲空刷
與筆一樣,可以用函數SelectObject或SelectStockObject將自定義的刷或預定義的刷選入DC中,供繪面狀圖時使用。
5.文本色
可使用CDC類的成員函數SetTextColor和SetBkColor來分別設置輸出文本的前景色和背景色:(缺省的前景色爲黑色,背景色空)
COLORREF GetTextColor( ) const;
virtual COLORREF SetTextColor( COLORREF crColor );
COLORREF GetBkColor( ) const; 
virtual COLORREF SetBkColor( COLORREF crColor );
例如:
         pDC->TextOut(10, 10, "Test text");
         pDC->SetTextColor(RGB(0, 128, 0)); pDC->TextOut(10, 30, "Test text");
         pDC->SetBkColor(RGB(0, 0, 128));       pDC->TextOut(10, 50, "Test text");
6.繪圖工具
1)GDI對象
Windows的圖形設備接口(GDI = graphics device interface)對象指各種繪圖工具,如筆、刷、位圖、字體、調色板、區域等,對應的MFC類爲CPen、CBrush、CBitmap、CFont等。這些圖形繪製對象類都是CGDIObject的派生類,而CGDIObject則是直接從CObject類派生的抽象基類。其中,Windws CE不支持調色板類CPalette;CRgn爲區域類,對應於窗口中的一個矩形、多邊形或(橢)圓區域(region),可用於移動、拷貝、合併、判斷和裁剪。
2)選入
可用設備上下文類CDC的多態成員函數SelectObject,來將繪圖工具對象選入設備上下文,以供繪圖時使用:
CPen* SelectObject( CPen* pPen );
CBrush* SelectObject( CBrush* pBrush );
virtual CFont* SelectObject( CFont* pFont );
CBitmap* SelectObject( CBitmap* pBitmap );
int SelectObject( CRgn* pRgn );
CGdiObject* SelectObject( CGdiObject* pObject );
3)獲取
可用API函數GetCurrentObject來獲得當前在DC中的指定類型的繪圖對象:
HGDIOBJ GetCurrentObject(
HDC hdc,             // handle to device context
UINT uObjectType     // specifies the object-type
);
其中,參數uObjectType可取值:
OBJ_PEN        // Returns the current selected pen. 
OBJ_BRUSH // Returns the current selected brush. 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章