Modern applications often have a customized, "skinned" look. There are a lot of tools, libraries and tutorials on creating skinned apps for desktop Windows systems. Unfortunately this is not the case with WinCE or PocketPC. If you try to adapt ideas or code from Win32 programs, you will experience a lot of challenges, just as I had while creating the Skinnable Dialogs Framework
. A very basic feature of a skinnable application is to have custom-shaped controls or main windows. Creating non-rectangular windows is a relatively easy task in the Win32 world. All you have to do is, create a region based on a bitmap mask and set it for your window with the
Creating a region is the first step towards a custom-shaped window.
Every region creator that I've seen so far uses the same idea:
- uses a bitmap mask to define the visible and transparent regions of a window
- calls the
GetPixel funcion on the bitmap mask to see which areas of the window should be transparent
- then calls
CreateRectRgn to create new rectangular regions based on pixels or pixel blocks
- and finally combines these smaller regions into the final, custom-shaped region with the
But have you tried this approach on PocketPC or WinCE? Well I have, and the results were very disappointing. The smiley bitmap above was parsed for several minutes!
I found that region functions work well on WinCE only if you want to combine a couple of rectangular regions. But when it comes to hundreds of small regions (just as with a bitmap mask),
becomes unacceptably slow.
In this article I'd like to present a much faster solution for custom region creation using the
API. The code works fine on WinCE/PocketPC and also on Win32. I've tested it with Embedded Visual C++ 4 and Visual Studio 2005, too.
CRegionBuilder class (see files
RegionBuilder.cpp) has only one public function,
BuildRegion. It uses the
ExtCreateRegion API to create a region from manually built region data. The region data consists of a header (a
RGNDATAHEADER structure) and an array of
RECT structures that make up the region.
BuildRegion function takes two parameters, a bitmap handle of a loaded bitmap and a pointer to store the resulting region handle:
RegionBuilderError BuildRegion(HBITMAP hBmp, HRGN *pDest);
The function includes every non-black pixel in the resulting region.
Possible return values are:
rbeGDIError as defined in
RegionBuilder.h. If memory allocation fails, it returns
rbeNoMem. If any of the used GDI functions return an error, it returns
rbeGDIError. If all goes fine, the function return
rbeOK and the resulting region handle will be copied to the destination.
- Do not call
DeleteObject on the region handle until your window is visible, instead, free it in
- Drawing custom-shaped windows on CE devices is considerably slower than drawing regular windows, so don't expect hyper-performance.
- If you use a very complicated bitmap mask that would result in a thousands of region
RECTs (eg. 640x480 "random noise"), you might experience sudden device crashes or other drawing problems.
How it works
BuildRegion function first gets bitmap dimensions with the GDI
GetObject function. To avoid the slow
GetPixel function, it reads bitmap bits directly. But the
GetObject function does not return a pointer to the bitmap bits unless the bitmap was created with
BuildRegion creates a new, monochrome bitmap using this function and then copies the source bitmap to it with
BitBlt. Using a monochrome version of the bitmap saves a lot of precious memory.
After the monochrome copy has been created, the function loops through the bitmap bits to see how much memory will be required for the
RECT array. If there are horizontal lines in the bitmap, they will be packed into one
Then the function allocates a proper memory block for the rectangles and loops through the bits again to build the
It loops twice through the bitmap, so it allocates only the required amount of memory. Despite this it's still pretty fast I think. A lot faster than the ordinary