360 likes | 537 Views
Visual C++ Windows Programming. 第五章 基本繪圖原理. 大綱. Windows 的基本繪圖原理 基本繪圖函式. Windows 的基本繪圖原理. 繪圖裝置介面 (GDI) 在視窗介面下,資料輸出的方式,相較於文字模式是較為複雜的,但也更具彈性且較多變。在視窗介面下顯示的所有資料,都被視為圖形,不論資料看起來是線段、圖形或者文字。
E N D
Visual C++ Windows Programming 第五章 基本繪圖原理
大綱 • Windows 的基本繪圖原理 • 基本繪圖函式
Windows 的基本繪圖原理 • 繪圖裝置介面 (GDI) • 在視窗介面下,資料輸出的方式,相較於文字模式是較為複雜的,但也更具彈性且較多變。在視窗介面下顯示的所有資料,都被視為圖形,不論資料看起來是線段、圖形或者文字。 • 在視窗程式裡,資料輸出均透過繪圖裝置介面 (graphics device interface; GDI) 來完成,由於運用 GDI 函式的關係,使得程式設計師只要運用相同的函式,就可以將資料輸出到不同的裝置上。換句話說,對於程式而言,將資料顯示在螢幕上,與將資料輸出到印表機的方法是一樣的。
應用程式 GDI 印表機驅動程式 螢幕驅動程式 印表機 螢幕
Windows 的基本繪圖原理 (續) • 裝置內文 (DC) • 當我們想要在視窗的工作區中繪製圖案,或者將工作區中的資料列印出來時,都必須先獲得一個裝置內文 (device context; DC) 。 • 裝置 (device) 泛指各種與資料輸出有關的設備,例如:螢幕,印表機等,以及各種特定格式的檔案,如 BMP 圖檔。 • 所謂的內文 (context) ,是指將輸出於各種裝置的資料。當你需要將你建立的資料輸出至裝置時,就必須為這個裝置準備一個 DC ,以便儲存欲輸出的資料。而這些資料就是使用 GDI 函數繪圖的結果。
Windows 的基本繪圖原理 (續) • 裝置內文是一個用以溝通程式與裝置的媒介。 • 不論是要在視窗客戶區顯示繪圖,或是將圖輸出到印表機裡印出來,所需要做的就是為那個裝置產生一個DC,然後利用GDI函數繪製圖形.透過GDI函數與DC的協助,可以讓我們不論將資料輸出到那個裝置,都使用相同的方法. • 顏色的定義 • RGB巨集 • (0,0,0):黑色, (255,255,255):白色 • COLORREF color = RGB(255, 0, 0);
Windows 的基本繪圖原理 (續) • MFC 的 GDI Classes (CGdiObject) • CBitmap:用以建立操作點陣圖的物件。 • CPen:用於建立操作畫筆的物件。 • CBrush:用於建立操作畫刷的物件。 • CFont:用於建立操作文字的物件。 • CRgn:用於建立繪製圖形的物件。 • CPalette:用於建立調色盤的物件。
Windows 的基本繪圖原理 (續) • MFC 的 DC Classes (CDC) • CClientDC:用於將資料輸出至視窗工作區。 • CWindowDC:用於將資料輸出至視窗中,包含工作區以外的區域。 • CPaintDC:用於輸出回應 WM_PAINT 訊息的資料。 • CMetaFileDC:用於將資料輸出至特殊格式的檔案中。
DC's GDI's
基本繪圖函式 • 畫點 (point) 函式 COLORREF CDC::SetPixel(int x, int y, COLORREF color); COLORREF CDC::SetPixel(POINT point, COLORREF color); COLORREF CDC::GetPixel(int x, int y) const; COLORREF CDC::GetPixel(POINT point) const; virtual BOOL CDC::PtVisible(int x, int y) const; virtual BOOL CDC::PtVisible(POINT point) const; //如果此像點恰好被其它視窗遮住就看不到了. • 畫線 (line) 函式 BOOL CDC::MoveTo(int x, int y); BOOL CDC::MoveTo(POINT point); BOOL CDC::LineTo(int x, int y); BOOL CDC::LineTo(POINT point);
基本繪圖函式 (續) • 畫弧 (arc) 函式與畫鐘 (chord) 函式 BOOL CDC::Arc(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4); BOOL CDC::Arc(LPCRECT r, POINT Start, POINT End); BOOL CDC::AngleArc(int x, int y, int radius, float startAngle, float sweepAngle); BOOL CDC::Chord(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4); BOOL CDC::Chord(LPCRECT r, POINT Start, POINT End); • 畫餅 (pie) 函式 BOOL CDC::Pie(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4); BOOL CDC::Pie(LPCRECT r, POINT Start, POINT End);
Chord Arc (x1,y1) (x1,y1) (x4,y4) (x3,y3) (x4,y4) (x3,y3) (x2,y2) (x2,y2) Pie AngleArc (x1,y1) (x4,y4) sweepAngle (x3,y3) radius startAngle (x,y) (x2,y2)
基本繪圖函式 (續) • 畫橢圓 (ellipses) 函式 BOOL CDC::Ellipses(int x1, int y1, int x2, int y2); BOOL CDC::Ellipses(LPCRECT rect); • 畫矩形 (rectangle) 函式 BOOL CDC::Rectangle(int x1, int y1, int x2, int y2); BOOL CDC::Rectangle(LPCRECT rect); void CDC::FrameRect(LPCRECT lpRect, CBrush *pBrush); void CDC::InvertRect(LPCRECT lpRect);//不會畫出任何東西,只是把指定矩形中所有像點都作一次NOT運算而已. BOOL CDC::RoundRect(int x1, int y1, int x2, int y2, int x3, int y3); BOOL CDC::RoundRect(LPCRECT lpRect, POINT point); BOOL CDC::RectVisible(LPCRECT rect) const;//只要有一丁點露出來,就是T
基本繪圖函式 (續) • 畫多邊形 (polygon) 函式 BOOL CDC::Polygon(LPPOINT lpPoints, int nCount); //多邊形 BOOL CDC::Polyline(LPPOINT lpPoints, int nCount); //不會封閉 BOOL CDC::PolyPolygon(LPPOINT lpPoints, LPINT lpPolyCounts, int nCount); //好幾個多邊形 BOOL CDC::PolylineTo(const POINT *lpPoints, int nCount); //類似Polyline,但會改變游標之值,像是作了一大串的LineTo BOOL CDC::PolyBezier(const POINT *lpPoints, int nCount);
物件Collection • CArray樣板類別 • 你可以用這個樣板將任何物件存在array內,讓array可以視需要自動加大. CArray<ObjectType, ObjectType&> anArray 成員函式的引數型態 要存放的物件型態
Index Object1 0 Object2 1 GetAt(2) SetSize(5) 定義初始大小 Object3 2 傳回物件 Object4 3 Object5 4 Add(Obj A) Object6 5 存放物件 Obj A 6 自動增加
CList<ObjectType, ObjectType&> aList; AddHead(ObjA) Obj A 存放物件 傳回型態為Position 的值 Object1 大小的增加 是自動的 Object2 aPos Object3 GetNext(aPos) Object4 AddTail(ObjB) Obj B
Object1 Object2 Object3 Object4 Key1 Key2 Key3 Key4 CMap<KeyType, KeyType&,ObjectType, ObjectType&> aMap; aMap[Key2]=Object2 LookUp(Key3,AnObject)
#include <afxwin.h> #include <afxtempl.h> #include <afxext.h> #include "resource.h" class CGObject : public CObject {//儲存形狀物件的類別,衍生自CObject類別以便具備儲存(serialize)的功能 public: int shapenum; //形狀代號 BOOL fill; //是否填滿 COLORREF FillColor, LineColor; //填滿顏色與外框顏色 int width; //外框寛度 CPoint StartPnt, EndPnt; //形狀的起點與終點 CGObject() { }//預設建構子 CGObject(int shapenum, BOOL fill, COLORREF FillColor, COLORREF LineColor, int width, CPoint StartPnt, CPoint EndPnt) : shapenum(shapenum), fill(fill), FillColor(FillColor), LineColor(LineColor), width(width), StartPnt(StartPnt), EndPnt(EndPnt) { } //一般建構子 CGObject(CGObject &g) : //複製建構子 shapenum(g.shapenum), fill(g.fill), FillColor(g.FillColor), LineColor(g.LineColor), width(g.width), StartPnt(g.StartPnt), EndPnt(g.EndPnt) { } //過載 = 運算子 CGObject & operator = (CGObject &g) { shapenum = g.shapenum; fill = g.fill; FillColor = g.FillColor; LineColor = g.LineColor; width = g.width; StartPnt = g.StartPnt; EndPnt = g.EndPnt; return *this; } } ;
class Shape { //形狀類別的基礎類別 protected: CPoint StartPnt, EndPnt; //定義形狀的起點與終點 int shapenum; //形狀代號 friend class CMyView; //將MyView設為friend類別,方便資料存取 public: //一般建構子 Shape(CPoint StartPnt, CPoint EndPnt, int shapenum) : StartPnt(StartPnt), EndPnt(EndPnt), shapenum(shapenum) { } //複製建構子 Shape(Shape &s) : StartPnt(s.StartPnt), EndPnt(s.EndPnt), shapenum(s.shapenum) { } Shape() { } //預設建構子 //過載 = 運算子 Shape & operator = (Shape &s) { StartPnt = s.StartPnt; EndPnt = s.EndPnt; //shapenum = s.shapenum; return *this; } virtual void draw(CDC &aDC, COLORREF color, COLORREF fcolor, int width, BOOL Filled = false) = 0; //純虛擬函式 int GetShapeNum() { return shapenum; }//取得形狀代號 void SetPoint(CPoint SPnt, CPoint EPnt) { StartPnt = SPnt; EndPnt = EPnt; }//設定起,終點 } ;
class Line : public Shape { //直線類別 public: friend class CMyView; Line() { shapenum = 0; } //預設建構子 Line(CPoint StartPnt, CPoint EndPnt) : Shape(StartPnt, EndPnt, 0) { } //一般建構子 Line(Line &l) : Shape(l.StartPnt, l.EndPnt, 0) { } //複製建構子 Line & operator = (Line &l) { //過載 = 運算子 StartPnt = l.StartPnt; EndPnt = l.EndPnt; return *this; } //Line::draw 繪出直線 void draw(CDC &dc, COLORREF color, COLORREF fcolor, int width, BOOL Filled = false) { CPen pen(PS_SOLID, width, color); //建立畫筆物件 CPen *oldPen = dc.SelectObject(&pen); //設定DC物件使用pen物件,並將舊的default pen物件儲存起來 dc.MoveTo(StartPnt); //移到直線起點 dc.LineTo(EndPnt); //畫到直線終點 dc.SelectObject(oldPen); //將原來的pen還原 } } ;
class ellipse : public Shape { public: friend class CMyView; ellipse() { shapenum = 1; } ellipse(CPoint StartPnt, CPoint EndPnt) : Shape(StartPnt, EndPnt, 1) { } ellipse(ellipse &e) : Shape(e.StartPnt, e.EndPnt, 1) { } ellipse & operator = (ellipse &e) { StartPnt = e.StartPnt; EndPnt = e.EndPnt; return *this; } void draw(CDC &dc, COLORREF color, COLORREF fcolor, int width, BOOL Filled = false) { CRect rect(StartPnt, EndPnt); CPen pen(PS_SOLID, width, color); CPen *oldPen = dc.SelectObject(&pen); dc.SelectStockObject(NULL_BRUSH); //設定DC物件不使用畫刷 dc.Ellipse(rect); dc.SelectObject(oldPen); } } ; class rectangle : public Shape { public: friend class CMyView; rectangle() { shapenum = 2; } rectangle(CPoint StartPnt, CPoint EndPnt) : Shape(StartPnt, EndPnt, 2) { } rectangle(rectangle &r) : Shape(r.StartPnt, r.EndPnt, 2) { } rectangle & operator = (rectangle &r) { StartPnt = r.StartPnt; EndPnt = r.EndPnt; return *this; } void draw(CDC &dc, COLORREF color, COLORREF fcolor, int width, BOOL Filled = false) { CRect rect(StartPnt, EndPnt); CPen pen(PS_SOLID, width, color); CPen *oldPen = dc.SelectObject(&pen); dc.SelectStockObject(NULL_BRUSH); dc.Rectangle(rect); dc.SelectObject(oldPen); } } ;
class CMyDocument : public CDocument { private: CArray<CGObject, CGObject&> gArray; //儲存CGObject物件的CArray容器物件 public: void AddObject(CGObject &g) { gArray.Add(g); } CGObject & GetObject(int i) { return gArray[i]; } int GetSize() { return gArray.GetSize(); } DECLARE_DYNCREATE(CMyDocument) DECLARE_MESSAGE_MAP() } ; IMPLEMENT_DYNCREATE(CMyDocument, CDocument) BEGIN_MESSAGE_MAP(CMyDocument, CDocument) END_MESSAGE_MAP()
class CMyFrame : public CFrameWnd { protected: CMenu *menu; public: CToolBar RGBBar, ShapeBar; CStatusBar statusbar; CMyFrame() { } ~CMyFrame() { } // afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct) { if(CFrameWnd::OnCreate(lpCreateStruct)) return -1; RGBBar.Create(this); RGBBar.LoadToolBar(IDR_TBRGB); RGBBar.EnableDocking(CBRS_ALIGN_ANY); RGBBar.SetBarStyle(RGBBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC); ShapeBar.Create(this); ShapeBar.LoadToolBar(IDR_TBSHAPE); ShapeBar.EnableDocking(CBRS_ALIGN_ANY); ShapeBar.SetBarStyle(RGBBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC); EnableDocking(CBRS_ALIGN_ANY); DockControlBar(&RGBBar); DockControlBar(&ShapeBar); static UINT indicators[] = { ID_SEPARATOR, IDS_RED, IDS_LINE } ; statusbar.Create(this); statusbar.SetIndicators(indicators, sizeof(indicators) / sizeof(UINT)); return 0; } DECLARE_DYNCREATE(CMyFrame) DECLARE_MESSAGE_MAP() } ; IMPLEMENT_DYNCREATE(CMyFrame, CFrameWnd) BEGIN_MESSAGE_MAP(CMyFrame, CFrameWnd) ON_WM_CREATE() END_MESSAGE_MAP()
class CMyView : public CView { private: COLORREF lcolor, fcolor;// lcolor為形狀外框的顏色,fcolor為填滿內部的顏色 Shape *aShape; //指向欲繪製之形狀類別的物件指標 Shape *rdShape; //為重繪視窗時,從Document中取得形狀物件 int width; //形狀外框的寬度 HCURSOR hcursor; public: CMyView() { lcolor = RGB(255, 0, 0); aShape = new Line; fcolor = RGB(0, 0, 0); width = 2; } ~CMyView() { } afx_msg void OnEllipse() { CString resstr; aShape = new ellipse; resstr.LoadString(IDS_ELLIPSE); ((CMyFrame *) GetParentFrame())->statusbar.SetPaneText(2, resstr.GetBuffer(80)); } afx_msg void OnRect() { CString resstr; aShape = new rectangle; resstr.LoadString(IDS_RECTANGLE); ((CMyFrame *) GetParentFrame())->statusbar.SetPaneText(2, resstr.GetBuffer(80)); } afx_msg void OnLine() { CString resstr; aShape = new Line; resstr.LoadString(IDS_LINE); ((CMyFrame *) GetParentFrame())->statusbar.SetPaneText(2, resstr.GetBuffer(80)); } afx_msg void OnDraw(CDC *aDC) { CMyDocument *doc = (CMyDocument *) GetDocument(); int num = doc->GetSize(); //取得目前Document物件中儲存的形狀物件個數 CView::OnDraw(aDC); int i; for(i = 0; i < num; i++) { CGObject *object = &(doc->GetObject(i)); switch(object->shapenum) { case 0: rdShape = new Line; break; case 1: rdShape = new ellipse; break; case 2: rdShape = new rectangle; break; } rdShape->SetPoint(object->StartPnt, object->EndPnt); rdShape->draw((*aDC), object->LineColor, object->FillColor, object->width); delete rdShape; } }
afx_msg void OnLButtonUp(UINT, CPoint point) { if(this == GetCapture()) { CClientDC aDC(this); (*aShape).EndPnt = point; (*aShape).draw(aDC, lcolor, fcolor, width); CGObject object(aShape->GetShapeNum(), true, fcolor, lcolor, width, aShape->StartPnt, aShape->EndPnt); CMyDocument *doc = (CMyDocument *) GetDocument(); doc->AddObject(object); ReleaseCapture(); } } afx_msg void OnRed() { CString resstr; lcolor = RGB(255, 0, 0); resstr.LoadString(IDS_RED); ((CMyFrame *) GetParentFrame())->statusbar.SetPaneText(1, resstr.GetBuffer(80)); } afx_msg void OnGreen() { CString resstr; lcolor = RGB(0, 255, 0); resstr.LoadString(IDS_GREEN); ((CMyFrame *) GetParentFrame())->statusbar.SetPaneText(1, resstr.GetBuffer(80)); } afx_msg void OnBlue() { CString resstr; lcolor = RGB(0, 0, 255); resstr.LoadString(IDS_BLUE); ((CMyFrame *) GetParentFrame())->statusbar.SetPaneText(1, resstr.GetBuffer(80)); } afx_msg void OnLButtonDown(UINT, CPoint point) { SetCapture(); if(this == GetCapture()) { (*aShape).StartPnt = (*aShape).EndPnt = point; switch((*aShape).shapenum) { case 0: hcursor = AfxGetApp()->LoadCursor(IDC_LINE); ::SetCursor(hcursor); break; case 1: hcursor = AfxGetApp()->LoadCursor(IDC_ELLIPSE); ::SetCursor(hcursor); break; case 2: hcursor = AfxGetApp()->LoadCursor(IDC_RECT); ::SetCursor(hcursor); break; } } } afx_msg void OnMouseMove(UINT, CPoint point) { if(this == GetCapture()) { CClientDC aDC(this); aDC.SetROP2(R2_NOT); (*aShape).draw(aDC, lcolor, fcolor, width); (*aShape).EndPnt = point; (*aShape).draw(aDC, lcolor, fcolor, width); } }
afx_msg void OnUpdateEllipse(CCmdUI *aCmdUI) { aCmdUI->SetCheck((*aShape).shapenum == 1); } afx_msg void OnUpdateRect(CCmdUI *aCmdUI) { aCmdUI->SetCheck((*aShape).shapenum == 2); } afx_msg void OnUpdateLine(CCmdUI *aCmdUI) { aCmdUI->SetCheck((*aShape).shapenum == 0); } afx_msg void OnUpdateRed(CCmdUI *aCmdUI) { aCmdUI->SetCheck(lcolor == RGB(255, 0, 0)); } afx_msg void OnUpdateGreen(CCmdUI *aCmdUI) { aCmdUI->SetCheck(lcolor == RGB(0, 255, 0)); } afx_msg void OnUpdateBlue(CCmdUI *aCmdUI) { aCmdUI->SetCheck(lcolor == RGB(0, 0, 255)); } DECLARE_DYNCREATE(CMyView) DECLARE_MESSAGE_MAP() } ; IMPLEMENT_DYNCREATE(CMyView, CView) BEGIN_MESSAGE_MAP(CMyView, CView) ON_WM_LBUTTONDOWN() ON_WM_MOUSEMOVE() ON_WM_LBUTTONUP() ON_COMMAND(IDM_RED, OnRed) ON_COMMAND(IDM_GREEN, OnGreen) ON_COMMAND(IDM_BLUE, OnBlue) ON_COMMAND(IDM_LINE, OnLine) ON_COMMAND(IDM_ELLIPSE, OnEllipse) ON_COMMAND(IDM_RECTANGLE, OnRect) ON_UPDATE_COMMAND_UI(IDM_RED, OnUpdateRed) ON_UPDATE_COMMAND_UI(IDM_GREEN, OnUpdateGreen) ON_UPDATE_COMMAND_UI(IDM_BLUE, OnUpdateBlue) ON_UPDATE_COMMAND_UI(IDM_LINE, OnUpdateLine) ON_UPDATE_COMMAND_UI(IDM_ELLIPSE, OnUpdateEllipse) ON_UPDATE_COMMAND_UI(IDM_RECTANGLE, OnUpdateRect) END_MESSAGE_MAP()
class CMyApp : public CWinApp { public: BOOL InitInstance() { CDocument *aDOC; CSingleDocTemplate *aDocTemplate; aDocTemplate = new CSingleDocTemplate(IDR_MENU, RUNTIME_CLASS(CMyDocument), RUNTIME_CLASS(CMyFrame), RUNTIME_CLASS(CMyView)); AddDocTemplate(aDocTemplate); aDOC = aDocTemplate->CreateNewDocument(); CFrameWnd *Frame = aDocTemplate->CreateNewFrame(aDOC, NULL); m_pMainWnd = Frame; aDocTemplate->InitialUpdateFrame(Frame, aDOC); Frame->ShowWindow(SW_SHOW); return true; } } ; CMyApp a_app;
視窗 形狀類別 執行繪圖 繪圖方法 呼叫 滑鼠訊息/視窗訊息 CMyView 訊息處理方法
1.點選 2 Afx_msg void MyView::OnRect { aShape = new rectangle; } 3.拖曳 6.放開 3 7 Afx_msg MyView::OnMouseMove() { aShape->draw() } Afx_msg MyView::OnLButtonUp() { aShape->draw() } 4 Virtual shape::draw()=0 Virtual rectangle:draw() {} 5
調整視窗大小 重繪畫布 發出WM_PAINT訊息呼叫 MyView::OnDraw 存取MyDocument中的gArray
1 Class CGObject: public CObject { .. CGObject(){}; Void Serialize(CArchive & ar) { …}; DECLARE_SERIAL(CGObject); }; IMPLEMENT_SERIAL(CGObject, CObject, 1) 2 3 4_1 4_2
Void CGObject::Serialize (CArchive & ar) { CObject::Serialize(ar); If (ar.IsStoring()) { ar << Shapenum << fill << fillcolor << linecolor << width << StartPnt << EndPnt; } Else { ar >> … } }
Void CMyDocment::Serialize(CArchive & ar) { CObject::Serialize(ar); gArray.Serialize(ar); } Void CMyDocument::AddObject(CGObject & g) { gArray.Add(g); SetModifiedFlag(true); }
New Open Save Save as ID_FILE_NEW ID_FILE_OPEN ID_FILE_SAVE ID_FILE_SAVE_AS ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew) ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)