GDI对象的用户模式数据结构
每个GDI对象在全局GDI对象句柄表中都有入口,这个表有个pUser的指针。对于大多数GDI对象来说,pUser是NULL(空)指针。但对于
画刷、区域、字体和设备上下文GDI对象,GDI对象表中相应的pUser字段不空。它们确实指向一些用户模式地址空间的数据结构。
(1)用户模式画刷数据:纯色画刷优化:
对于纯色画刷pUser指向一个24字节的块,其中前12字节是LOGBRUSH结构的拷贝。如果画刷是纯色画刷,唯一重要的信息是画刷的颜
色。对其他类型的画刷,pUser是空指针。
typedef struct
{
LOGBRUSH logbrush;
DWORD dwUnused[3];
}USerData_SolidBrush;
纯色画刷很特别,因为它们量多,但生存期很短。实现渐进填充、阴影或光线效果时,会生成成百上千的纯色画刷,使用一次或者几
次来绘制图片的切片,然后立该删除。因为它们的需要量很大,应用程序不能把它们保存到下一次需要的时候。否则很容易超过GDI
对象表的限制。所以大多数纯色画刷在使用一次后不久就被销毁,并在需要的时候再创建。
通过在用户模式保存一份LOGBRUSH的拷贝,GDI也许能对大量创建、使用和删除纯色画刷的序列模式进行优化。第一个纯色画刷删除
之后,GDI可以把它保存在内部,而不是释放分配好的数据结构。需要新纯色画刷时,GDI能够通过改变颜色创建新画刷来重用它,而
不必回到内核模式分配一块内存再把新画刷的信息加进去。
(2)用户模式区域数据:正方形区域优化:
对于由CreateRectRgn和ExtCreateRgn这样的函数创建的区域对象来说,pUser字段仅在最简单的情况——矩形区域下使用,这和纯色
画刷类似。由pUser指向的数据块是大小为24字节的矩形框。在数据块内部,前两个DWORD的意义未知,剩下16字节构成了RECT结构
typedef struct
{
DWORD dwUnknown1;
DWORD dwUnknown2;
RECT rcBound;
}UserData_RectRgn;
你可以用SetRectRgn通过指定的坐标把区域变成矩形区域。知道pUser字段指向的数据结构,就知道这在GDI中很容易实现的,GDI只
需要保证pUser字段非空,即UserData_TectRgn结构被分配,然后设置正确的内部坐标。因此作为GDI对象的区域就是可变的对象。
(3)用户模式字体数据:宽度表
Windows GDI为字体定义的数据结构是所有GDI对象最多的。有LOGFONT、TEXTMETERIC、PANOSE、ABC和GLYPHSET等等。但是我们在GDI
对象表中没有发现这些数据结构的行踪,字体句柄的用户模式数据结构非常简单:
typedef struct
{
DWORD dwUnknown;
void * pCharWidthData;
}UserData_Font;
UserData_Font的第一个字段总是零。第二个字段也经常是零,只有调用GetCharWidth这样的函数之后除外。GetCharWidth用于将某
个范围内的字符宽度来填充一个数组。调用GetCharWidth之后,pCharWidthData指向从系统堆分配的数据结构,它基本上是一个缓存
的字符宽度表。这里我们又看到GDI费很大力气优化性能。
(4)用户模式设备上下文数据:存储设置信息
在用Windows GDI绘制之前,首先要得到设备上下文的句柄。可以自己创建这个句柄,也可以从OS中得到。也可设置并得到设备上下
文的24个属性,如映射模式、文本前景色、背景色、画刷、画笔和字体常用设备上下文设置,因此GDI很自然地需要一个结构存储各
种各样的与设备上下文有关的设置。在Windows NT/2000下,GDI对象表中的pUser字段指向这样一个设备上下文句柄结构。
通过改变设备上下文的设置,并监视二进制数据的变化这一枯燥乏味的过程后,就得到了pUser指向的设备上下文结构的粗略图景。
但是这些信息还是不完全、非正式的。借助微软提供的内核级GDI调试器扩展和WinDbg,这幅图就更完全、更正式了。
(*对于这个数据结构,DC_ATTR结构书写了很大篇幅,没太看懂,也就不写了,以免误人子弟,总之也是为了提高GDI性能也设计的*
)
总结一下,本节我们看了纯色画刷、矩形、区域、字体和设备上下文GDI对象的用户模式可存取数据结构,这些数据结构十分简单,
大部分是通过减少GDI在用户内核模式执行特权之间的切换来优化GDI性能。