【Windows编程】系列第五篇:GDI图形绘制

上两篇我们学习了文本字符输出以及Unicode编写程序,知道如何用常见Win32输出文本字符串,这一篇我们来学习Windows编程中另一个非常重要的部分GDI图形绘图。Windows的GDI函数包含数百个API可供我们使用,本篇把最常用的GDI绘图做一个讲解。GDI可以绘制点、直线曲线、填充封闭区域、位图以及文本,其中文本部分已经在上一篇中将了,请参考【Windows编程】系列第三篇:文本字符输出。

跟前面的GDI对象一样,本篇的这些绘图函数也必须要设备上下文句柄(HDC)作为函数参数,从前文我们知道,HDC可以在处理WM_PAINT的时候用BeginPaint函数获取,也可以从GetDC、GetWindowDC拿到。

既然是画图,就少不了颜色的描述,Windows中的颜色有几种表示,其中COLORREF在GDI绘制中用的最多,它实际上是一个无符号32为整型。其中红、绿、蓝各占一个字节,最高字节不使用,如下图所示:

该值可以用Windows提供的RGB宏来生成,Windows中RGB的定义为:

#define RGB(r,g,b) ((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)))

除此之外,Windows还有结构体RGBQUAD也表示颜色,这种一般用于位图结构信息中。

  • 画像素点

Windows提供了SetPixel和GetPixel函数来设定和获取像素点的颜色。函数原型为:

COLORREF SetPixel(HDC hdc, int X, int Y, COLORREF crColor);
COLORREF GetPixel(HDC hdc, int nXPos, int nYPos);

该函数并不常使用。

  • 画笔画刷

在图形绘制之前,可以创建画笔给后续的画图使用,创建画笔的API函数为:

HPEN CreatePen(int fnPenStyle, int nWidth, COLORREF crColor);
HBRUSH CreateSolidBrush(COLORREF crColor);
HBRUSH CreatePatternBrush(HBITMAP hbmp);
HBRUSH CreateHatchBrush(int fnStyle, COLORREF clrref);

它可以指定画笔风格,宽度和颜色。风格可以是实线、虚线、点虚线等,具体参考MSDN说明的各种类型。

  • 画线条

Windows提供的画线条函数有十几个,常用的直线绘制为LineTo,多条线段一般用Polyline、PolylineTo、PolyPolyine等,曲线可以画椭圆、椭圆弧、贝塞尔样条曲线。这些函数的原型请参考MSDN,后面我们将用实例来演示这些函数的用法。

  • 封闭区域填充

Windows的绘图如果是一个封闭区,则内部是可以填充的,当然如果你不显示填充,系统会用默认颜色来填,比如窗口背景色。我们也可以在绘制封闭图形之前创建画刷,如果把创建的画刷选入设备环境中,系统将用画刷填充内部区。常见的会封闭的绘图API函数有画直角矩形Rectangle、圆角矩形RoundRect、椭圆Ellipse、扇形图Pie以及弦割图Chord。

  • 位图输出

Windows关于位图的输出内容很多,包括设备相关和设备无关位图、以及位块转移、透明、缩放等等,本文仅针对位图画刷进行实例演示,其他内容将来可单独写一篇介绍。用位图做画刷时先要使用LoadImage函数加载位图文件,然后用CreatePatternBrush创建一个模式画刷即可。

  • 文本输出

这个在前面已经讨论过了,请参考【Windows编程】系列第三篇:文本字符输出一文。

  • 绘图属性

在绘制图形时,环境设备有5个属性会影响大多数绘图:

画笔位置:在画线条时,会从画笔所在的位置开始画,画笔位置可以用MoveToEx函数来设置。

画笔:绘图时会采用当前环境中的画笔进行绘制,如果显示不创建,将会用系统默认的画笔。

背景:某些GDI会有透明和不透明的设置。

背景颜色:比如文本输出的间隙颜色。

绘制模式:比如划线是可以设置实线、虚线等,填充时可能有不同的填充绘制模式。

下面我们通过一个完整的实例,来演示上面这些常见函数的具体运用以及实际使用效果。

#include <windows.h>

