首页 > Windows开发 > 详细

windows GDI和GDI+对比小结

时间:2020-07-02 16:51:25      阅读:47      评论:0      收藏:0      [点我收藏+]
目录结构
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 }
windows hello world

2.windows程序的运行本质是消息队列机制。

技术分享图片
1 MSG msg;
2     while (GetMessage(&msg, NULL, 0, 0))
3     {
4         TranslateMessage(&msg);
5         DispatchMessage(&msg);
6     }
windows程序消息队列

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 );
SetPixel原型

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 }
GDI+画线

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 }
GDI和GDI+画线区别
技术分享图片
 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+绘制矩形的后两个参数是高度、宽度和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);
双缓冲、内存DC解决闪烁

 

windows GDI和GDI+对比小结

原文:https://www.cnblogs.com/yao2yaoblog/p/13208639.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!