windows Windows客户端开发3——GDI(1)

 

对于Windows开发而言,界面绘制使用的一类API就是所谓的“GDI”。...

 关于作者 


赵科进

16295620@qq.com

爱奇艺资深工程师,曾任职于沪江网和PPTV,资深windows开发人士。
对于Windows开发而言,界面绘制使用的一类API就是所谓的“GDI”。

GDI这个东西可有历史了,但是我们就不去追根朔源了。首先,我不能免俗的要先介绍一下它的全称:“Graphic Device Interface”,即“图形设备接口”,从这个名称我们可以大略吸收到的信息就是:GDI是个跟图形绘制有关的接口,对的,正是这样!

先让我们来看看GDI函数大致包括了哪些内容,哪些分类,因为我的这系列文章偏实用而且希望能给想进入Windows开发的程序员们提供一些捷径,所以我不做过多的理论陈述,而是打算重点介绍API的使用方法以及一些基本功能应该如何实现,当然还包括在实际开发过程中可能会遇到的坑,如何避免这些坑,还有如何提高绘制效率,如何让界面的各种行为变的流畅。

首先来介绍一下:“DC”,DC就是“设备上下文”,初学者不要去硬抠这个词的字面含义作何解释,你需要知道的是所谓设备上下文实际是一种抽象,那么它抽象出了什么呢?答案是:设备上下文抽象出了绘制设备,包括打印机,显示器等等,对于我们Windows开发人员而言,我们不需要去关注特定的绘制设备如何通信,如何调用最底层的接口来复杂的在设备上进行绘制,有了设备上下文,一切都变的透明,高效,简单。当然,在GDI上一层还有更高层的抽象,绘制更为简单,但是对于我们Windows C++开发人员而言,那不是我们立刻需要知道的东西。我们所有对绘制设备的绘制行为,都是通过设备上下文进行的。

DC在Windows程序中的表现形式,是一个被称作“设备上下文句柄”的东西,也即HDC,HDC在代码中是一种数据类型,它是一个32位的值。我们可以通过创建一个设备上下文句柄,也即HDC类型的对象来获取对绘制设备的抽象,之后所有的绘制操作都是施加于这个HDC之上,也就是对应的设备上。也就是说,如果你的HDC代表你的当前显示区域,那么调用一个GDI函数画一条线,这条线就会出现在你的屏幕上。而如果HDC代表的是一台打印机,这条线就会绘制到打印机上,这样讲是否明白了呢?我用两段文字来解释DC和HDC,是因为这是GDI绘制图形的基础,如果没有DC和HDC,GDI的一切行为都无从谈起。

在介绍完设备上下文这个基础以后,我要讲讲GDI对象,就是说什么是GDI对象?GDI对象大概有哪些类型?每一种类型的GDI对象是做什么用的?

我这篇文章并不想写的大而全,因而我只罗列一下常见的GDI对象,比如:

Bitmap

位图对象,表征了一副位图,在内存中的组织实际是一个二进制矩阵,标识了位图每个点的位置以及相应的色彩。代码中位图用HBITMAP,即位图句柄来表示。

Font

字体对象,代表了设备上下文中的字体属性,什么字体?多大的字体?是否带下划线?是否粗体“。代码中用HFONT,字体句柄表示。

Brush

画刷对象,代表了图形的填充色,代码中用HBRUSH表示。

Pen

画笔对象,代表了线条绘制的颜色以及字体的颜色,用HPEN表示。

我们可以看出,所谓的GDI对象,实际上是设备上下文的一些属性或者说参数,它们决定了最终的绘制效果。

在介绍了这些基础概念之后,下面我就要正式开始有点实际的东西了,我会教大家一些GDI的基本功能代码编写,基本的技巧,当然还有如何避免基本的坑,哈哈,对的,基本的坑而不是高级的。

那么我要如何说起呢,首先我要告诉大家如何创建DC,如何使用DC,如何设置DC的属性(也就是GDI对象),如何在DC上绘制简单的文字,绘制图形,绘制图片也就是位图。我还会附带的说说GDI+,说说CImage这个绘图超级方便的类(我在我过去的项目中大量的使用了这个类),还要说说圆角窗口的实现,包括用蒙板色的办法和用层叠窗口的办法。当然这么多的内容在这样一篇文章里肯定是写不完的,所以我将在后面的文章里逐一介绍,有兴趣的朋友可以等我一篇一篇婉婉道来。

首先来谈谈DC的使用

获取DC的方式有好几种,一般而言有如下几种

CreateDC

HDC CreateDC(LPCTSTR lpszDriver, LPCTSTR lpszDevice, LPCTSTR lpszOutput,constDEVMODE* lpInitData)

这个函数的第一个参数lpszDriver,表示你要创建DC的设备,也就是表示你想进行抽象的设备,一般而言可以是显示器或者打印机,因为用于打印的情况比较少,我们这里只讨论“DISPLAY”这个参数的情况。

当第一个参数为“DISPLAY”时,lpszDevice决定了你要在哪一个显示设备上进行绘制,如果你要在主显示设备上进行绘制,可以这样调用CreateDC

CreateDC(TEXT("DISPLAY"),NULL,NULL,NULL)

这个函数的HDC类型返回值,就代表了这个显示设备,有了这个HDC,我们就可以在屏幕上进行绘制了。

GetDC

GetDC是基于窗口的,它只有一个HWND类型参数,代表了你想进行绘制的窗口,因为它的用法比较简单,我这里不做多说

说到这里,是不是觉得这两个函数还挺简单,但是在使用CreateDC和GetDC创建的DC之后,GDI的对象和资源在使用完毕后都是需要释放的,这里有个常见的坑,我在网上很多的技术博客上看到不少作者都犯了这个错误。

释放DC有两种函数:DeleteDC,ReleaseDC。那么它二者有什么区别呢?查阅MSDN对二者的Remark可知:

An application must not delete a DC whose handle was obtained bycalling the GetDC function. Instead, it mustcall the ReleaseDC function to free the DC.

An applicationcannot use the ReleaseDC functionto release a DC that was created by calling the CreateDC function; instead, it must usethe DeleteDC function. ReleaseDC mustbe called from the same thread that called GetDC.

换句话说:CreateDC与DeleteDC配对使用,而GetDC与ReleaseDC配对使用,二者不可替换,用错了释放方法,这是初学者,甚至很多有经验的老鸟都会犯的错误。而且在最后,还需要有一点要注意的,RelaseDC必须在与GetDC相同的线程中进行调用。

看到这里,大家可能觉得真的细节还是不少的,但这些正是区分一个Windows开发老手和菜鸟的标志,所以对于我们这些Windows开发人员而言,需要注意得到地方的确很多,我们必须一丝不苟的抠每一个API的参数,含义以及坑,这大概也是一种乐趣吧。



未来CTO

关注我

CTO之路从此开始

微信号:wlaicto


    关注 未来CTO


微信扫一扫关注公众号

0 个评论

要回复文章请先登录注册