windows基礎程式設計----第四篇(呼叫GDI繪製出相關圖形)
這一篇就來了解一下windows的繪圖API。
在windows應用程式中如果想要畫圖,就必須呼叫一定的介面,而這個介面需要有一定的庫來支援。
這裡,我們就用比較簡單的GDI32.dll這個庫提供的介面來進行畫圖。這個庫預設在系統中有,並不需要包含進來。
先看下這一篇能夠實現的效果:
很簡潔。它的程式碼如下:
可以複製貼上看看執行效果是否和我一樣。//視窗過程處理函式 LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_DESTROY: PostQuitMessage(0); return 0; case WM_PAINT: //1.獲得裝置環境 //2.呼叫GDI介面 //3.釋放裝置環境 PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); //矩形 Rectangle(hdc, 0, 0, 100, 100); //扇形 Pie(hdc, 100, 0, 200, 100, 100, 0, 150, 50); //橢圓 不過這裡因為長寬相同,所以變成了圓,其實圓算是橢圓中一種特殊的圓了 Ellipse(hdc, 200, 0, 300, 100); //圓角矩形 RoundRect(hdc, 300, 0, 400, 100, 10, 10); //三角形 MoveToEx(hdc, 400, 100, NULL); LineTo(hdc, 500, 100); LineTo(hdc, 450, 0); LineTo(hdc, 400, 100); MoveToEx(hdc,0, 0, NULL); //三角形的另一種畫法 POINT apt[4] = { 300, 300, 400, 300, 350, 200 ,300,300}; MoveToEx(hdc,300, 300,NULL); PolylineTo(hdc, apt, 4); EndPaint(hwnd, &ps); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); } //主函式 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nCmdShow) { //設計視窗類 WNDCLASS wndclass = { CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInstance, LoadIcon(NULL, IDI_APPLICATION), LoadCursor(NULL, IDC_ARROW), (HBRUSH)GetStockObject(WHITE_BRUSH), NULL, TEXT("MYDEMO") }; //註冊視窗類 RegisterClass(&wndclass); //建立視窗類 HWND hwnd = CreateWindow( TEXT("MYDEMO"), TEXT("MyDemo"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); //顯示視窗 ShowWindow(hwnd, nCmdShow); //更新視窗 UpdateWindow(hwnd); //訊息迴圈 MSG Msg; BOOL bRet; while ((bRet = GetMessage(&Msg, NULL, 0, 0)) != 0) { if (bRet == -1) { break; } else { TranslateMessage(&Msg); DispatchMessage(&Msg); } } return Msg.wParam; }
之所以能夠實現這些,是因為我們直接呼叫了windows的GDI介面。GDI是什麼呢?
GDI,圖形裝置介面(Graphics Device Interface)。
嗯,既然知道用這個介面了,就應該開始畫了吧?其實不急,因為要通過我們的介面來實現硬體的配合。這當中需要執行一定的順序,我們來看一張圖:
由圖中我們可以看出,應用程式呼叫的介面之後需要與windows裝置驅動程式打交道,然後再由windows裝置驅動程式在與不同硬體實現出來。這當中,我們注意到了右邊有個裝置描述表,沒錯,其實,如果我們要在不同硬體中實現我們的效果,需要定義出一個裝置描述表。然後再當中進行介面的呼叫。關於這個裝置描述表具體不多說,我們只需要知道,如果你要繪圖,就需要創建出一個使用裝置的環境出來,只有這個環境搭建好了,我們才可以進行繪畫,而這個裝置描述表就類似於這個環境。
好了,實現繪圖,最簡單的繪圖步驟有三步:
1.裝置環境的準備;
2.呼叫GDI介面;
3.釋放裝置環境。
在前面的篇章中,我們說到了windows的訊息機制。這裡便關聯到了。當視窗被重新整理的時候,其實系統會發送一個WM_PAINT訊息給視窗處理函式。而這個訊息就是視窗重新繪製的訊息。
從剛剛展示的圖中很明顯的看出這裡有五種圖形。其實,還可以畫其它的一些,不過,這裡就簡單的畫一些比較簡單的圖形。
現在直接對上面的繪圖程式碼進行簡單的解析吧。
主函式就不用看了,主要是看視窗處理函式中WM_PAINT裡面的。
首先,我們是裝置環境的準備,即定義一個HDC控制代碼,然後再這個控制代碼中呼叫BeginPaint()函式。這個函式有兩個引數,第一個是視窗控制代碼,就是你要在那個視窗獲得裝置描述表的控制代碼,這裡當然就是我們過程處理函式的視窗控制代碼了,第二個引數是一個繪畫結構體的指標,什麼是繪畫結構?其實就是一個結構體PAINTSTRUCT,把它的結構貼出來給你們看下:
typedef struct tagPAINTSTRUCT {
HDC hdc;//裝置環境控制代碼
BOOL fErase;//是否擦除
RECT rcPaint;//無效區域
BOOL fRestore;//系統保留
BOOL fIncUpdate;//系統保留
BYTE rgbReserved[32];//系統保留
} PAINTSTRUCT, *PPAINTSTRUCT, *NPPAINTSTRUCT, *LPPAINTSTRUCT;
這裡我們只關注前面三個引數,第一個是裝置環境的控制代碼,即它擁有著一個獨有的視窗控制代碼,第二個是擦除的區域,這與第三個引數有關,windows程式設計中引入了兩個定義性的詞彙:“有效區域”和“無效區域”。我們現在只需要知道,如果你在應用程式中畫一個圖,而那個圖只佔用了應用程式區域的小部分,那麼,在視窗更新的時候,這個需要繪製的區域我們稱為無效區域,而無需繪畫的區域稱為有效區域。
通過這麼一提,我們知道,我們之前建立的空白視窗其區域均為有效區域,那麼我們在繪圖的時候就需要營造出一塊無效區域出來,而這個這個BeginPaint()函式就是根據視窗來傳入一個PAINTSTRUCT結構來營建這麼一個無效的區域。
所以,在我們繪圖之前,我們需要再定義出一個PAINTSTRUCT結構。然後傳入到BeginPaint()中使用即可。這樣,裝置環境便建立完成。
現在就可以呼叫GDI介面來進行繪畫了,既然是繪畫,當然要有畫筆畫刷什麼的,其實這在我們建立了一個hdc的時候已經給我們提供一個預設值了。比如筆的顏色預設為黑色,畫刷預設為白色等等,這裡我們暫時不必深究,只管使用就行了。
GDI介面我們可以看到如果是矩形就呼叫:Rectangle(),具體的介面就不多說了,註釋也有。主要需要說明的是它的引數。
對於矩形,我們先來看一張圖:
這當中的四個引數對應圖中的:Rectangle(hdc,xLeft,yTop,xRight,yBottom);
如果是點(xLeft,yTop),那麼就是左上角的那個點,如果是點(xRight,yBottom),就是指右下角的那個點。
這樣就應該明白了矩形的建立過程了,指定矩形的左上點和右下點,就可建立一個矩形。
好了,既然明白了矩形的建立,那麼,橢圓也如此,就不多說了。
至於圓角矩形,還有兩個引數,我們再來看一張圖:
後面的那兩個引數就是xCornerEllipse和yCornerEllipse。
如果這兩個值越大,代表圓角越圓,如果當這兩個值達到了矩形的寬度和高度,那麼便會變成橢圓。
然後,來說下那個扇形,在說之前,需要說下的就是關於視窗的座標問題,就二維平面來說:原點(0,0)在視窗的左上點,x軸由左到右自增,y軸由上至下自增。
好,現在再來看下這張圖:
扇形的引數中,在傳入了裝置環境控制代碼之後,後面的四個引數就是和矩形一樣。關鍵的是最後的四個引數,這裡我們可以明顯的看出它分為兩個點:(xStart,yStart),(xEnd,yEnd),然後從圓心連線到這兩個點形成兩條線,其中,開始的那條線通過圓心逆時鐘旋轉到結束那條線為所需要的扇形區域。
最後,來說說這個三角形的建立吧。
其實,三角形之所以能夠形成,是通過畫線來形成的,我們呼叫了MoveToEx來把開始的點移動到作為畫線的起點,然後再通過LIneTo來指定到線的終點。最後,這個點便成了下一次畫線的七點。所以,如果要畫三角形,則只需呼叫三次lineTo即可畫出。
那麼,如果我們覺得這樣麻煩,那麼,我們就用一個點的陣列來儲存這些點,然後通過PolylineTo()來把它們一個個的連線起來。其中在傳入裝置環境控制代碼之後,第二個引數便是這個陣列了,然後指定畫線的條數即可。
最後,在呼叫完介面後記得要釋放裝置環境,不然,繪圖的區域一直無效就不好了~呼叫的介面便是EndPaint()這個函式。
關於GDI的簡單介紹與使用就介紹到這兒。