我們今天繼續塗鴉,實踐證明,塗鴉是人生一大樂趣。
首先,我們寫一個程序骨架子,以便 做實驗。
#include <Windows.h>
LRESULT CALLBACK MainWinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(
HINSTANCE hThisApp,
HINSTANCE hPrevApp,
LPSTR lpsCmdln,
int iShow)
{
WNDCLASS wc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = CreateSolidBrush(RGB(0,0,0));
// 默認光標類型為箭頭
wc.hCursor = LoadCursor(hThisApp, IDC_ARROW);
// 默認應用程序圖標
wc.hIcon = LoadIcon(hThisApp, IDI_APPLICATION);
wc.hInstance = hThisApp;
wc.lpfnWndProc = MainWinProc;
wc.lpszClassName = L"MyAppTest";
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW | CS_VREDRAW;
// 注冊窗口類
RegisterClass(&wc);
// 創建窗口
HWND hwnd = CreateWindow(
L"MyAppTest",
L"繪畫課",
/* 使用 WS_VISIBLE 就不用調用ShowWindow了 */
WS_VISIBLE | WS_OVERLAPPEDWINDOW,
100,
45,
500,
380,
NULL,
NULL,
hThisApp,
NULL);
// 消息循環
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK MainWinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
/*
待實現
*/
EndPaint(hwnd, &ps);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
CreatePen函數
我們要進行素描畫創作,所以我們必須想清楚要使用什麼樣的鋼筆, 畫出什麼樣的線條。故,畫圖之前得創建一支鋼筆,不然,巧婦難為無米之炊。
HPEN
CreatePen(
int iStyle, //鋼筆的樣式,如虛線、實線
int cWidth, //線條寬度
COLORREF color //線條是啥顏色的
);
第一個參數指定線條的樣式,如
/* Pen Styles */ #define PS_SOLID 0 #define PS_DASH 1 /* ------- */ #define PS_DOT 2 /* ....... */ #define PS_DASHDOT 3 /* _._._._ */ #define PS_DASHDOTDOT 4 /* _.._.._ */ #define PS_NULL 5 #define PS_INSIDEFRAME 6 #define PS_USERSTYLE 7 #define PS_ALTERNATE 8
函數成功創建鋼筆後就會返回HPEN,H開頭的你就要知道它 表示句柄。Pen也是系統資源,所以創建筆後系統要為它一個標識。
SelectObject函數
上過美術課,你會知道,有了用於進行素描創作的鋼筆還不能動手干活,我們還需要有紙。接下來,調 用SelectObject函數,把剛才創建的鋼筆與繪圖紙關聯,就等於把我們創建的繪圖資源放進DC工具箱中 ,
HGDIOBJ WINAPI SelectObject( HDC hdc, //設備描述表的句柄 HGDIOBJ h //要放到DC中的資源的句柄 );
調用成功後,返回原先的資源句柄。
MoveToEx和LineTo
MoveToEx是設置繪 制的起點,下次繪圖將從這個點開始。它的最後一個參數將被設置為當前點,即Move後的坐標。LineTo 從當前坐標開始,到指定坐標之間繪制一條線段。
// 創建鋼筆 HPEN pen = CreatePen(PS_DASH, 1, RGB(0,255,50)); // 把筆選到DC中 SelectObject(ps.hdc, pen); // 設定線段的起點 MoveToEx(ps.hdc, 15, 25, NULL); // 繪制線條 LineTo(ps.hdc, 65, 49); LineTo(ps.hdc, 12, 120); LineTo(ps.hdc, 250, 78); LineTo(ps.hdc, 312, 185); DeleteObject(pen);
記住,只要是我們創建的句柄,用完後調用DeleteObject函數將其銷毀 。

PolyBezier函數和PolyBezierTo函數
兩個函數都是用來繪制貝塞爾曲線的,不同的是,PolyBezier函數包含指定的起點和終點, PolyBezierTo是從當前點開始繪制貝塞爾曲線。
// 繪制貝塞爾曲線 pen = CreatePen(PS_DOT, 1, RGB(0,3,255)); SelectObject(ps.hdc, pen); POINT* pts = new POINT[4]; pts[0].x = 421; pts[0].y = 16; pts[1].x = 7; pts[1].y = 197; pts[2].x = 480; pts[2].y = 320; pts[3].x = 30; pts[3].y = 350; PolyBezier(ps.hdc, pts, 4); delete [] pts; // 第二段貝塞爾曲線 POINT* pts2 = new POINT[3]; pts2[0].x = 176; pts2[0].y = 84; pts2[1].x = 17; pts2[1].y = 247; pts2[2].x = 400; pts2[2].y = 490; // 移動當前點 MoveToEx(ps.hdc, 395, 270, NULL); PolyBezierTo(ps.hdc, pts2, 3); delete [] pts2;

PolyPolyline繪制復合線條
PolyPolyline函數可以繪制多段復合線條。它的聲明如下:
BOOL PolyPolyline( HDC hdc, const POINT *lppt, const DWORD *lpdwPolyPoints, DWORD cCount );
lppt參數指向一個POINT的數組,它包含繪制復合線條所需的所有點;lpdwPolyPoints指向 一個數組,這個數字數組指明如何分配點數組。
例如,lppt有7個點,我計劃,前2個點繪制第 一條線,接著3個點繪制第二條線,最後2個點繪制第三條線,這樣一來,lpdwPolyPolyPoints得值應為 :
{ 2, 3, 2 }
nCount裡包含lpdwPolyPolyPoints中的數量,我們上面的例子是 3.
由於兩點決定一條線段,因此,lpdwPolyPolyPoints裡面的值記得要>=2。
//
復雜圖形
pen = CreatePen(PS_DASHDOTDOT, 1, RGB(80,20,160));
SelectObject(ps.hdc, pen);
POINT plpts[10] =
{
{47,3}, {11,46}, {28,199}, {203,305}, {94,22},
{402,377}, {21,45}, {237,7}, {300,398}, {175,25}
};
DWORD arr[4] = { 2, 3, 3, 2};
PolyPolyline(ps.hdc, &plpts[0], &arr[0], 4);
上面的代碼將畫出以下圖形。

完整的代碼清單如下:
#include <Windows.h>
LRESULT CALLBACK MainWinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(
HINSTANCE hThisApp,
HINSTANCE hPrevApp,
LPSTR lpsCmdln,
int iShow)
{
WNDCLASS wc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = CreateSolidBrush(RGB(0,0,0));
// 默認光標類型為箭頭
wc.hCursor = LoadCursor(hThisApp, IDC_ARROW);
// 默認應用程序圖標
wc.hIcon = LoadIcon(hThisApp, IDI_APPLICATION);
wc.hInstance = hThisApp;
wc.lpfnWndProc = MainWinProc;
wc.lpszClassName = L"MyAppTest";
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW | CS_VREDRAW;
// 注冊窗口類
RegisterClass(&wc);
// 創建窗口
HWND hwnd = CreateWindow(
L"MyAppTest",
L"繪畫課",
/* 使用 WS_VISIBLE 就不用調用ShowWindow了 */
WS_VISIBLE | WS_OVERLAPPEDWINDOW,
100,
45,
500,
380,
NULL,
NULL,
hThisApp,
NULL);
// 消息循環
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK MainWinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
// 創建鋼筆
HPEN pen = CreatePen(PS_DASH, 1, RGB(0,255,50));
// 把筆選到DC中
SelectObject(ps.hdc, pen);
// 設定線段的起點
MoveToEx(ps.hdc, 15, 25, NULL);
// 繪制線條
LineTo(ps.hdc, 65, 49);
LineTo(ps.hdc, 12, 120);
LineTo(ps.hdc, 250, 78);
LineTo(ps.hdc, 312, 185);
// 繪制貝塞爾曲線
pen = CreatePen(PS_DOT, 1, RGB(0,3,255));
SelectObject(ps.hdc, pen);
POINT* pts = new POINT[4];
pts[0].x = 421;
pts[0].y = 16;
pts[1].x = 7;
pts[1].y = 197;
pts[2].x = 480;
pts[2].y = 320;
pts[3].x = 30;
pts[3].y = 350;
PolyBezier(ps.hdc, pts, 4);
delete [] pts;
// 第二段貝塞爾曲線
POINT* pts2 = new POINT[3];
pts2[0].x = 176;
pts2[0].y = 84;
pts2[1].x = 17;
pts2[1].y = 247;
pts2[2].x = 400;
pts2[2].y = 490;
// 移動當前點
MoveToEx(ps.hdc, 395, 270, NULL);
PolyBezierTo(ps.hdc, pts2, 3);
delete [] pts2;
// 復雜圖形
pen = CreatePen(PS_DASHDOTDOT, 1, RGB(80,20,160));
SelectObject(ps.hdc, pen);
POINT plpts[10] =
{
{47,3}, {11,46}, {28,199}, {203,305}, {94,22},
{402,377}, {21,45}, {237,7}, {300,398}, {175,25}
};
DWORD arr[4] = { 2, 3, 3, 2};
PolyPolyline(ps.hdc, &plpts[0], &arr[0], 4);
DeleteObject(pen);
EndPaint(hwnd, &ps);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}