static TCHAR szAppName[] = TEXT("GDI Test");
static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
     HWND     hWnd;
     MSG      msg;
     WNDCLASS wndclass;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW;
     wndclass.lpfnWndProc   = WndProc;
     wndclass.cbClsExtra    = 0;
     wndclass.cbWndExtra    = 0;
     wndclass.hInstance     = hInstance;
     wndclass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
     wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);
     wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
     wndclass.lpszMenuName  = NULL;
     wndclass.lpszClassName = szAppName;

     if (!RegisterClass(&wndclass))
     {
          MessageBox (NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
          return 0;
     }
     
     hWnd = CreateWindow(szAppName,            // window class name
                          szAppName,           // window caption
                          WS_OVERLAPPEDWINDOW, // window style
                          CW_USEDEFAULT,       // initial x position
                          CW_USEDEFAULT,       // initial y position
                          400,                 // initial x size
                          300,                 // initial y size
                          NULL,                // parent window handle
                          NULL,                // window menu handle
                          hInstance,           // program instance handle
                          NULL);               // creation parameters
     
     ShowWindow(hWnd, iCmdShow);
     UpdateWindow(hWnd);
     
     while (GetMessage(&msg, NULL, 0, 0))
     {
          TranslateMessage(&msg);
          DispatchMessage(&msg);
     }

     return msg.wParam;
}

//绘制指定属性的直线
static void DrawLine(HDC hDC, int x0, int y0, int x1, int y1, int style, int width, COLORREF color)
{
	HPEN hPen = CreatePen(style, width, color);
	HPEN hOldPen = (HPEN)SelectObject(hDC, hPen);

	MoveToEx(hDC, x0, y0, NULL);
	LineTo(hDC, x1, y1);

	SelectObject(hDC, hOldPen);
	DeleteObject(hPen);
}

//绘制实心圆
static void DrawCircle(HDC hDC, int x, int y, int len, COLORREF color)
{
	HBRUSH hBrush = CreateSolidBrush(color);
	HBRUSH hOldBrush = (HBRUSH)SelectObject(hDC, hBrush);

	HPEN hPen = CreatePen(PS_SOLID, 1, color);
	HPEN hOldPen = (HPEN)SelectObject(hDC, hPen);

	Ellipse(hDC, x-len/2, y-len/2, x+len/2, y+len/2);

	SelectObject(hDC, hOldBrush);
	DeleteObject(hPen);

	SelectObject(hDC, hOldPen);
	DeleteObject(hOldBrush);
}

//绘制填充矩形
static void DrawRect(HDC hDC, int left, int top, int width, int height, int style, COLORREF color)
{
	HBRUSH hBrush = CreateHatchBrush(style, color);
	HBRUSH hOldBrush = (HBRUSH)SelectObject(hDC, hBrush);

	Rectangle(hDC, left, top, left+width, top+height);
	
	SelectObject(hDC, hOldBrush);
	DeleteObject(hOldBrush);
}

//绘制位图填充矩形
static void DrawBmpRect(HDC hDC, int left, int top, int width, int height, LPCTSTR file)
{
	HBITMAP hBitmap = (HBITMAP)LoadImage(NULL, file, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION);
	HBRUSH hBrush = CreatePatternBrush(hBitmap);
	HBRUSH hOldBrush = (HBRUSH)SelectObject(hDC, hBrush);

	Rectangle(hDC, left, top, left+width, top+height);

	SelectObject(hDC, hOldBrush);
	DeleteObject(hOldBrush);
	DeleteObject(hBitmap);
}

