Windows【游戏】编程GDI是什么

前言

上一篇已经实现了基本的引擎类了,漫漫长路才刚刚开始。

先看看下面这个案例,将前面的代码进行一点修改。

1.在Violet.h开头的外部非成员函数声明区域加上下面的声明

void Draw_Line(HDC hdc);

2.在Violet.cpp的Paint函数里加上

	void MyEngine::Paint(HDC hdc) {
		//实际绘图
		Draw_Line(hdc);
	}

3.在main.cpp中加上其实现



void 	Draw_Line(HDC hdc){
	

		HPEN pen;
		int y = 1, x = 1;//row col
	

		//画笔类型
		int style[] = {
			PS_SOLID,                  //  ————
			PS_DASH,				   //  -----------
			PS_DOT,					   //  .............
			PS_DASHDOT,            //  _._._._._
			PS_DASHDOTDOT,	   //  _.._.._.._
			PS_NULL					   //  
		};


		//画彩色方块
		for (int red = 0; red < 256; red++) {
			for (int blue = 0; blue < 256; blue++) {
				//创建画笔
				pen = CreatePen(PS_SOLID, 1, RGB(red, 0, blue));
				//选中画笔
				SelectObject(hdc, pen);

				//开始绘制
				MoveToEx(hdc, x, y, NULL);
				LineTo(hdc, x++, y + 1);
				//删除画笔
				DeleteObject(pen);


			}
			x = 1;
			y += 1;

		}

		//画线段样式
		for (int i = 0; i < sizeof(style) / sizeof(style[0]); i++) {
			//创建画笔
			pen = CreatePen(style[i], 1, RGB(0, 0, 0));
			//选中画笔
			SelectObject(hdc, pen);

			//开始绘制
			MoveToEx(hdc, 30 * i + 380, 50, NULL);
			LineTo(hdc, 30 * i + 380, 200);

		}
		DeleteObject(pen);


		//创建画笔对象
		pen = CreatePen(PS_DASH, 1, RGB(0, 0, 255));
		//选中画笔
		SelectObject(hdc, pen);

		//移动点
		MoveToEx(hdc, 200, 200, NULL);
		//开始绘制
		LineTo(hdc, 400, 200);
		LineTo(hdc, 300, 50);
		LineTo(hdc, 200, 200);

		//删除画笔对象
		DeleteObject(pen);

	

}

显示效果:

在这里插入图片描述

  • 看到绘图的速度没?这么慢的技术简单了解一下就行了。

可以通过调试加断点的方法把上面这个案例的运行步骤找出来。

在运行主循环前,直到显示窗口后,就经常循环在回调函数这。
到了主循环后,同样不时地会运行进回调函数,就当回调函数是另外一个线程罢了。
在这里插入图片描述

这算是以我目前的水平对这个引擎框架的运行步骤做的解释了。

**

Graphic Device Interface

**
图形设备接口。
Windows中的图形主要由gdI32.dll动态链接库输出的函数来处理。这些动态链接库调用显示器、打印机等外部设备的驱动程序,之后由驱动程序来控制相应的硬件,将GDI命令转换为硬件能够理解的代码或者命令。
因此,GDI的主要目的之一是支持与设备无关的图形。

附上我盗的图
在这里插入图片描述

Windows程序应该能够在Windows支持的任意一种图形输出设备上执行,GDI通过将应用程序和不同输出设备的特性隔离开的方法来达到这一目的。

GDI的局限

虽然可以在显示器上到处移动图形对象,但GDI通常是一个静态的显示系统,只有有限的动画支持。对于不能胜任的动画任务,需要去依赖GDI+、DirectX和OpenGL等。

GDI的函数有以下几类:

  1. 获取和释放设备环境的函数
  2. 获取有关设备环境信息的函数
  3. 绘图函数
  4. 设定和获取设备环境参数的函数
  5. 操作GDI对象的函数

那么,什么是设备环境???

又叫设备内容、设备上下文。实际上是GDI内部保存的数据结构。

