• windows GDI和GDI+对比小结


    目录结构
    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解决闪烁
  • 相关阅读:
    DDD中的聚合和UML中的聚合以及组合的关系
    服务端高并发分布式架构演进之路
    互联网架构的演变,那些神奇的东西怎么来的?
    WPF 之 创建继承自Window 基类的自定义窗口基类
    IIS 之 在IIS7、IIS7.5中应用程序池最优配置方案
    性能测试工具 之 性能计数器
    IIS 之 线程池最大线程数
    WebService 之 已超过传入消息(65536)的最大消息大小配额。若要增加配额,请使用相应绑定元素上的 MaxReceivedMessageSize 属性。
    IIS 之 Web 服务器上的 ASP.NET 进程模型设置
    ADO.Net 之 数据库连接池(二)
  • 原文地址:https://www.cnblogs.com/yao2yaoblog/p/13208639.html
Copyright © 2020-2023  润新知