static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC         hDC;
	PAINTSTRUCT ps;

	switch (message)
	{
	case WM_CREATE:
		return 0;

	case WM_PAINT:
		{
			hDC = BeginPaint(hWnd, &ps);
			for (int i=10; i<50; i+=4)
			{
				SetPixel(hDC, i, 10, RGB(0, 0, 0)); //绘制像素点
			}
                        //绘制不同模式的直线
			DrawLine(hDC, 120, 30, 200, 30, PS_SOLID, 2, RGB(0,0,0));
			DrawLine(hDC, 120, 50, 200, 50, PS_DASH, 1, RGB(100,0,200));
			DrawLine(hDC, 120, 70, 200, 70, PS_DASHDOT, 1, RGB(100,250,100));
                        //绘制弧线、弦割线、饼图
			Arc(hDC, 10, 30, 40, 50, 40, 30, 10, 40);
			Chord(hDC, 10, 60, 40, 80, 40, 60, 10, 70);
			Pie(hDC, 10, 90, 40, 110, 40, 90, 10, 100);

			POINT pt[4] = {{90,130},{60,40},{140,150},{160,80}};
                        //绘制椭圆、矩形
			Ellipse(hDC,pt[0].x, pt[0].y, pt[1].x, pt[1].y);
			Rectangle(hDC, pt[2].x, pt[2].y, pt[3].x, pt[3].y);

                        //绘制贝塞尔曲线
			PolyBezier(hDC, pt, 4);
                        //标出贝塞尔曲线的四个锚点
			DrawCircle(hDC, pt[0].x, pt[0].y, 8, RGB(0,255,0));
			DrawCircle(hDC, pt[1].x, pt[1].y, 8, RGB(0,0,255));
			DrawCircle(hDC, pt[2].x, pt[2].y, 8, RGB(0,0,0));
			DrawCircle(hDC, pt[3].x, pt[3].y, 8, RGB(255,0,0));
                        //绘制圆
			DrawCircle(hDC, 100, 180, 60, RGB(0, 250, 250));
                        //绘制不同填充模式的矩形
			DrawRect(hDC, 220, 20, 60, 40, HS_BDIAGONAL, RGB(255,0,0));
			DrawRect(hDC, 220, 80, 60, 40, HS_CROSS, RGB(0,255,0));
			DrawRect(hDC, 290, 20, 60, 40, HS_DIAGCROSS, RGB(0,0,255));
			DrawRect(hDC, 290, 80, 60, 40, HS_VERTICAL, RGB(0,0,0));
                        //绘制位图
			DrawBmpRect(hDC, 180, 140, 180, 100, TEXT("chenggong.bmp"));
                        //绘制文本
			TextOut(hDC, 20, 220, TEXT("GDI画图输出测试程序"), 11);
		}
		EndPaint(hWnd, &ps);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0 ;
	}

	return DefWindowProc (hWnd, message, wParam, lParam);
}

本实例运行结果如下图所示,图中可以看到线条不平滑,这是因为Win32的画图函数是没有抗锯齿功能的,图越小,锯齿越明显。可以使用微软提供的GDI+绘图函数,具有抗锯齿效果。

Windows的GDI基本绘制其实并不难掌握,只要仔细阅读MSDN上API的详细使用说明就一定能正确使用,但是在创建GDI对象并使用后,一定要记得释放。

原文发布于微信公众号 - 程序员互动联盟(coder_online)

原文发表时间:2015-07-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏转载gongluck的CSDN博客

自制截图工具V1.0

之前比较空闲,就尝试着做了个截图工具,功能还算实用。等以后有时间再研究V2.0。 这个项目都是用C语言做的,所以还算精简。 #include <windows....

3408
来自专栏拂晓风起

结合JSFL/actionscript 实现轮廓动画

925
来自专栏ASP.NET MVC5 后台权限管理系统

ASP.NET MVC5+EF6+EasyUI 后台管理系统(75)-微信公众平台开发-用户管理

前言 本节主要是关注者(即用户)和用户组的管理,微信公众号提供了用户和用户组的管理,我们可以在微信公众号官方里面进行操作,添加备注和标签,以及移动用户组别,同...

3546
来自专栏开发与安全

数据结构:图的存储结构之邻接矩阵

图的邻接矩阵(Adjacency Matrix)存储方式是用两个数组来表示图。一个一维的数组存储图中顶点信息,一个二维数组(称为邻接矩阵)存储图中的边或弧的信息...

2338
来自专栏后端之路

来自Google的瓜娃简介之Lists

注:以下分析均基于Guava18 背景 在很久很久以前(大概就是jdk7之前吧)没有钻石语法的我们苦逼的一遍又一遍的写着泛型 List<String> lis...

2215
来自专栏人工智能LeadAI

实时Android语音对讲系统架构

本文属于Android局域网内的语音对讲项目(https://github.com/yhthu/intercom)系列,《通过UDP广播实现Android局域网...

5754
来自专栏拂晓风起

Stage3d AGAL GPU处理照片 旧照片效果 sepia || pixelbender

883
来自专栏我的博客

左右滚动,带控制按钮

今天需要一个左右滚动图的js,从网上着了半天,修改调试了半天才弄好,于是就收藏了。不过以后真得看看js了 关键代码有注释:(红色部分是我加的注释) <table...

2486
来自专栏菩提树下的杨过

Flash/Flex学习笔记(46):正向运动学

所谓"正向运动学"通俗点讲就是把几个连接部件的一端固定起来,另一个端可以自由(向前/向外)运动。比如人的行走,单个下肢可以理解为脚连接小腿,小腿连接大腿,大腿连...

1946
来自专栏函数式编程语言及工具

Akka(25): Stream:对接外部系统-Integration

   在现实应用中akka-stream往往需要集成其它的外部系统形成完整的应用。这些外部系统可能是akka系列系统或者其它类型的系统。所以,akka-stre...

3007

扫描关注云+社区