设备环境与特定的显示设备(显示器或打印机)相关。对于显示器,设备环境总是与显示器上的特定窗口相关。设备环境中的有些内容是图形属性,这些属性定义了绘图函数工作的细节。

设备环境句柄是GDI函数的窗口通行证,可以以设备环境句柄为参数,调用相应的GDI函数,就可以在显示屏中特定窗口的显示区域上绘图。大部分GDI函数都将设备环境句柄作为第一个参数。当程序需要绘图时,它必须先取得设备环境句柄。取得后,windows用默认的属性值填入设备环境结构。下面的表显示了一些更改设备环境属性的函数:

在这里插入图片描述

GDI绘图

在Windows中使用GDI绘图,首先要进行一系列的准备工作,然后调用GDI绘图函数实现主要的绘图工作,最后进行相应的清理,恢复工作。

  1. 获取设备环境
  2. 根据绘图需求,对设备环境默认属性进行修改
  3. 调用绘图函数绘图
  4. 清理工具、恢复环境

1.获取设备环境

HDC BeginPaint(HWND hWnd,PAINTSTRUCT& ps);

BeginPaint函数将获得设备环境句柄返回,程序应该保留此句柄,之后通过它在窗口上绘图。第一个参数是窗口句柄,用来指定设备环境所关联的窗口。第二个参数是绘图信息结构指针,函数执行时,系统将当期窗口显示区域的无效矩形信息填充在此结构中。绘图时,Windows只在无效区域内绘图。

需要与EndPaint(HWND hWnd,PAINTSTRUCT& ps);配合使用。

并且,上面这种方法只能在WM_PAINT消息处理期间使用

HDC GetDC(HWND hwnd);

有时,程序需要在其他非WM_PAINT消息处理期间绘图,或者程序只需要获得设备环境,从而得到设备环境的某些信息。用到GetDC函数,在调用时需要将设备环境所关联的窗口句柄作为参数,调用后得到该窗口显示区域的设备环境,之后返回HDC类型的设备环境句柄。

int ReleaseDC(HWND hwnd,HDC hdc);

需要与ReleaseDC配合使用。

  • 与从BeginPaint传回设备环境句柄不同,GetDC传回的设备环境句柄具有一个剪取矩形,它等于整个显示区域。可以在显示区域的某一部分绘图,而不是在无效矩形上绘图。另外,与BeginPaint不同,GetDC不会使任何无效区域变为有效。

程序调用GetDC和ReleaseDC对键盘消息和鼠标消息做出反应,此时程序可以立即根据使用者的输入来更新显示区域,而不需要考虑为了窗口的无效区域而使用WM_PAINT。

所以此时绘图是直接在窗口上绘图,而没有将绘图结果保存为有效。
如果此时移动窗口、菜单下来等操作引起窗口更新,那么绘图就变得不正常。
解决方法是在绘图后手动将窗口区域变得有效。

ValidateRect(hwnd,NULL)
将hwnd窗口中的整个显示区域都标记为有效(有效表示不用再画)。

同理,也可以用类似的函数将所标识的区域作为无效区域而得到重绘。

BOOL InvalidateRect(HWND hwnd,RECT& rt,bFlag flag);
  • flag表示重绘时是否擦除背景,函数执行成功返回非0

2.创建GDI绘图工具

按照习惯,先贴代码(3月初开始的吧(逃))

1.声明下面的绘图函数

void Draw(HDC hdc,int cxClient,int cyClient);

2.修改HandleEvent内的代码

