目录结构
windows
1.windows hello world。
2.windows程序消息队列机制。
GDI
1.画一个点。
2.设备环境DC。
3.画线、矩形、圆角矩形、椭圆。
4.画笔。
5.画刷。
6.GDI对象还原。
GDI+
1.使用GDI+。
2.GDI和GDI+绘制对比。
3.GDI+渐变填充、位图填充。
内存DC
微软Windows子系统负责在称为图形设备接口(Graphics Device Interface, GDI)的视频显示器和打印机上显示图形。 ——《Windows程序设计(第五版)》
GDI+是早期Windows版本中包括的图形设备接口GDI的继任者。GDI+将GDI的很多功能进行了优化,而且提供了新的附加功能。
——《精通GDI+编程》
1.windows程序运行hello world。只需要在vs下新建一个空项目,输入以下代码,即可出现hello world。
1 #include <Windows.h>
2
3 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
4
5 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
6 PSTR szCmdLine, int iCmdShow)
7 {
8
9 TCHAR szAppName[] = TEXT("Hello");
10 WNDCLASS wndclass;
11 wndclass.style = CS_HREDRAW | CS_VREDRAW;
12 wndclass.lpfnWndProc = WndProc;
13 wndclass.cbClsExtra = 0;
14 wndclass.cbWndExtra = 0;
15 wndclass.hInstance = hInstance;
16 wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
17 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
18 wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
19 wndclass.lpszMenuName = NULL;
20 wndclass.lpszClassName = szAppName;
21
22 if (!RegisterClass(&wndclass))
23 {
24 MessageBox(NULL, TEXT("error"), szAppName, MB_ICONERROR);
25 return 0;
26 }
27
28 HWND hwnd = CreateWindow(szAppName,
29 TEXT("Hello"),
30 WS_OVERLAPPEDWINDOW,
31 CW_USEDEFAULT, CW_USEDEFAULT,
32 CW_USEDEFAULT, CW_USEDEFAULT,
33 NULL,
34 NULL,
35 hInstance,
36 NULL);
37
38 ShowWindow(hwnd, iCmdShow);
39 UpdateWindow(hwnd);
40
41 MSG msg;
42 while (GetMessage(&msg, NULL, 0, 0))
43 {
44 TranslateMessage(&msg);
45 DispatchMessage(&msg);
46 }
47 return msg.wParam;
48 }
49
50 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
51 {
52 HDC hdc;
53 PAINTSTRUCT ps;
54 TCHAR text[30];
55 switch (msg)
56 {
57 case WM_CREATE:
58 return 0;
59 case WM_PAINT:
60 hdc = BeginPaint(hwnd, &ps);
61 TextOut(hdc, 100, 100, text, wsprintf(text, TEXT("HELLO WORLD")));
62 EndPaint(hwnd, &ps);
63 return 0;
64 case WM_DESTROY:
65 PostQuitMessage(0);
66 return 0;
67 }
68 return DefWindowProc(hwnd, msg, wParam, lParam);
69 }
2.windows程序的运行本质是消息队列机制。
1 MSG msg;
2 while (GetMessage(&msg, NULL, 0, 0))
3 {
4 TranslateMessage(&msg);
5 DispatchMessage(&msg);
6 }
windows程序不停地检测消息队列中是否有新的消息,
while (GetMessage(&msg, NULL, 0, 0)),若消息队列中没有消息,则程序会阻塞;
若消息队列中有消息:
对于所有非WM_QUIT消息,GetMessage函数都将返回非零值,
而对WM_QUIT消息,GetMessage将返回0,则退出消息循环。
在此模型中,其实有两个队列。
一个是操作系统队列,事件发生时(比如按键盘的某个键),在操作系统的队列会插入一个相应的事件消息。
接着操作系统队列会把此消息传递给windows程序的消息队列。
GDI
1.首先我们往窗口上画一个点。
1 #include <Windows.h>
2
3 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
4
5 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
6 PSTR szCmdLine, int iCmdShow)
7 {
8
9 TCHAR szAppName[] = TEXT("Hello");
10 WNDCLASS wndclass;
11 wndclass.style = CS_HREDRAW | CS_VREDRAW;
12 wndclass.lpfnWndProc = WndProc;
13 wndclass.cbClsExtra = 0;
14 wndclass.cbWndExtra = 0;
15 wndclass.hInstance = hInstance;
16 wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
17 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
18 wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
19 wndclass.lpszMenuName = NULL;
20 wndclass.lpszClassName = szAppName;
21
22 if (!RegisterClass(&wndclass))
23 {
24 MessageBox(NULL, TEXT("error"), szAppName, MB_ICONERROR);
25 return 0;
26 }
27
28 HWND hwnd = CreateWindow(szAppName,
29 TEXT("Hello"),
30 WS_OVERLAPPEDWINDOW,
31 CW_USEDEFAULT, CW_USEDEFAULT,
32 CW_USEDEFAULT, CW_USEDEFAULT,
33 NULL,
34 NULL,
35 hInstance,
36 NULL);
37
38 ShowWindow(hwnd, iCmdShow);
39 UpdateWindow(hwnd);
40
41 MSG msg;
42 while (GetMessage(&msg, NULL, 0, 0))
43 {
44 TranslateMessage(&msg);
45 DispatchMessage(&msg);
46 }
47 return msg.wParam;
48 }
49
50 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
51 {
52 HDC hdc;
53 PAINTSTRUCT ps;
54 switch (msg)
55 {
56 case WM_CREATE:
57 return 0;
58 case WM_PAINT:
59 hdc = BeginPaint(hwnd, &ps);
60 SetPixel(hdc, 400, 400, 0x000000ff);
61 EndPaint(hwnd, &ps);
62 return 0;
63 case WM_DESTROY:
64 PostQuitMessage(0);
65 return 0;
66 }
67 return DefWindowProc(hwnd, msg, wParam, lParam);
68 }
核心GDI函数是SetPixel,函数的参数列表依次是设备环境句柄,x,y坐标和颜色。
这里颜色是一个DWORD的数值。(0x00bbggrr,从右往左依次为rgb值,如红色是0x000000ff)
1 COLORREF SetPixel(
2 HDC hdc,
3 int X,
4 int Y,
5 COLORREF crColor
6 );
2.设备环境DC
SetPixel写在WM_PAINT消息的处理中,WM_PAINT处理处有成对出现的
hdc = BeginPaint(hwnd, &ps);
// other code
EndPaint(hwnd, &ps);
这便是使用GDI函数时必经的一个步骤,获取设备环境句柄(HDC),可见GDI函数的参数列表中都有一个HDC。
除了在WM_PAINT中的BeginPaint和EndPaint,在非WM_PAINT消息中获取DC的常用方式还有:
hdc = GetDC(hwnd);
// other code
ReleaseDC(hwnd, hdc);
比如点击鼠标的时候画一个红点,此操作不在WM_PAINT消息中处理,在WM_LBUTTONDOWN消息中处理:
1 #include <Windows.h> 2 3 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); 4 5 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 6 PSTR szCmdLine, int iCmdShow) 7 { 8 9 TCHAR szAppName[] = TEXT("Hello"); 10 WNDCLASS wndclass; 11 wndclass.style = CS_HREDRAW | CS_VREDRAW; 12 wndclass.lpfnWndProc = WndProc; 13 wndclass.cbClsExtra = 0; 14 wndclass.cbWndExtra = 0; 15 wndclass.hInstance = hInstance; 16 wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); 17 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); 18 wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); 19 wndclass.lpszMenuName = NULL; 20 wndclass.lpszClassName = szAppName; 21 22 if (!RegisterClass(&wndclass)) 23 { 24 MessageBox(NULL, TEXT("error"), szAppName, MB_ICONERROR); 25 return 0; 26 } 27 28 HWND hwnd = CreateWindow(szAppName, 29 TEXT("Hello"), 30 WS_OVERLAPPEDWINDOW, 31 CW_USEDEFAULT, CW_USEDEFAULT, 32 CW_USEDEFAULT, CW_USEDEFAULT, 33 NULL, 34 NULL, 35 hInstance, 36 NULL); 37 38 ShowWindow(hwnd, iCmdShow); 39 UpdateWindow(hwnd); 40 41 MSG msg; 42 while (GetMessage(&msg, NULL, 0, 0)) 43 { 44 TranslateMessage(&msg); 45 DispatchMessage(&msg); 46 } 47 return msg.wParam; 48 } 49 50 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 51 { 52 HDC hdc; 53 PAINTSTRUCT ps; 54 int x = 0, y = 0; 55 switch (msg) 56 { 57 case WM_CREATE: 58 return 0; 59 case WM_PAINT: 60 hdc = BeginPaint(hwnd, &ps); 61 // SetPixel(hdc, 400, 400, 0x000000ff); 62 EndPaint(hwnd, &ps); 63 return 0; 64 case WM_LBUTTONDOWN: 65 hdc = GetDC(hwnd); 66 x = LOWORD(lParam); 67 y = HIWORD(lParam); 68 SetPixel(hdc, x, y, 0x000000ff); 69 ReleaseDC(hwnd, hdc); 70 return 0; 71 case WM_DESTROY: 72 PostQuitMessage(0); 73 return 0; 74 } 75 return DefWindowProc(hwnd, msg, wParam, lParam); 76 }
有了以上的基础,接着可以来看下一些画线的GDI函数:
3.画线、矩形、圆角矩形、椭圆。
画直线MoveToEx,LineTo:
1 #include <Windows.h> 2 3 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); 4 5 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 6 PSTR szCmdLine, int iCmdShow) 7 { 8 9 TCHAR szAppName[] = TEXT("Hello"); 10 WNDCLASS wndclass; 11 wndclass.style = CS_HREDRAW | CS_VREDRAW; 12 wndclass.lpfnWndProc = WndProc; 13 wndclass.cbClsExtra = 0; 14 wndclass.cbWndExtra = 0; 15 wndclass.hInstance = hInstance; 16 wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); 17 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); 18 wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); 19 wndclass.lpszMenuName = NULL; 20 wndclass.lpszClassName = szAppName; 21 22 if (!RegisterClass(&wndclass)) 23 { 24 MessageBox(NULL, TEXT("error"), szAppName, MB_ICONERROR); 25 return 0; 26 } 27 28 HWND hwnd = CreateWindow(szAppName, 29 TEXT("Hello"), 30 WS_OVERLAPPEDWINDOW, 31 CW_USEDEFAULT, CW_USEDEFAULT, 32 CW_USEDEFAULT, CW_USEDEFAULT, 33 NULL, 34 NULL, 35 hInstance, 36 NULL); 37 38 ShowWindow(hwnd, iCmdShow); 39 UpdateWindow(hwnd); 40 41 MSG msg; 42 while (GetMessage(&msg, NULL, 0, 0)) 43 { 44 TranslateMessage(&msg); 45 DispatchMessage(&msg); 46 } 47 return msg.wParam; 48 } 49 50 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 51 { 52 HDC hdc; 53 PAINTSTRUCT ps; 54 static int cxClient = 0, cyClient = 0; 55 switch (msg) 56 { 57 case WM_CREATE: 58 return 0; 59 case WM_SIZE: 60 cxClient = LOWORD(lParam); 61 cyClient = HIWORD(lParam); 62 return 0; 63 case WM_PAINT: 64 hdc = BeginPaint(hwnd, &ps); 65 MoveToEx(hdc, 0, cyClient / 2, NULL); 66 LineTo(hdc, cxClient, cyClient / 2); 67 EndPaint(hwnd, &ps); 68 return 0; 69 case WM_LBUTTONDOWN: 70 return 0; 71 case WM_DESTROY: 72 PostQuitMessage(0); 73 return 0; 74 } 75 return DefWindowProc(hwnd, msg, wParam, lParam); 76 }
画一个由多个点的数组组成的折线PolyLine:
1 #include <Windows.h> 2 #include <math.h> 3 4 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); 5 6 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 7 PSTR szCmdLine, int iCmdShow) 8 { 9 10 TCHAR szAppName[] = TEXT("Hello"); 11 WNDCLASS wndclass; 12 wndclass.style = CS_HREDRAW | CS_VREDRAW; 13 wndclass.lpfnWndProc = WndProc; 14 wndclass.cbClsExtra = 0; 15 wndclass.cbWndExtra = 0; 16 wndclass.hInstance = hInstance; 17 wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); 18 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); 19 wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); 20 wndclass.lpszMenuName = NULL; 21 wndclass.lpszClassName = szAppName; 22 23 if (!RegisterClass(&wndclass)) 24 { 25 MessageBox(NULL, TEXT("error"), szAppName, MB_ICONERROR); 26 return 0; 27 } 28 29 HWND hwnd = CreateWindow(szAppName, 30 TEXT("Hello"), 31 WS_OVERLAPPEDWINDOW, 32 CW_USEDEFAULT, CW_USEDEFAULT, 33 CW_USEDEFAULT, CW_USEDEFAULT, 34 NULL, 35 NULL, 36 hInstance, 37 NULL); 38 39 ShowWindow(hwnd, iCmdShow); 40 UpdateWindow(hwnd); 41 42 MSG msg; 43 while (GetMessage(&msg, NULL, 0, 0)) 44 { 45 TranslateMessage(&msg); 46 DispatchMessage(&msg); 47 } 48 return msg.wParam; 49 } 50 51 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 52 { 53 HDC hdc; 54 PAINTSTRUCT ps; 55 static int cxClient = 0, cyClient = 0; 56 const static int NUM = 1000; 57 const static float TWOPI = 2 * 3.1415926; 58 POINT apt[NUM]; 59 switch (msg) 60 { 61 case WM_CREATE: 62 return 0; 63 case WM_SIZE: 64 cxClient = LOWORD(lParam); 65 cyClient = HIWORD(lParam); 66 return 0; 67 case WM_PAINT: 68 hdc = BeginPaint(hwnd, &ps); 69 MoveToEx(hdc, 0, cyClient / 2, NULL); 70 LineTo(hdc, cxClient, cyClient / 2); 71 72 for (int i = 0; i < NUM; ++i) 73 { 74 apt[i].x = i * cxClient / NUM; 75 apt[i].y = (int)(cyClient / 2 * (1 - sin(TWOPI * i / NUM))); 76 } 77 78 Polyline(hdc, apt, NUM); 79 EndPaint(hwnd, &ps); 80 return 0; 81 case WM_LBUTTONDOWN: 82 return 0; 83 case WM_DESTROY: 84 PostQuitMessage(0); 85 return 0; 86 } 87 return DefWindowProc(hwnd, msg, wParam, lParam); 88 }
画一个矩形Rectangle,圆角矩形RoundRect,椭圆Ellipse:
1 #include <Windows.h> 2 3 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); 4 5 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 6 PSTR szCmdLine, int iCmdShow) 7 { 8 9 TCHAR szAppName[] = TEXT("Hello"); 10 WNDCLASS wndclass; 11 wndclass.style = CS_HREDRAW | CS_VREDRAW; 12 wndclass.lpfnWndProc = WndProc; 13 wndclass.cbClsExtra = 0; 14 wndclass.cbWndExtra = 0; 15 wndclass.hInstance = hInstance; 16 wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); 17 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); 18 wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); 19 wndclass.lpszMenuName = NULL; 20 wndclass.lpszClassName = szAppName; 21 22 if (!RegisterClass(&wndclass)) 23 { 24 MessageBox(NULL, TEXT("error"), szAppName, MB_ICONERROR); 25 return 0; 26 } 27 28 HWND hwnd = CreateWindow(szAppName, 29 TEXT("Hello"), 30 WS_OVERLAPPEDWINDOW, 31 CW_USEDEFAULT, CW_USEDEFAULT, 32 CW_USEDEFAULT, CW_USEDEFAULT, 33 NULL, 34 NULL, 35 hInstance, 36 NULL); 37 38 ShowWindow(hwnd, iCmdShow); 39 UpdateWindow(hwnd); 40 41 MSG msg; 42 while (GetMessage(&msg, NULL, 0, 0)) 43 { 44 TranslateMessage(&msg); 45 DispatchMessage(&msg); 46 } 47 return msg.wParam; 48 } 49 50 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 51 { 52 HDC hdc; 53 PAINTSTRUCT ps; 54 static int cxClient = 0, cyClient = 0; 55 switch (msg) 56 { 57 case WM_CREATE: 58 return 0; 59 case WM_SIZE: 60 cxClient = LOWORD(lParam); 61 cyClient = HIWORD(lParam); 62 return 0; 63 case WM_PAINT: 64 hdc = BeginPaint(hwnd, &ps); 65 66 Rectangle(hdc, cxClient / 8, cyClient / 8, 7 * cxClient / 8, 7 * cyClient / 8); 67 Ellipse(hdc, cxClient / 8, cyClient / 8, 7 * cxClient / 8, 7 * cyClient / 8); 68 RoundRect(hdc, cxClient / 4, cyClient / 4, 3 * cxClient / 4, 3 * cyClient / 4, cxClient / 4, cyClient / 4); 69 70 EndPaint(hwnd, &ps); 71 return 0; 72 case WM_LBUTTONDOWN: 73 return 0; 74 case WM_DESTROY: 75 PostQuitMessage(0); 76 return 0; 77 } 78 return DefWindowProc(hwnd, msg, wParam, lParam); 79 }
还有其余的用法都是类似,有需要去查msdn就行。
4.画笔
前面画线使用的都是默认的画笔。Windows提供了三种备用画笔:WHITE_PEN,BLACK_PEN,NULL_PEN。
如果想要其他类型的画笔,则需要自己创建,使用CreatePen或CreatePenIndirect。
还是前面的画矩形、画圆角矩形、画椭圆的例子。这次用绿色的虚线来画。
1 #include <Windows.h> 2 3 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); 4 5 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 6 PSTR szCmdLine, int iCmdShow) 7 { 8 9 TCHAR szAppName[] = TEXT("Hello"); 10 WNDCLASS wndclass; 11 wndclass.style = CS_HREDRAW | CS_VREDRAW; 12 wndclass.lpfnWndProc = WndProc; 13 wndclass.cbClsExtra = 0; 14 wndclass.cbWndExtra = 0; 15 wndclass.hInstance = hInstance; 16 wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); 17 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); 18 wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); 19 wndclass.lpszMenuName = NULL; 20 wndclass.lpszClassName = szAppName; 21 22 if (!RegisterClass(&wndclass)) 23 { 24 MessageBox(NULL, TEXT("error"), szAppName, MB_ICONERROR); 25 return 0; 26 } 27 28 HWND hwnd = CreateWindow(szAppName, 29 TEXT("Hello"), 30 WS_OVERLAPPEDWINDOW, 31 CW_USEDEFAULT, CW_USEDEFAULT, 32 CW_USEDEFAULT, CW_USEDEFAULT, 33 NULL, 34 NULL, 35 hInstance, 36 NULL); 37 38 ShowWindow(hwnd, iCmdShow); 39 UpdateWindow(hwnd); 40 41 MSG msg; 42 while (GetMessage(&msg, NULL, 0, 0)) 43 { 44 TranslateMessage(&msg); 45 DispatchMessage(&msg); 46 } 47 return msg.wParam; 48 } 49 50 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 51 { 52 HDC hdc; 53 PAINTSTRUCT ps; 54 static int cxClient = 0, cyClient = 0; 55 static HPEN hPen; 56 switch (msg) 57 { 58 case WM_CREATE: 59 return 0; 60 case WM_SIZE: 61 cxClient = LOWORD(lParam); 62 cyClient = HIWORD(lParam); 63 return 0; 64 case WM_PAINT: 65 hdc = BeginPaint(hwnd, &ps); 66 hPen = CreatePen(PS_DASH, 1, 0x0000ff00); 67 SelectObject(hdc, hPen); 68 Rectangle(hdc, cxClient / 8, cyClient / 8, 7 * cxClient / 8, 7 * cyClient / 8); 69 Ellipse(hdc, cxClient / 8, cyClient / 8, 7 * cxClient / 8, 7 * cyClient / 8); 70 RoundRect(hdc, cxClient / 4, cyClient / 4, 3 * cxClient / 4, 3 * cyClient / 4, cxClient / 4, cyClient / 4); 71 DeleteObject(hPen); 72 EndPaint(hwnd, &ps); 73 return 0; 74 case WM_LBUTTONDOWN: 75 return 0; 76 case WM_DESTROY: 77 PostQuitMessage(0); 78 return 0; 79 } 80 return DefWindowProc(hwnd, msg, wParam, lParam); 81 }
创建了画笔要记得删除画笔。
hPen = CreatePen(PS_DASH, 1, 0x0000ff00);
SelectObject(hdc, hPen);
// other code
DeleteObject(hPen);
5.画刷。
前面类似圆角矩形的区域,用到的画刷都是默认的画刷。Windows提供了6种备用画刷。
如果想要给一片区域填充某一种颜色,比如一片绿色的圆角矩形区域。此时用到画刷。
1 #include <Windows.h> 2 3 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); 4 5 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 6 PSTR szCmdLine, int iCmdShow) 7 { 8 9 TCHAR szAppName[] = TEXT("Hello"); 10 WNDCLASS wndclass; 11 wndclass.style = CS_HREDRAW | CS_VREDRAW; 12 wndclass.lpfnWndProc = WndProc; 13 wndclass.cbClsExtra = 0; 14 wndclass.cbWndExtra = 0; 15 wndclass.hInstance = hInstance; 16 wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); 17 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); 18 wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); 19 wndclass.lpszMenuName = NULL; 20 wndclass.lpszClassName = szAppName; 21 22 if (!RegisterClass(&wndclass)) 23 { 24 MessageBox(NULL, TEXT("error"), szAppName, MB_ICONERROR); 25 return 0; 26 } 27 28 HWND hwnd = CreateWindow(szAppName, 29 TEXT("Hello"), 30 WS_OVERLAPPEDWINDOW, 31 CW_USEDEFAULT, CW_USEDEFAULT, 32 CW_USEDEFAULT, CW_USEDEFAULT, 33 NULL, 34 NULL, 35 hInstance, 36 NULL); 37 38 ShowWindow(hwnd, iCmdShow); 39 UpdateWindow(hwnd); 40 41 MSG msg; 42 while (GetMessage(&msg, NULL, 0, 0)) 43 { 44 TranslateMessage(&msg); 45 DispatchMessage(&msg); 46 } 47 return msg.wParam; 48 } 49 50 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 51 { 52 HDC hdc; 53 PAINTSTRUCT ps; 54 static int cxClient = 0, cyClient = 0; 55 static HBRUSH hBrush; 56 switch (msg) 57 { 58 case WM_CREATE: 59 return 0; 60 case WM_SIZE: 61 cxClient = LOWORD(lParam); 62 cyClient = HIWORD(lParam); 63 return 0; 64 case WM_PAINT: 65 hdc = BeginPaint(hwnd, &ps); 66 hBrush = CreateSolidBrush(0x0000ff00); 67 SelectObject(hdc, hBrush); 68 RoundRect(hdc, cxClient / 4, cyClient / 4, 3 * cxClient / 4, 3 * cyClient / 4, cxClient / 4, cyClient / 4); 69 DeleteObject(hBrush); 70 EndPaint(hwnd, &ps); 71 return 0; 72 case WM_LBUTTONDOWN: 73 return 0; 74 case WM_DESTROY: 75 PostQuitMessage(0); 76 return 0; 77 } 78 return DefWindowProc(hwnd, msg, wParam, lParam); 79 }
注意到也使用了SelectObject这个函数,此时可以把GDI对象关联到设备。
一个程序可以创建6个GDI对象,分别是画笔,画刷,位图,区域,字体和调色板。
6.还原GDI对象。
当使用了一种新的GDI对象后,希望还原原有的GDI环境,应该如何做呢?
SelectObject返回值是旧的GDI对象,因此可以设置新GDI对象的时候将旧的GDI对象记录下来。
HPen hOldPen = (HPen)SelectObject(hdc, hPen);
最后删除新GDI环境之前,再把旧的GDI对象选入环境。
SelectObject(hdc, hOldPen);
GDI+
GDI+用一个“无状态模型”取代了GDI种把选中项目放到设备环境(DC)对象上的“状态模型”,
在GDI+种的每一个绘图操作都是相互独立的。图形对象(Graphics object)是绘图操作种唯一保留的对象。
1.使用GDI+的方法。
1)头文件引入#include <gdiplus.h>。
2)项目附加依赖项gdiplus.lib。
3)在程序开始时初始化GDI+环境。
ULONG_PTR uToken = 0;
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&uToken, &gdiplusStartupInput, NULL);
4)在程序结束时释放GDI+环境。
GdiplusShutdown(uToken);
2.GDI和GDI+的绘制对比。
有了以上的准备,直接来看画一条线的方法:
1 #include <Windows.h> 2 #include <gdiplus.h> 3 4 using namespace Gdiplus; 5 6 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); 7 8 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 9 PSTR szCmdLine, int iCmdShow) 10 { 11 12 ULONG_PTR uToken = 0; 13 GdiplusStartupInput gdiplusStartupInput; 14 GdiplusStartup(&uToken, &gdiplusStartupInput, NULL); 15 16 TCHAR szAppName[] = TEXT("Hello"); 17 WNDCLASS wndclass; 18 wndclass.style = CS_HREDRAW | CS_VREDRAW; 19 wndclass.lpfnWndProc = WndProc; 20 wndclass.cbClsExtra = 0; 21 wndclass.cbWndExtra = 0; 22 wndclass.hInstance = hInstance; 23 wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); 24 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); 25 wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); 26 wndclass.lpszMenuName = NULL; 27 wndclass.lpszClassName = szAppName; 28 29 if (!RegisterClass(&wndclass)) 30 { 31 MessageBox(NULL, TEXT("error"), szAppName, MB_ICONERROR); 32 return 0; 33 } 34 35 HWND hwnd = CreateWindow(szAppName, 36 TEXT("Hello"), 37 WS_OVERLAPPEDWINDOW, 38 CW_USEDEFAULT, CW_USEDEFAULT, 39 CW_USEDEFAULT, CW_USEDEFAULT, 40 NULL, 41 NULL, 42 hInstance, 43 NULL); 44 45 ShowWindow(hwnd, iCmdShow); 46 UpdateWindow(hwnd); 47 48 MSG msg; 49 while (GetMessage(&msg, NULL, 0, 0)) 50 { 51 TranslateMessage(&msg); 52 DispatchMessage(&msg); 53 } 54 55 GdiplusShutdown(uToken); 56 return msg.wParam; 57 } 58 59 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 60 { 61 HDC hdc; 62 PAINTSTRUCT ps; 63 static int cxClient = 0, cyClient = 0; 64 65 switch (msg) 66 { 67 case WM_CREATE: 68 return 0; 69 case WM_SIZE: 70 cxClient = LOWORD(lParam); 71 cyClient = HIWORD(lParam); 72 return 0; 73 case WM_PAINT: 74 { 75 hdc = BeginPaint(hwnd, &ps); 76 Graphics graphics(hdc); 77 Pen greenPen(Color(255, 0, 255, 0)); 78 graphics.DrawLine(&greenPen, 0, cyClient / 2, cxClient, cyClient / 2); 79 80 EndPaint(hwnd, &ps); 81 } 82 return 0; 83 case WM_LBUTTONDOWN: 84 return 0; 85 case WM_DESTROY: 86 PostQuitMessage(0); 87 return 0; 88 } 89 90 91 return DefWindowProc(hwnd, msg, wParam, lParam); 92 }
Graphics graphics(hdc);
Pen greenPen(Color(255, 0, 255, 0));
graphics.DrawLine(&greenPen, 0, cyClient / 2, cxClient, cyClient / 2);
无需再像GDI一样,将画笔选入设备环境,而是直接用Graphics对象的对应函数,画笔也是函数的一个参数。
为了让代码更清晰,把画线、画矩形单独写成一个函数,传入一个hdc。
以下是使用GDI绘制和使用GDI+绘制的各种对比。
1 void paintLine(HDC hdc) 2 { 3 // GDI 4 HPEN hPen = CreatePen(PS_SOLID, 1, 0x0000ff00); 5 HPEN hOldPen = (HPEN)SelectObject(hdc, hPen); 6 MoveToEx(hdc, 0, cyClient / 2, NULL); 7 LineTo(hdc, cxClient, cyClient / 2); 8 DeleteObject(hPen); 9 SelectObject(hdc, hOldPen); 10 11 // GDI+ 12 Graphics graphics(hdc); 13 Pen greenPen(Color(255, 0, 255, 0)); 14 graphics.DrawLine(&greenPen, 0, cyClient / 2, cxClient, cyClient / 2); 15 }
1 void paintRec(HDC hdc) 2 { 3 // GDI 4 HBRUSH hBrush = CreateSolidBrush(0x0000ff00); 5 HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, hBrush); 6 7 Rectangle(hdc, cxClient / 8, cyClient / 8, 7 * cxClient / 8, 7 * cyClient / 8); 8 9 DeleteObject(hBrush); 10 SelectObject(hdc, hOldBrush); 11 12 // GDI+ 13 Graphics graphics(hdc); 14 Pen blackPen(Color(255, 0, 0, 0)); 15 SolidBrush greenBrush(Color(255, 0, 255, 0)); 16 graphics.DrawRectangle(&blackPen, Rect(cxClient / 8, cyClient / 8, 6 * cxClient / 8, 6 * cyClient / 8)); 17 graphics.FillRectangle(&greenBrush, Rect(cxClient / 8 + 1, cyClient / 8 + 1, 6 * cxClient / 8 - 1, 6 * cyClient / 8 - 1)); 18 }
GDI+绘制矩形的后两个参数是高度、宽度和GDI的决定坐标有区别。
另外GDI+填充用的是Fill系列的函数,想要达到和GDI一样的效果需要考虑边框的一个像素。
3.GDI+的渐变画刷。
GDI+实现了一个渐变画刷,这在GDI是没有的。
1 Graphics graphics(hdc); 2 LinearGradientBrush linearBrush(Point(cxClient / 8, cyClient / 8), Point(7 * cxClient / 8, 7 * cyClient / 8), Color(255, 0, 0, 255), Color(255, 255, 0, 0)); 3 graphics.FillRectangle(&linearBrush, Rect(cxClient / 8 + 1, cyClient / 8 + 1, 6 * cxClient / 8 - 1, 6 * cyClient / 8 - 1));
内存DC
当需要绘制的图形、位图过多时,屏幕可能出现闪烁问题。
原因是WM_PAINT消息中每次绘制都是直接往目标DC绘制,有大量操作。
可以首先创建一个内存DC,一个位图选入此内存DC,所需绘制的内容先在新建的内存DC中绘制,再最后一次拷贝到目标DC。
此操作可称为双缓冲。
1 HDC hMemDc = CreateCompatibleDC(hdc); 2 HBITMAP hbitmap = CreateCompatibleBitmap(hdc, cxClient, cyClient); 3 HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDc, hbitmap); 4 5 // paint code 6 paintLine(hMemDc); 7 paintRec(hMemDc); 8 paintEllipse(hMemDc); 9 paintRoundRec(hMemDc); 10 11 BitBlt(hdc, 0, 0, cxClient, cyClient, hMemDc, 0, 0, SRCCOPY); 12 SelectObject(hMemDc, hOldBitmap); 13 DeleteObject(hbitmap); 14 DeleteDC(hMemDc);