GDI

GDI 有一些基本函数和图形定义。总之,其就是一些数据结构、函数和宏组成的整体。而各种各样的函数都逃不开一个概念——设备环境 DC.

4.2 设备环境

一个设备环境,就是程序中一个可以进行绘图的地方。也就是“客户区”。

在 GDI 图像输出时,我们只需要关心设备环境的类型。

而要确定设备环境的类型,创建一个相应类型的句柄即可。(不得不说 C++ 的句柄设计的挺好的)

4.2.2 获取 HDC (设备环境句柄)

  1. 使用 BeginPaintEndPaint

    在微软文档中查到这两个函数的原型:

    HDC BeginPaint([in]  HWND          hWnd,[out] LPPAINTSTRUCT lpPaint
    );
    
    BOOL EndPaint([in] HWND              hWnd,[in] const PAINTSTRUCT *lpPaint
    );
    

    分别是窗口句柄和一个 lpPaint.

    一个实例:

    HDC g_hdc; // 注意匈牙利命名法的g_前缀表示全局变量.
    ...case WM_PAINT:g_hdc = BeginPaint(hwnd, &paintStruct);Game_Paint();EndPaint(hwnd, &paintStruct);ValidateRect(hwnd, NULL);break;
    ...
    
  2. 直接使用 GetDC,并使用 ReleaseDC 进行 DC 释放.

    HDC g_hdc;
    g_hdc = GetDC(hWnd);
    ...
    ReleaseDC(hWnd, g_hdc);
    

4.3

4.4

写出一个 GDI 程序通用框架,只是一个框架,将上一节的 GameCore 中的 main 拷贝过来进行修改得到:

//-------------------【程序说明】-------------------
// 程序名称:GDIdemoCore
// Learn from 浅墨 orz
// 2022 年 2 月 Created by Eke
// 描述:实现 GDI 游戏开发所需的核心程序
//--------------------------------------------------//-----------------【头文件部分】---------------------
// 描述:程序依赖的头文件
//--------------------------------------------------
#include <Windows.h>//-----------------【宏定义部分】--------------------
// 描述:定义辅助宏
//--------------------------------------------------
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
constexpr auto WINDOW_TITLE = L"谁准备在滑索上飞行?我!"; // 新版本中使用constexpr比define更安全.//----------------【全局函数声明部分】----------------
// 描述:全局函数声明.
//--------------------------------------------------
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
BOOL Game_Init(HWND hwnd); // 资源初始化
VOID Game_Paint(HWND hwnd); // 绘图代码书写
BOOL Game_CleanUp(HWND hwnd); // 资源清理//----------------【全局变量声明部分】----------------
// 描述:全局变量声明.
//--------------------------------------------------
HDC g_hdc = NULL;//----------------【WinMain( )函数】-----------------
// 描述:程序主入口
//--------------------------------------------------
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) // 需要将 _In_, _In_opt_ 依照函数原型加上,否则会报警告. 不过为啥呢?
{// 四部曲// 1. Design (Oh 11 paras!)WNDCLASSEX wndClass = { 0 }; // 定义一个窗口类wndClass.cbSize = sizeof(WNDCLASSEX);wndClass.style = CS_HREDRAW | CS_VREDRAW;wndClass.lpfnWndProc = WndProc;wndClass.cbClsExtra = 0;wndClass.cbWndExtra = 0;wndClass.hInstance = hInstance;wndClass.hIcon = (HICON)::LoadImage(NULL, L"favicon.ico", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); // 试了下没有RED\BLUE 的brush,qwqwndClass.lpszMenuName = NULL;wndClass.lpszClassName = L"ForTheDreamOfGameDevelop";// 2. Registerif (!RegisterClassEx(&wndClass)) return -1;// 3. CreateHWND hwnd = CreateWindow(L"ForTheDreamOfGameDevelop", WINDOW_TITLE,WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hInstance, NULL);// 4. Move, show and updateMoveWindow(hwnd, 250, 80, WINDOW_WIDTH, WINDOW_HEIGHT, true);ShowWindow(hwnd, nCmdShow);UpdateWindow(hwnd);if (!Game_Init(hwnd)){MessageBox(hwnd, L"资源初始化失败", L"消息", 0);return FALSE;}// 5. msg loopMSG msg = { 0 };while (msg.message != WM_QUIT);{if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)){TranslateMessage(&msg);DispatchMessage(&msg);}}// 6. unregisterUnregisterClass(L"ForTheDreamOfGameDevelop", wndClass.hInstance);return 0;
}LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{PAINTSTRUCT paintStruct;switch (message){case WM_PAINT:g_hdc = BeginPaint(hwnd, &paintStruct);Game_Paint(hwnd);EndPaint(hwnd, &paintStruct);ValidateRect(hwnd, NULL);break;case WM_KEYDOWN:if (wParam == VK_ESCAPE) DestroyWindow(hwnd);break;case WM_DESTROY:Game_CleanUp(hwnd);PostQuitMessage(0);break;default:return DefWindowProc(hwnd, message, wParam, lParam);}return 0;
}BOOL Game_Init(HWND hwnd)
{g_hdc = GetDC(hwnd);Game_Paint(hwnd);ReleaseDC(hwnd, g_hdc);return TRUE;
}VOID Game_Paint(HWND hwnd)
{}BOOL Game_CleanUp(HWND hwnd)
{return TRUE;
}