long MyEngine::HandleEvent(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {


		static int cxClient, cyClient;

		switch(msg){
		case WM_CREATE:
			SetSleep(false);//激活游戏
			return 0;
		case WM_SIZE:
		{
			cyClient = HIWORD(lParam);
			cxClient = LOWORD(lParam);
		}
			return 0;
		case WM_SETFOCUS:
			return 0;
		case WM_PAINT:
		{
			PAINTSTRUCT ps;
			HWND hwnd = GetEngine()->GetWindow();
			HDC hdc = BeginPaint(hwnd, &ps);
			//绘图
			//Paint(hdc);
			Draw(hdc, cxClient, cyClient);
			EndPaint(hwnd, &ps);
		}
			return 0;
		case WM_DESTROY:
			End();
			PostQuitMessage(0);
			return 0;
		}

		return DefWindowProc(hWnd, msg, wParam, lParam);

	}

3.在main.cpp中实现画一个哆啦A梦


void Draw(HDC hdc, int cxClient, int cyClient){
	
	TCHAR temp[128] = TEXT("I love fish.com!"), buff[128] = TEXT("");
	HBRUSH hOldBrush;
	RECT rect;
	HPEN hPen, hOldPen;
	POINT apt[128];




	HWND hwnd = vio::MyEngine::GetEngine()->GetWindow();
	GetClientRect(hwnd, &rect);
	//对齐
	SetTextAlign(hdc, TA_CENTER);
	TextOut(hdc, (rect.right - rect.left) / 2 + 150, (rect.bottom - rect.top) / 2, temp, wcslen(temp));


	//1 辅助线
	hPen = CreatePen(PS_DOT, 1, RGB(192, 192, 192));
	hOldPen = (HPEN)SelectObject(hdc, hPen);
	MoveToEx(hdc, cxClient / 2, 0, NULL);
	LineTo(hdc, cxClient / 2, cyClient);
	MoveToEx(hdc, 0, cyClient / 2, NULL);
	LineTo(hdc, cxClient, cyClient / 2);
	SelectObject(hdc, hOldPen);



	//2 头
	HBRUSH hBlueBrush = CreateSolidBrush(RGB(0, 159, 232));
	hOldBrush = (HBRUSH)SelectObject(hdc, hBlueBrush);
	Ellipse(hdc, cxClient / 2 - 120, cyClient / 2 - 200, cxClient / 2 + 120, cyClient / 2 + 40);
	SelectObject(hdc, hOldBrush);


	//3 脸
	Ellipse(hdc, cxClient / 2 - 100, cyClient / 2 - 160, cxClient / 2 + 100, cyClient / 2 + 40);

	//4 眼睛
	Ellipse(hdc, cxClient / 2 - 50, cyClient / 2 - 180, cxClient / 2, cyClient / 2 - 120);
	Ellipse(hdc, cxClient / 2, cyClient / 2 - 180, cxClient / 2 + 50, cyClient / 2 - 120);


	//5 眼珠
	hOldBrush = (HBRUSH)SelectObject(hdc, GetStockObject(BLACK_BRUSH));//预设的画刷和画笔也需要选回来!!!
	Ellipse(hdc, cxClient / 2 - 20, cyClient / 2 - 160, cxClient / 2 - 5, cyClient / 2 - 140);
	Ellipse(hdc, cxClient / 2 + 20, cyClient / 2 - 160, cxClient / 2 + 5, cyClient / 2 - 140);


	//6 眼睛里面的光
	SelectObject(hdc, GetStockObject(WHITE_BRUSH));//一个预设替换另一个预设,等下再把原画具选回来!!!
	Ellipse(hdc, cxClient / 2 - 15, cyClient / 2 - 155, cxClient / 2 - 10, cyClient / 2 - 145);
	Ellipse(hdc, cxClient / 2 + 15, cyClient / 2 - 155, cxClient / 2 + 10, cyClient / 2 - 145);
	SelectObject(hdc, hOldBrush);




	//7 鼻子
	HBRUSH hRedBrush = CreateSolidBrush(RGB(255, 0, 0));
	hOldBrush = (HBRUSH)SelectObject(hdc, hRedBrush);
	Ellipse(hdc, cxClient / 2 - 10, cyClient / 2 - 135, cxClient / 2 + 10, cyClient / 2 - 115);
	// 鼻子上的线
	MoveToEx(hdc, cxClient / 2, cyClient / 2 - 115, NULL);
	LineTo(hdc, cxClient / 2, cyClient / 2 - 30);
	SelectObject(hdc, hOldBrush);


	//8 嘴巴
	Arc(hdc, cxClient / 2 - 70, cyClient / 2 - 120, cxClient / 2 + 70, cyClient / 2 - 30,
		cxClient / 2 - 60, cyClient / 2 - 50, cxClient / 2 + 60, cyClient / 2 - 50);


	//9 胡子
	//左边胡子
	MoveToEx(hdc, cxClient / 2 - 70, cyClient / 2 - 115, NULL);
	LineTo(hdc, cxClient / 2 - 20, cyClient / 2 - 100);
	MoveToEx(hdc, cxClient / 2 - 80, cyClient / 2 - 85, NULL);
	LineTo(hdc, cxClient / 2 - 20, cyClient / 2 - 85);
	MoveToEx(hdc, cxClient / 2 - 70, cyClient / 2 - 55, NULL);
	LineTo(hdc, cxClient / 2 - 20, cyClient / 2 - 70);
	//右边胡子
	MoveToEx(hdc, cxClient / 2 + 70, cyClient / 2 - 115, NULL);
	LineTo(hdc, cxClient / 2 + 20, cyClient / 2 - 100);
	MoveToEx(hdc, cxClient / 2 + 80, cyClient / 2 - 85, NULL);
	LineTo(hdc, cxClient / 2 + 20, cyClient / 2 - 85);
	MoveToEx(hdc, cxClient / 2 + 70, cyClient / 2 - 55, NULL);
	LineTo(hdc, cxClient / 2 + 20, cyClient / 2 - 70);



	//10 身体的矩形
	hOldBrush = (HBRUSH)SelectObject(hdc, hBlueBrush);
	Rectangle(hdc, cxClient / 2 - 90, cyClient / 2, cxClient / 2 + 90, cyClient / 2 + 150);
	SelectObject(hdc, hOldBrush);

	//11 肚子的圆形
	Ellipse(hdc, cxClient / 2 - 70, cyClient / 2 - 20, cxClient / 2 + 70, cyClient / 2 + 120);
	// 覆盖脖子上的线
	hPen = CreatePen(PS_SOLID, 2, RGB(255, 255, 255));
	hOldPen = (HPEN)SelectObject(hdc, hPen);
	Arc(hdc, cxClient / 2 - 70, cyClient / 2 - 20, cxClient / 2 + 70, cyClient / 2 + 120,
		cxClient / 2 + 60, cyClient / 2 - 10, cxClient / 2 - 60, cyClient / 2 - 10);
	SelectObject(hdc, hOldPen);



	//12 项圈
	hOldBrush = (HBRUSH)SelectObject(hdc, hRedBrush);
	RoundRect(hdc, cxClient / 2 - 95, cyClient / 2 - 5, cxClient / 2 + 95, cyClient / 2 + 10,
		20, 20);
	SelectObject(hdc, hOldBrush);



	//13 铃铛
	HBRUSH hYellowBrush = CreateSolidBrush(RGB(255, 255, 0));
	hOldBrush = (HBRUSH)SelectObject(hdc, hYellowBrush);
	Ellipse(hdc, cxClient / 2 - 15, cyClient / 2, cxClient / 2 + 15, cyClient / 2 + 30);
	RoundRect(hdc, cxClient / 2 - 15, cyClient / 2 + 10, cxClient / 2 + 15, cyClient / 2 + 15,
		2, 2);
	SelectObject(hdc, hRedBrush);
	Ellipse(hdc, cxClient / 2 - 4, cyClient / 2 + 15, cxClient / 2 + 4, cyClient / 2 + 18);
	MoveToEx(hdc, cxClient / 2, cyClient / 2 + 16, NULL);
	LineTo(hdc, cxClient / 2, cyClient / 2 + 30);
	SelectObject(hdc, hOldBrush);


	//14 口袋
	Pie(hdc, cxClient / 2 - 50, cyClient / 2, cxClient / 2 + 50, cyClient / 2 + 100,
		cxClient / 2 - 50, cyClient / 2 + 50, cxClient / 2 + 50, cyClient / 2 + 50);


	//15裤裆
	Pie(hdc, cxClient / 2 - 20, cyClient / 2 + 130, cxClient / 2 + 20, cyClient / 2 + 170,
		cxClient / 2 + 20, cyClient / 2 + 150, cxClient / 2 - 20, cyClient / 2 + 150);
	//覆盖裤裆下的实线
	hPen = CreatePen(PS_SOLID, 2, RGB(255, 255, 255));
	hOldPen = (HPEN)SelectObject(hdc, hPen);
	MoveToEx(hdc, cxClient / 2 - 20, cyClient / 2 + 150, NULL);
	LineTo(hdc, cxClient / 2 + 20, cyClient / 2 + 150);
	SelectObject(hdc, hOldPen);


	//16 脚掌
	Ellipse(hdc, cxClient / 2 - 110, cyClient / 2 + 130, cxClient / 2 - 10, cyClient / 2 + 170);
	Ellipse(hdc, cxClient / 2 + 110, cyClient / 2 + 130, cxClient / 2 + 10, cyClient / 2 + 170);


	//17 手
	//左手
	hOldBrush = (HBRUSH)SelectObject(hdc, hBlueBrush);
	apt[0].x = cxClient / 2 - 90;
	apt[0].y = cyClient / 2 + 10;
	apt[1].x = cxClient / 2 - 130;
	apt[1].y = cyClient / 2 + 50;
	apt[2].x = cxClient / 2 - 110;
	apt[2].y = cyClient / 2 + 70;
	apt[3].x = cxClient / 2 - 90;
	apt[3].y = cyClient / 2 + 60;
	Polygon(hdc, apt, 4);
	SelectObject(hdc, hOldBrush);
	Ellipse(hdc, cxClient / 2 - 150, cyClient / 2 + 46, cxClient / 2 - 110, cyClient / 2 + 86);
	//右手
	hOldBrush = (HBRUSH)SelectObject(hdc, hBlueBrush);
	apt[0].x = cxClient / 2 + 90;
	apt[0].y = cyClient / 2 + 10;
	apt[1].x = cxClient / 2 + 130;
	apt[1].y = cyClient / 2 + 50;
	apt[2].x = cxClient / 2 + 110;
	apt[2].y = cyClient / 2 + 70;
	apt[3].x = cxClient / 2 + 90;
	apt[3].y = cyClient / 2 + 60;
	Polygon(hdc, apt, 4);
	SelectObject(hdc, hOldBrush);
	Ellipse(hdc, cxClient / 2 + 150, cyClient / 2 + 46, cxClient / 2 + 110, cyClient / 2 + 86);
	//覆盖手与身体之间的线
	hPen = CreatePen(PS_SOLID, 2, RGB(0, 159, 232));
	hOldPen = (HPEN)SelectObject(hdc, hPen);
	MoveToEx(hdc, cxClient / 2 - 90, cyClient / 2 + 10, NULL);
	LineTo(hdc, cxClient / 2 - 90, cyClient / 2 + 50);
	MoveToEx(hdc, cxClient / 2 + 90, cyClient / 2 + 10, NULL);
	LineTo(hdc, cxClient / 2 + 90, cyClient / 2 + 50);

	SelectObject(hdc, hOldBrush);



	//清理空间
	DeleteObject(hPen);
	DeleteObject(hBlueBrush);
	DeleteObject(hRedBrush);
	DeleteObject(hYellowBrush);

}

显示效果:
在这里插入图片描述

  • 上面这个绘制哆啦A梦的例子已经涵盖许多东西了。如何绘制图形,填充图形,字体和获取客户端窗口大小等。

3.常用绘图函数

参考:《Windows游戏编程》

猜你喜欢

转载自blog.csdn.net/weixin_41374099/article/details/88703412