最近更新于 2024-05-05 14:18
环境
Windows 11 专业工作站版 22H2
Visual Studio 2022 专业版
Unicode 字符集,C17 标准编译,64 位编译
像素点及简单图形绘制
#include <Windows.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_PAINT:
{
PAINTSTRUCT ps = {0};
HDC hdc = BeginPaint(hWnd, &ps);
for (int i = 50; i < 200; ++i) // 连续绘制点构成一条直线
{
// 绘制像素点
// RGB 三色配比的取值范围 0~255
SetPixel(hdc, i, i, RGB(i + 50, i - 10, i + 50));
}
// 直线绘制
// 用四条线首尾相连画一个矩形
MoveToEx(hdc, 20, 20, NULL); // 起始位置,不指定 LineTo 默认从 (0,0) 开始
LineTo(hdc, 20, 200);
LineTo(hdc, 200, 200); // 上一次的末尾作为起始点
LineTo(hdc, 200, 20);
LineTo(hdc, 20, 20);
// 矩形绘制
Rectangle(hdc, 30, 30, 240, 100); // 封闭图形,内部有填充颜色,默认白色,和背景一样,会遮挡前面绘制的图案。下同
// 圆/椭圆绘制
Ellipse(hdc, 35, 35, 90, 90); // 指定圆或椭圆的外接矩形的左上角和右下角坐标
EndPaint(hWnd, &ps);
break;
}
case WM_CLOSE:
{
DestroyWindow(hWnd);
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
default:
{
return DefWindowProc(hWnd, msg, wParam, lParam);
}
}
return 0;
}
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPreInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
PCWSTR szClassName = L"Desktop";
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WndProc;
wc.lpszClassName = szClassName;
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, L"注册窗口失败!", L"错误", MB_OK);
return 1;
}
HWND hWnd = CreateWindowEx(0, szClassName, L"简单程序", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 300, 300, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
MessageBox(NULL, L"创建窗口失败!", L"错误", MB_OK);
return 1;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
MSG msg = {0};
while (GetMessage(&msg, NULL, 0, 0))
{
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
画笔和画刷的使用
相比于前面的绘图,还可以设置画笔的粗细以及颜色,另外还有图形填充。
#include <Windows.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_PAINT:
{
PAINTSTRUCT ps = {0};
HDC hdc = BeginPaint(hWnd, &ps);
HPEN hPen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0)); // 创建画笔,第一个参数设置线型,PS_SOLID 是实线,比如 PS_DASH 是虚线,还有其它线型,如果第二个参数线宽大于 1,那么还是会变成实线
// 这里是创建纯色的画刷
// 还有可以创建纹理的画刷等等,函数名格式大概就是 Create[XXXX]Brush
// 画刷用于填充
HBRUSH hBrush = CreateSolidBrush(RGB(0, 0, 255));
HGDIOBJ hOldPen = SelectObject(hdc, hPen); // 将画笔切换为自己创建的,并保存返回值,即默认画笔
HGDIOBJ hOldBrush = SelectObject(hdc, hBrush); // 将画刷切换为自己创建的,并保存返回值,即默认画刷
Rectangle(hdc, 20, 20, 250, 100); // 绘图
SelectObject(hdc, hOldPen); // 将画笔重新设为默认的
SelectObject(hdc, hOldBrush); // 将画刷重新设为默认的
DeleteObject(hPen); // 销毁创建的画笔
DeleteObject(hBrush); // 销毁创建的画刷
EndPaint(hWnd, &ps);
break;
}
case WM_CLOSE:
{
DestroyWindow(hWnd);
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
default:
{
return DefWindowProc(hWnd, msg, wParam, lParam);
}
}
return 0;
}
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPreInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
PCWSTR szClassName = L"Desktop";
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WndProc;
wc.lpszClassName = szClassName;
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, L"注册窗口失败!", L"错误", MB_OK);
return 1;
}
HWND hWnd = CreateWindowEx(0, szClassName, L"简单程序", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 300, 300, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
MessageBox(NULL, L"创建窗口失败!", L"错误", MB_OK);
return 1;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
MSG msg = {0};
while (GetMessage(&msg, NULL, 0, 0))
{
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
位图的使用
先创建一个资源文件,再添加位图资源
然后可以自己绘制图案,在空白处右键属性可以设置位图大小,我这里就是默认的 48×48(后面写代码要用)
画完 Ctrl+S 保存一下,开始撸代码
顺便提一下,位图分为光栅图形和矢量图形,这里画的属于前者,图像数据会记录每个像素点,而矢量图形通常记录的图像的算法及绘图指令等,就比如我图像上要展示 y=x^2
的图像,我并不需要画出来再保存这个图像,而是保存这个函数式。那么矢量图形就有一个性质,放大图像都不会影响清晰度,因为图像内容是根据绘图的指令直接生成的,图像和分辨率没什么关系。
#include <Windows.h>
#include "resource.h" // 引入资源文件
HBITMAP g_hBitmap = {0}; // 用于保存位图资源句柄
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_PAINT:
{
PAINTSTRUCT ps = {0};
HDC hdc = BeginPaint(hWnd, &ps);
HDC hdc_ccdc = CreateCompatibleDC(hdc); // 创建和 hdc 兼容的内存上下文
HGDIOBJ hOldBitmap = SelectObject(hdc_ccdc, g_hBitmap); // 将位图资源放入 hdc_ccdc
// 将 hdc_ccdc 中的位图资源原样复制到 hdc 中,即实现位图显示出来
// 目的 DC 句柄
// 目的左上角 x
// 目的左上角 y
// 目的宽度 - 这里就对应的位图的分辨率 48x48,根据自己绘制的图像大小而定
// 目的高度
// 源 DC 句柄
// 源的左上角 x
// 源的左上角 y
// 操作代码,这里是原样复制
BitBlt(hdc, 30, 30, 48, 48, hdc_ccdc, 0, 0, SRCCOPY);
SelectObject(hdc_ccdc, hOldBitmap);
DeleteDC(hdc_ccdc);
EndPaint(hWnd, &ps);
break;
}
case WM_CLOSE:
{
DestroyWindow(hWnd);
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
DeleteObject(g_hBitmap); // 释放位图资源
break;
}
default:
{
return DefWindowProc(hWnd, msg, wParam, lParam);
}
}
return 0;
}
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPreInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
g_hBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP1)); // 加载位图资源
PCWSTR szClassName = L"Desktop";
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WndProc;
wc.lpszClassName = szClassName;
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, L"注册窗口失败!", L"错误", MB_OK);
return 1;
}
HWND hWnd = CreateWindowEx(0, szClassName, L"简单程序", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 300, 300, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
MessageBox(NULL, L"创建窗口失败!", L"错误", MB_OK);
return 1;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
MSG msg = {0};
while (GetMessage(&msg, NULL, 0, 0))
{
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
桌面程序绘图编程(GDI) – Windows API