ok. 留作备用.

4.5 GDI 基本几何绘图

看似不太重要的一节。

大概浏览了一下,也是相似的一些接口函数。先不在笔记里记录。

(也没什么难点)

主要是要搞清楚画笔和画刷:

画笔是用于勾勒线条、形状轮廓以及颜色的;

画刷用于创建实心形状和呈现文本.

就是说,画笔决定颜色和形状,而画刷在画笔画好的图形上“绣花”.

4.6 随机数

没啥看点,熟悉。

熟悉完随机之后,写一个示例程序 GDIdemo1,一个画笔与画刷的使用演示程序。

其中还有背景音乐的播放,不过不是使用的 PlaySound() 函数,而是使用了 MCI:

//-------------------【程序说明】-------------------
// 程序名称:GDIdemoCore
// Learn from 浅墨 orz
// 2022 年 2 月 Created by Eke
// 描述:实现 GDI 游戏开发所需的核心程序
//--------------------------------------------------//-----------------【头文件部分】---------------------
// 描述:程序依赖的头文件
//--------------------------------------------------
#include <Windows.h>
#include <time.h>
#include <mmsystem.h>
#pragma comment (lib, "winmm.lib") // 链接 winmm 库.//-----------------【宏定义部分】--------------------
// 描述:定义辅助宏
//--------------------------------------------------
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
constexpr auto WINDOW_TITLE = L"谁准备在滑索上飞行?我!"; // 新版本中使用constexpr比define更安全.//----------------【全局函数声明部分】----------------
// 描述:全局函数声明.
//--------------------------------------------------
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
BOOL Game_Init(HWND hwnd); // 资源初始化
VOID Game_Paint(HWND hwnd); // 绘图代码书写
BOOL Game_CleanUp(HWND hwnd); // 资源清理//----------------【全局变量声明部分】----------------
// 描述:全局变量声明.
//--------------------------------------------------
HDC g_hdc = NULL;
HPEN g_hPen[7] = { 0 };
HBRUSH g_hBrush[7] = { 0 };
int g_iPenStyle[7] = { PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOT, PS_DASHDOTDOT, PS_NULL, PS_INSIDEFRAME };
int g_iBrushStyle[6] = { HS_VERTICAL, HS_HORIZONTAL, HS_CROSS, HS_DIAGCROSS, HS_FDIAGONAL, HS_BDIAGONAL };//----------------【WinMain( )函数】-----------------
// 描述:程序主入口
//--------------------------------------------------
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) // 需要将 _In_, _In_opt_ 依照函数原型加上,否则会报警告. 为啥呢?
{// 四部曲// 1. Design (Oh 11 paras!)WNDCLASSEX wndClass = { 0 }; // 定义一个窗口类wndClass.cbSize = sizeof(WNDCLASSEX);wndClass.style = CS_HREDRAW | CS_VREDRAW;wndClass.lpfnWndProc = WndProc;wndClass.cbClsExtra = 0;wndClass.cbWndExtra = 0;wndClass.hInstance = hInstance;wndClass.hIcon = (HICON)::LoadImage(NULL, L"favicon.ico", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // 试了下没有RED\BLUE 的brush,qwqwndClass.lpszMenuName = NULL;wndClass.lpszClassName = L"ForTheDreamOfGameDevelop";// 2. Registerif (!RegisterClassEx(&wndClass)) return -1;// 3. CreateHWND hwnd = CreateWindow(L"ForTheDreamOfGameDevelop", WINDOW_TITLE,WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hInstance, NULL);// 4. Move, show and updateMoveWindow(hwnd, 250, 80, WINDOW_WIDTH, WINDOW_HEIGHT, true);ShowWindow(hwnd, nCmdShow);UpdateWindow(hwnd);/* 这里书中使用 PlaySound 函数来播放背景音乐,可惜这个函数只支持 .wav 格式. 所以我使用了 mci 来进行声音播放,功能强大,支持mp3. */mciSendString(TEXT("open 打上花火.mp3 alias BackMusic"), NULL, 0, NULL); // 打开音乐mciSendString(L"play BackMusic repeat", NULL, 0, NULL); // 开始播放if (!Game_Init(hwnd)){MessageBox(hwnd, L"资源初始化失败", L"消息", 0);return FALSE;}// 5. msg loopMSG msg = { 0 };while (msg.message != WM_QUIT);{if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)){TranslateMessage(&msg);DispatchMessage(&msg);}}mciSendString(L"stop BackMusic", NULL, 0, NULL); // 停止播放mciSendString(L"close BackMusic", NULL, 0, NULL); // 关闭音乐// 6. unregisterUnregisterClass(L"ForTheDreamOfGameDevelop", wndClass.hInstance);return 0;
}LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{PAINTSTRUCT paintStruct;switch (message){case WM_PAINT:g_hdc = BeginPaint(hwnd, &paintStruct);Game_Paint(hwnd);EndPaint(hwnd, &paintStruct);ValidateRect(hwnd, NULL);break;case WM_KEYDOWN:if (wParam == VK_ESCAPE) DestroyWindow(hwnd);break;case WM_DESTROY:Game_CleanUp(hwnd);PostQuitMessage(0);break;default:return DefWindowProc(hwnd, message, wParam, lParam);}return 0;
}BOOL Game_Init(HWND hwnd)
{g_hdc = GetDC(hwnd);srand((unsigned)time(NULL));for (int i = 0; i <= 6; ++i) {g_hPen[i] = CreatePen(g_iPenStyle[i], 1, RGB(rand() % 256, rand() % 256, rand() % 256));if (i == 6) {g_hBrush[i] = CreateSolidBrush(RGB(rand() % 256, rand() % 256, rand() % 256));}else {g_hBrush[i] = CreateHatchBrush(g_iBrushStyle[i], RGB(rand() % 256, rand() % 256, rand() % 256));}}Game_Paint(hwnd);ReleaseDC(hwnd, g_hdc);return TRUE;
}VOID Game_Paint(HWND hwnd)
{int y = 0;for (int i = 0; i <= 6; ++i) {y = (i + 1) * 70;SelectObject(g_hdc, g_hPen[i]); // 选择好画笔. 这里选择的主体是谁呢? g_hdc,是我们的 DC.MoveToEx(g_hdc, 30, y, NULL); // move cursor to (30, y)LineTo(g_hdc, 100, y); // from pos to (100, y)}/* now y = 420 */int x1 = 120, x2 = 190;// 好,画笔画好了,现在需要用画刷填充画出来的矩形.for (int i = 0; i <= 6; ++i) {SelectObject(g_hdc, g_hBrush[i]);Rectangle(g_hdc, x1, 70, x2, y);x1 += 90, x2 += 90;}
}BOOL Game_CleanUp(HWND hwnd)
{for (int i = 0; i <= 6; ++i) {DeleteObject(g_hPen[i]);DeleteObject(g_hBrush[i]);}return TRUE;
}

某次运行的效果:

(此处应有 DAOKO 与八爷的《打上花火》BGM…)

4.7 文字

最常用文字输出函数:TextOut.

了解你的进阶输出函数:DrawText.

这个函数在指定的矩形里写入格式化文本,并对文本进行格式化一些格式化.

SetTextColor: 设置文字的颜色值. 注意其参数只有 DC 句柄和文本颜色. ( 天呐,这不就意味着一个设备环境只能有一个颜色了吗 )

SetBkMode:设置文字背景模式. 通常设置为 TRANSPARENT(透明)。如果不调用这个函数,默认文字背景为白色。

CreateFont:字体创建函数. 字体在创建后使用 SelectObject 选进设备环境即可。

第二个示例程序:我们只修改了 GamePaint 函数.

VOID Game_Paint(HWND hwnd)
{HFONT hFont = CreateFont(30, 0, 0, 0, 0, 0, 0, 0, GB2312_CHARSET, 0, 0, 0, 0, L"微软雅黑"); // 创建一种字体SelectObject(g_hdc, hFont);SetBkMode(g_hdc, TRANSPARENT);wchar_t text1[] = L"飞身乘上时代的风暴,今夜要成为台风之眼!";wchar_t text2[] = L"この風に飛び乗って、今夜台風の目となって!";wchar_t text3[] = L"------King GNU 《飛行艇》";SetTextColor(g_hdc, RGB(50, 255, 50));TextOut(g_hdc, 30, 150, text1, wcslen(text1));SetTextColor(g_hdc, RGB(50, 50, 255));TextOut(g_hdc, 30, 200, text2, wcslen(text2));SetTextColor(g_hdc, RGB(255, 150, 50));TextOut(g_hdc, 500, 250, text3, wcslen(text3));DeleteObject(hFont);
}

运行效果:

4.8 位图

位图绘制四步曲:

  1. 从文件中加载位图对象(给位图句柄)
  2. 建立一个与窗口 DC 兼容的内存 DC.
  3. 选用位图对象
  4. 进行贴图. 将内存 DC 的内容贴到窗口 DC 中.

加载位图

使用 LoadImage 函数. 其作用就是将一个图片加载到位图句柄中.

建立兼容DC

使用 CreateCompatibleDC 函数. 其作用就是创建一个与窗口 DC 兼容的内存 DC.

内存 DC 在使用后释放,调用的是 DleteDC() 而不是 ReleaseDC(), 可以参考下方链接:

DeleteDC() 与 ReleaseDC() 的区别 转 - vranger - 博客园 (cnblogs.com)

选用位图对象

SelectObject().

进行贴图

必须要熟悉好贴图函数,例如 BitBlt(), TransparentBlt(), StrentchBlt().

接下来,在 GameInit(), GamePaint() 中加上这四步,就大功告成了。别忘了释放。释放在 Game_CleanUp() 中.

BOOL Game_Init(HWND hwnd)
{g_hdc = GetDC(hwnd);// STEP1g_hBitmap = (HBITMAP)LoadImage(NULL, L"11.bmp", IMAGE_BITMAP, 800, 600, LR_LOADFROMFILE);// STEP2g_mdc = CreateCompatibleDC(g_hdc);Game_Paint(hwnd);ReleaseDC(hwnd, g_hdc);return TRUE;
}
VOID Game_Paint(HWND hwnd)
{// STEP3SelectObject(g_mdc, g_hBitmap);// STEP4BitBlt(g_hdc, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, g_mdc, 0, 0, NOTSRCCOPY);HFONT hFont = CreateFont(30, 0, 0, 0, 0, 0, 0, 0, GB2312_CHARSET, 0, 0, 0, 0, L"微软雅黑"); // 创建一种字体SelectObject(g_hdc, hFont);SetBkMode(g_hdc, TRANSPARENT);wchar_t text1[] = L"飞身乘上时代的风暴,今夜要成为台风之眼!";wchar_t text2[] = L"この風に飛び乗って、今夜台風の目となって!";wchar_t text3[] = L"------King GNU 《飛行艇》";SetTextColor(g_hdc, RGB(50, 255, 50));TextOut(g_hdc, 30, 150, text1, wcslen(text1));SetTextColor(g_hdc, RGB(50, 50, 255));TextOut(g_hdc, 30, 200, text2, wcslen(text2));SetTextColor(g_hdc, RGB(255, 150, 50));TextOut(g_hdc, 500, 250, text3, wcslen(text3));DeleteObject(hFont);
}
BOOL Game_CleanUp(HWND hwnd)
{DeleteObject(g_hBitmap);DeleteDC(g_mdc);return TRUE;
}

第三个示例程序:

总结

似乎讲到现在就是一系列的接口+函数。我觉得,使用 GDI 开发何尝不是如此呢?

《WINDOWS游戏编程之从零开始》第四章学习笔记相关推荐

  1. Windows游戏编程之从零开始d

    Windows游戏编程之从零开始d I'm back~~恩,几个月不见,大家还好吗? 这段时间真的好多童鞋在博客里留言说或者发邮件说浅墨你回来继续更新博客吧. woxiangnifrr童鞋说每天都在来 ...

  2. 【浅墨著作】 逐梦旅程 Windows游戏编程之从零开始 勘误 配套源代码下载

    这段时间真的好多童鞋在博客里留言说或者发邮件说浅墨你回来继续更新博客吧. woxiangnifrr童鞋说每天都在来浅墨的博客逛一下看有没有更新,"每天都来就像看女神那般不依不舍", ...

  3. C++程序设计教程(钱能)第四章 学习笔记

    C++程序设计教程(钱能)第四章 学习笔记 4.1 名词解释与操作符 4.1.1 名词解释 4.1.2 操作符汇总 4.1.3 操作符的说明 4.2 算数运算问题 4.2.1 周而复始的整数 4.2. ...

  4. 【读书笔记】《Windows游戏编程之从零开始》(一)

    Windows编程入门 HelloVisualStudio这个项目介绍了,Windows程序的"心脏"--WinMain函数,以及MessageBox函数的详细用法. FirstB ...

  5. 数字图像处理与MATLAB 第四章学习笔记

    第四章 图像复原与重建 图像复原技术主要目的是以预先确定的目标来改善图像,大部分属于客观处理,面向退化模型,并采用相反的过程进行处理,以便恢复出原图像. 图像增强技术基本上是一种探索性过程,即根据人类 ...

  6. 《鲁棒控制——线性矩阵不等式处理方法》(俞立)第二、三、四章学习笔记

    第二章   线性矩阵不等式  :非零向量,  或者的最大特征值小于0. 是凸集.(设V是数域P上的线性空间,W是V的一个非空子集,如果对W中任意两个向量a,b以及任意0<=c<=1,都有c ...

  7. 《WINDOWS游戏编程之从零开始》第五章学习笔记

    原本粗略浏览了这一章节,就又去自己找图片素材了- 找到一半发现贴图不太好找原图,但是不想抠图,也不想用糊图,结果我下回了<美少女万华镜 4>(甚至忍不住玩了一会,剧情还是大赞的),并看了一 ...

  8. 《逐梦旅程 WINDOWS游戏编程之从零开始》笔记10——三维天空的构建三维粒子的实现多游戏模型的载入...

    第23章 三维天空的构建 目前描述三维天空的技术主要包括三种类型,直接来介绍使用最广泛的模拟技术,详细的描述可以见作者的博文. 天空盒(Sky Box),即放到场景的是一个立方体.它是目前使用最广泛的 ...

  9. dx12 龙书第四章学习笔记 -- Direct3D的初始化

    1.预备知识: ①Direct3D 12概述: 通过Direct3D这种底层图形应用程序编程接口(Application Programming Interface, API),即可在应用程序中对图形 ...

  10. 【C++ Primer】第四章学习笔记 (复合类型)

    一,数组      1,数组只有在定义时候才能使用初始化,不能将一个数组赋给另一个数组.            int  a[4]={1,2,3,4};//正确            int  a[4 ...

最新文章

  1. “中序表达式”转“后续表达式”
  2. Local Response Normalization作用——对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力...
  3. 百步斋诗钞【绝句四首】
  4. 计算机系统结构结构相关实验报告,计算机系统结构实验报告(中南民族大学).doc...
  5. 河北体检系统诚信企业推荐_海南膜结构停车棚加工厂诚信企业推荐
  6. Ansible 获取主机信息模块setup、获取文件详细信息模块stat(学习笔记十)
  7. 北大AI第八讲 李航 自然语言处理的现实与挑战
  8. ASP.NET 性能监控工具和优化技巧
  9. theano java_Windows Theano GPU 版配置
  10. java数据类型有哪几种_java数据类型有哪些
  11. sql server 日期条件-上月、年初、同期
  12. 集丰照明|LED点光源安装方法
  13. 腾讯云大学大咖分享 | 自然语言处理技术(NLP)究竟能做些什么?
  14. 新型超级电容/法拉电容介绍
  15. 怎样两个网络互远程连接计算机,两个电脑怎么远程控制两个方法教你
  16. 计算机办公软件应用教学总结,2018年office办公软件教学总结(精选)
  17. 合振动的初相位推导_②音响处理器肿么调?处理器教学 音箱延时相位耦合就是这么简单【愤怒的调音师】...
  18. 李宁Matplotlib视频课程作业
  19. 加速代理游戏,让指定进程走代理网络,驱动级SOCKS5代理使用方法。
  20. mysql打平子表_股民网友加入散户家园的流程表

热门文章

  1. webx框架升级springboot遇到的问题及解决方案
  2. 6m缓存和8m缓存差距_i7-9700和i7-9700K性能差距多大?i7-9700和i7-9700K区别对比评测...
  3. java 极光_极光IM系列之java后台集成
  4. VTD的官方help翻译-ROD部分(10~15章)
  5. scratch 3 下载和安装
  6. ERP系统之JPJDE入门-1 JDE 简史
  7. Windows XP 系统中内置的AT命令
  8. 摄像头视频画面接入,使用iframe标签嵌入到页面中(海康威视、萤石)
  9. Android原生PDF功能实现,掌握了这些Android高级工程师必备知识,
  10. TokenInsight对话首席——暗流涌动,钱包如何引领数字资产新生态