WinCE/PocketPC 快速区域创建
一种快速创建自定义形状窗口的解决方案,使用位图蒙版。

引言
现代应用程序通常具有自定义的“皮肤化”外观。有许多工具、库和教程可以用于创建桌面 Windows 系统的皮肤化应用程序。不幸的是,WinCE 或 PocketPC 的情况并非如此。如果您尝试改编 Win32 程序的想法或代码,您将遇到很多挑战,就像我在创建 Skinnable Dialogs Framework 时遇到的那样。皮肤化应用程序的一个基本功能是拥有自定义形状的控件或主窗口。在 Win32 世界中创建非矩形窗口是一项相对容易的任务。您所要做的就是基于位图蒙版创建一个区域,并使用SetWindowRgn
API 为您的窗口设置它。创建区域是创建自定义形状窗口的第一步。
我到目前为止看到的每一个区域创建器都使用了相同的想法
- 使用位图蒙版来定义窗口的可见和透明区域
- 在位图蒙版上调用
GetPixel
函数,以查看窗口的哪些区域应该是透明的 - 然后调用
CreateRectRgn
基于像素或像素块创建新的矩形区域 - 最后使用
CombineRgn
函数将这些小区域组合成最终的自定义形状区域。
我发现,区域函数在 WinCE 上只有在您想要组合几个矩形区域时才有效。但是,当涉及到数百个小区域(就像使用位图蒙版时)时,
CombineRgn
会变得慢得无法接受。在本文中,我想介绍一种使用
ExtCreateRegion
API 进行自定义区域创建的更快解决方案。该代码在 WinCE/PocketPC 和 Win32 上都能正常工作。我也用 Embedded Visual C++ 4 和 Visual Studio 2005 对其进行了测试。解决方案
CRegionBuilder
类(请参阅文件 RegionBuilder.h
和 RegionBuilder.cpp
)只有一个公共函数 BuildRegion
。它使用 ExtCreateRegion
API 从手动构建的区域数据创建区域。区域数据包括一个头(RGNDATAHEADER
结构)和一个 RECT
结构数组,这些结构构成了区域。BuildRegion
函数接受两个参数:一个已加载位图的位图句柄和一个用于存储结果区域句柄的指针。
RegionBuilderError BuildRegion(HBITMAP hBmp, HRGN *pDest);
该函数包含结果区域中的所有非黑色像素。
可能的返回值是:rbeOK
、rbeNoMem
和 rbeGDIError
,如 RegionBuilder.h
中定义。如果内存分配失败,它将返回 rbeNoMem
。如果任何使用的 GDI 函数返回错误,它将返回 rbeGDIError
。如果一切顺利,函数将返回 rbeOK
,并且结果区域句柄将被复制到目标。
一些建议
- 在窗口可见之前,请勿对区域句柄调用
DeleteObject
,而应在OnDestroy
中释放它。 - 在 CE 设备上绘制自定义形状窗口比绘制常规窗口慢得多,因此不要期望超高性能。
- 如果您使用非常复杂的位图蒙版,会导致生成数千个区域
RECT
(例如,640x480 的“随机噪声”),您可能会遇到设备突然崩溃或其他绘图问题。
工作原理
BuildRegion
函数首先使用 GDI GetObject
函数获取位图尺寸。为了避免缓慢的 GetPixel
函数,它直接读取位图的位。但是,除非位图是用 CreateDIBSection
创建的,否则 GetObject
函数不会返回指向位图位的指针。因此,BuildRegion
使用此函数创建一个新的单色位图,然后使用 BitBlt
将源位图复制到其中。使用单色版本的位图可以节省大量宝贵的内存。
创建单色副本后,该函数会遍历位图位以查看 RECT
数组需要多少内存。如果位图中存在水平线,它们将被打包到一个 RECT
中。
然后,该函数为矩形分配适当的内存块,并再次遍历位图以构建 RECT
数组。
它会遍历位图两次,因此只分配了所需的内存量。尽管如此,我认为它仍然相当快。比普通的 GetPixel
+ CombineRgn
方法快得多。
玩得开心!