GDI
GDI:即图形设备接口,英文全称 Graphics Device Interface,简称GDI,GDI绘图效率低,是和Directx和openGL相比,
GDI+
GDI+ 由于是GDI基础上再次开发,比GDI高层,也就是距离底层远,执行效率低,GDI+不适合应用游戏
GDI特定
GDI 实现一套通用的图形对象,来向屏幕,内存甚至打印机进行绘图操作,GDI的特点:
- 不允许程序直接访问物理显示硬件,通过称为“设备环境”的抽象接口间接访问显示硬件
- 程序需要与显示硬件进行通讯时,必须首先获得与特定窗口相关联的设备环境
- 用户无需关系具体的物理设备类型
- windows参考设备环境的数据结构完成数据的输出
GDI中的基本图形
GDI中显示的“基本图形”:
- 直线和曲线:GID支持直线,矩形,椭圆等图形
- 填入区域:当一系列直线或者曲线封闭了一个区域,这个区域使用GID画刷对象进行填图
- 位图:位的矩形数组,位对应于显示设备上的像素,位映像图形的基础工具
- 文字
- 映像模式和变化
- 图元
- 绘图区域
- 路径
- 裁剪
- 调色盘
- 打印
GDI的函数分类
大体来看:GDI就是由几百个函数组成,那么它们的分类是:
- 取得和释放设备上下文的函数
- 取得有关设备上下文信息的函数
- 绘图函数
- 设定和取得设备上下文参数的函数
- 使用GDI对象的函数
设备环境DC
设备环境DC,DC就是程序可以进行绘图的地方,如果在屏幕上绘图,DC就是窗口上可以绘图的地方,系统中可以具有多个设备环境每个设备环境都有一个与之对应的关联设备;
如果将图形输出到特定的设备,只需要创建相应类型的设备句柄(HDC)就可,所以GDI绘图的时候,必须先取得设备环境的句柄,取得句柄的两种方式:
获得句柄的两种方式
第一种方法:需要在窗口过程函数处理WM_PAINT消息的case之后使用,在这里需要使用BeginPaint和EndPaint这两个函数
HDC BeginPaint( // 它为指定的窗口进行绘图工作,并将绘图有关的信息填充到 PAINTSTRUCT结构中
_In_ HWND hWnd, //HWND 类型的hund,填写获取设备环境句柄的那个窗口句柄
_Out_ LPPAINTSTRUCT lpPaint);//LPPAINTSTRUCT类型的lpPaint
对于结构体LPPAINTSTRUCT,这个结构体包含了用来绘制它所拥有的窗口客户区所需要的消息:
typedef struct tagPAINTSTRUCT {
HDC hdc; //绘制的句柄
BOOL fErase; //非零擦除背景,否则不擦除
RECT rcPaint; //制定左上角和右上角坐下确定矩形范围
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32]; //系统预留
} PAINTSTRUCT, *PPAINTSTRUCT, *NPPAINTSTRUCT, *LPPAINTSTRUCT;
对应BeginPaint的EndPaint函数,表示指定窗口的绘画过程结束:
BOOL EndPaint(
_In_ HWND hWnd, //被重画窗口的句柄
_In_ CONST PAINTSTRUCT *lpPaint); // BeginPaint中的结构体
ValidateRect函数。它用于更新指定窗口无效矩形的区域,使之有效
BOOL WINAPI ValidateRect(
_In_opt_ HWND hWnd, //标识想要修改状态的窗口,若为NULL,系统更新所有窗口
_In_opt_ CONST RECT *lpRect); // 指向有关包含需要生效的矩形更新区域坐标的RECT,为NULL,则客户区域将会生效
第二种方法:使用GetDC来获得句柄,调用GetDC后调用ReleaseDC对设备环境释放
HDC WINAPI GetDC(
_In_opt_ HWND hWnd); //窗口句柄,根据提供的窗口句柄返回对应窗口的设备上下文DC
与之对应的RealeaseDC,释放上下文的函数
int WINAPI ReleaseDC(
_In_opt_ HWND hWnd, //指定释放设备上下环境所在窗口的句柄
_In_ HDC hDC); //指向要释放的设备上下文环境的句柄
一个GDI程序的通过框架
//-----------------------------------【程序说明】----------------------------------------------
// 程序名称::GDIdemoCore
// 描述:GDI基本几何绘图示例程序
//------------------------------------------------------------------------------------------------
//-----------------------------------【头文件包含部分】---------------------------------------
// 描述:包含程序所依赖的头文件
//------------------------------------------------------------------------------------------------
#include <windows.h>
#include <time.h> //使用获取系统时间time函数需要包含的头文件
//-----------------------------------【库文件包含部分】---------------------------------------
// 描述:包含程序所依赖的库文件
//------------------------------------------------------------------------------------------------
#pragma comment(lib,"winmm.lib") //调用PlaySound函数所需库文件
//-----------------------------------【宏定义部分】--------------------------------------------
// 描述:定义一些辅助宏
//------------------------------------------------------------------------------------------------
#define WINDOW_WIDTH 800 //为窗口宽度定义的宏,以方便在此处修改窗口宽度
#define WINDOW_HEIGHT 600 //为窗口高度定义的宏,以方便在此处修改窗口高度
#define WINDOW_TITLE L"【致我们永不熄灭的游戏开发梦想】GDI基本几何绘图" //为窗口标题定义的宏
//-----------------------------------【全局变量声明部分】-------------------------------------
// 描述:全局变量的声明
//------------------------------------------------------------------------------------------------
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}; //定义画刷样式数组并初始化
//-----------------------------------【全局函数声明部分】-------------------------------------
// 描述:全局函数声明,防止“未声明的标识”系列错误
//------------------------------------------------------------------------------------------------
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 ); //在此函数中进行资源的清理
//-----------------------------------【WinMain( )函数】--------------------------------------
// 描述:Windows应用程序的入口函数,我们的程序从这里开始
//------------------------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd)
{
//【1】窗口创建四步曲之一:开始设计一个完整的窗口类
WNDCLASSEX wndClass = { 0 }; //用WINDCLASSEX定义了一个窗口类
wndClass.cbSize = sizeof( WNDCLASSEX ) ; //设置结构体的字节数大小
wndClass.style = CS_HREDRAW | CS_VREDRAW; //设置窗口的样式
wndClass.lpfnWndProc = WndProc; //设置指向窗口过程函数的指针
wndClass.cbClsExtra = 0; //窗口类的附加内存,取0就可以了
wndClass.cbWndExtra = 0; //窗口的附加内存,依然取0就行了
wndClass.hInstance = hInstance; //指定包含窗口过程的程序的实例句柄。
wndClass.hIcon=(HICON)::LoadImage(NULL,L"icon.ico",IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE); //本地加载自定义ico图标
wndClass.hCursor = LoadCursor( NULL, IDC_ARROW ); //指定窗口类的光标句柄。
wndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); //为hbrBackground成员指定一个白色画刷句柄
wndClass.lpszMenuName = NULL; //用一个以空终止的字符串,指定菜单资源的名字。
wndClass.lpszClassName = L"ForTheDreamOfGameDevelop"; //用一个以空终止的字符串,指定窗口类的名字。
//【2】窗口创建四步曲之二:注册窗口类
if( !RegisterClassEx( &wndClass ) ) //设计完窗口后,需要对窗口类进行注册,这样才能创建该类型的窗口
return -1;
//【3】窗口创建四步曲之三:正式创建窗口
HWND hwnd = CreateWindow( L"ForTheDreamOfGameDevelop",WINDOW_TITLE, //喜闻乐见的创建窗口函数CreateWindow
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH,
WINDOW_HEIGHT, NULL, NULL, hInstance, NULL );
//【4】窗口创建四步曲之四:窗口的移动、显示与更新
MoveWindow(hwnd,250,80,WINDOW_WIDTH,WINDOW_HEIGHT,true); //调整窗口显示时的位置,使窗口左上角位于(250,80)处
ShowWindow( hwnd, nShowCmd ); //调用ShowWindow函数来显示窗口
UpdateWindow(hwnd); //对窗口进行更新,就像我们买了新房子要装修一样
//游戏资源的初始化,若初始化失败,弹出一个消息框,并返回FALSE
if (!Game_Init (hwnd))
{
MessageBox(hwnd, L"资源初始化失败", L"消息窗口", 0); //使用MessageBox函数,创建一个消息窗口
return FALSE;
}
PlaySound(L"AIR - 夏影.wav", NULL, SND_FILENAME | SND_ASYNC|SND_LOOP); //循环播放背景音乐
//【5】消息循环过程
MSG msg = { 0 }; //定义并初始化msg
while( msg.message != WM_QUIT ) //使用while循环,如果消息不是WM_QUIT消息,就继续循环
{
if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) ) //查看应用程序消息队列,有消息时将队列中的消息派发出去。
{
TranslateMessage( &msg ); //将虚拟键消息转换为字符消息
DispatchMessage( &msg ); //分发一个消息给窗口程序。
}
}
//【6】窗口类的注销
UnregisterClass(L"ForTheDreamOfGameDevelop", wndClass.hInstance); //程序准备结束,注销窗口类
return 0;
}
//-----------------------------------【WndProc( )函数】--------------------------------------
// 描述:窗口过程函数WndProc,对窗口消息进行处理
//------------------------------------------------------------------------------------------------
LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
PAINTSTRUCT paintStruct; //定义一个PAINTSTRUCT结构体来记录一些绘制信息
switch( message ) //switch语句开始
{
case WM_PAINT: // 若是客户区重绘消息
g_hdc = BeginPaint( hwnd, &paintStruct ); //指定窗口进行绘图工作的准备,并用将和绘图有关的信息填充到paintStruct结构体中。
Game_Paint( hwnd);
EndPaint( hwnd, &paintStruct ); //EndPaint函数标记指定窗口的绘画过程结束
ValidateRect(hwnd, NULL); // 更新客户区的显示
break; //跳出该switch语句
case WM_KEYDOWN: // 若是键盘按下消息
if (wParam == VK_ESCAPE) // 如果被按下的键是ESC
DestroyWindow(hwnd); // 销毁窗口, 并发送一条WM_DESTROY消息
break; //跳出该switch语句
case WM_DESTROY: //若是窗口销毁消息
Game_CleanUp(hwnd); //调用自定义的资源清理函数Game_CleanUp()进行退出前的资源清理
PostQuitMessage( 0 ); //向系统表明有个线程有终止请求。用来响应WM_DESTROY消息
break; //跳出该switch语句
default: //若上述case条件都不符合,则执行该default语句
return DefWindowProc( hwnd, message, wParam, lParam ); //调用缺省的窗口过程
}
return 0; //正常退出
}
//-----------------------------------【Game_Init( )函数】--------------------------------------
// 描述:初始化函数,进行一些简单的初始化
//------------------------------------------------------------------------------------------------
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;
}
//-----------------------------------【Game_Paint( )函数】--------------------------------------
// 描述:绘制函数,在此函数中进行绘制操作
//--------------------------------------------------------------------------------------------------
VOID Game_Paint( HWND hwnd )
{
//定义一个y坐标值
int y=0;
//一个for循环,用7种不同的画笔绘制线条
for(int i=0;i<=6;i++)
{
y = (i+1) * 70;
SelectObject(g_hdc,g_hPen[i]);//将对应的画笔选好
MoveToEx(g_hdc,30,y,NULL); //“光标”移动到对应的(30,y)坐标处
LineTo(g_hdc,100,y); //从(30,y)坐标处向(100,y)绘制线段
}
/*注意上面画完后y=420,下面画矩形的时候还有用*/
//定义两个x坐标值
int x1 = 120;
int x2 = 190;
//用7种不同的画刷填充矩形
for(int i=0;i<=6;i++)
{
SelectObject(g_hdc,g_hBrush[i]); //选用画刷
Rectangle(g_hdc,x1,70,x2,y); //画出一个封闭的矩形,矩形左上角坐标为(x1,50),右下角坐标为(x2,y)
x1 += 90;
x2 += 90;
}
}
//-----------------------------------【Game_CleanUp( )函数】--------------------------------
// 描述:资源清理函数,在此函数中进行程序退出前资源的清理工作
//---------------------------------------------------------------------------------------------------
BOOL Game_CleanUp( HWND hwnd )
{
//一个for循环,释放掉所有的画笔和画刷句柄
for (int i=0;i<=6;i++)
{
DeleteObject(g_hPen[i]);
DeleteObject(g_hBrush[i]);
}
return TRUE;
}
GDI基本几何绘图
画笔和画刷都是GDI中定义的图形对象,画笔是线条的样式,画刷是封闭图形内部填充的样式,自定义绘图所需的画笔以及画刷的样式
创建画笔
HPEN 是画笔对象的句柄数据类型,标识一个画笔对象,新建一个画笔对象,使用CreatePen函数
WINGDIAPI HPEN WINAPI CreatePen(
_In_ int iStyle, //画笔样式
_In_ int cWidth, //线条宽度
_In_ COLORREF color);//线条颜色 color RGB控制
创建画刷
HBRUSH 是画刷对象句柄数据类型,标识一个画刷对象,新建画刷使用两个API:CreateSolidBrush 和 CreateHatchBrush
WINGDIAPI HBRUSH WINAPI CreateSolidBrush( // 创建实心画刷,填充时,填充使用纯色
_In_ COLORREF color); // RGB
WINGDIAPI HBRUSH WINAPI CreateHatchBrush( //创建一个阴影画刷,填充的内容是阴影线
_In_ int iHatch, // 阴影样式
_In_ COLORREF color); // RGB
图形对象的选择
创建画笔和画刷后,想要绘制图形还需要调用函数:
WINGDIAPI HGDIOBJ WINAPI SelectObject(
_In_ HDC hdc, //设备环境句柄
_In_ HGDIOBJ h); //被选用对象的句柄
WINGDIAPI BOOL WINAPI DeleteObject(
_In_ HGDIOBJ ho); // 用于指定想删掉的GDI对象句柄,成功TRUE, 失败FALSE
画笔画刷对象使用的过程: 创建-> 选用-> 删除
绘制图形和线条
WINGDIAPI BOOL WINAPI LineTo( // 绘制线条
_In_ HDC hdc, // 设备上下文的句柄
_In_ int x, // 线条终点x坐标值
_In_ int y); // 线条终点y坐标值
LineTo函数并不能指定线的起点,而是从画笔对象的“当前点”开始画,画完后当前点变为了绘制的终点,于LIneTo配合,使用MoveToEx函数来移动画笔的当前点
WINGDIAPI BOOL WINAPI MoveToEx(
_In_ HDC hdc, // 设备上下文句柄
_In_ int x, // x轴
_In_ int y, // y轴
_Out_opt_ LPPOINT lppt); // 指向存放上一个点的位置
WINGDIAPI BOOL WINAPI Rectangle( //绘制矩形
_In_ HDC hdc, // 句柄
_In_ int left, // 左上角x轴
_In_ int top, // 左上角y轴
_In_ int right, // 右下角x轴
_In_ int bottom); // 右下角y轴
随机系统
使用rand() 函数和 srand() 配合来达到产生随机数的效果,srand初始化随机种子,rand产生随机数;
_ACRTIMP void __cdecl srand(_In_ unsigned int _Seed);//随机种子
_Check_return_ _ACRTIMP int __cdecl rand(void); //rand() 调用起来不用设置参数,直接写
文字的输出
常用的文字输出函数TextOut
WINGDIAPI BOOL WINAPI TextOutW( _In_ HDC hdc, //进行文本输出的DC的句柄
_In_ int x, //开始书写的x坐标
_In_ int y, //开始书写的y坐标
_In_reads_(c) LPCWSTR lpString, // 指向字符串的指针
_In_ int c); //字符串的字符数
TextOut 的函数作用是当前选择的字符,背景颜色和正文颜色将一个字符串写到指定的位置,在指定的位置输出一段文字;
DrawTextW(
_In_ HDC hdc, //句柄
_When_((format & DT_MODIFYSTRING), _At_((LPWSTR)lpchText, _Inout_grows_updates_bypassable_or_z_(cchText, 4)))
_When_((!(format & DT_MODIFYSTRING)), _In_bypassable_reads_or_z_(cchText))
LPCWSTR lpchText, //写入字符串的指针
_In_ int cchText, //长度
_Inout_ LPRECT lprc, //绘制矩形结构体的指针
_In_ UINT format); //书写模式,定制标识选择
DrawText:在指定的矩形里吸入格式化文本,指定的方法对文本格式化;
设置文字颜色
WINGDIAPI COLORREF WINAPI SetTextColor(_In_ HDC hdc, //句柄
_In_ COLORREF color); //颜色
设置文字背景透明
WINGDIAPI int WINAPI SetBkMode(_In_ HDC hdc, //句柄
_In_ int mode); // 设备模式,
进行背景模式设置
字体创建
WINGDIAPI HFONT WINAPI CreateFontW( _In_ int cHeight, //字体高度
_In_ int cWidth, //字体宽度
_In_ int cEscapement, //字体显示角度
_In_ int cOrientation, //字体角度
_In_ int cWeight, //字体磅数
_In_ DWORD bItalic, //是否斜体
_In_ DWORD bUnderline, //下划线
_In_ DWORD bStrikeOut, // 删除线
_In_ DWORD iCharSet, // 所需字符集
_In_ DWORD iOutPrecision, //输出精度
_In_ DWORD iClipPrecision, //裁剪精度
_In_ DWORD iQuality, //输出质量
_In_ DWORD iPitchAndFamily, //字体间距字符集
_In_opt_ LPCWSTR pszFaceName); //字体名称
位图绘制
位图绘制四个步骤:
- 加载位图,从文件中加载位图对象
- 建立兼容DC,建立一个于窗口设备环境DC兼容的内存设备环境DC
- 选用位图对象,内存DC使用第一步建立的位图对象
- 进行贴图,将内存DC的内容贴到窗口DC中
加载位图:
HANDLE
WINAPI
LoadImageW( //加载位图的函数
_In_opt_ HINSTANCE hInst, //实例句柄
_In_ LPCWSTR name, //名称
_In_ UINT type, //加载类型
_In_ int cx, //存储宽度
_In_ int cy, //存储高度
_In_ UINT fuLoad); //加载方式
#ifdef UNICODE
#define LoadImage LoadImageW
建立兼容DC:
WINGDIAPI HDC WINAPI CreateCompatibleDC( //创建于窗口DC兼容的内存DC
_In_opt_ HDC hdc);
之后不使用该DC之后,也要撤销使用DelectDC
选用位图对象: SelectObject()就可以实现
进行贴图:
WINGDIAPI BOOL WINAPI BitBlt( _In_ HDC hdc, //目标设备环境句柄
_In_ int x, //目的DC的x坐标
_In_ int y, //y坐标
_In_ int cx, //宽度
_In_ int cy, //高度
_In_opt_ HDC hdcSrc,//源设备环境句柄
_In_ int x1, //DC x坐标
_In_ int y1, //DC y坐标
_In_ DWORD rop); //贴图方式