Transparent Bitmaps on Buttons and other Controls
January 26, 2012 •
Overlayed bitmaps in FoxPro have always been a royal pain, but in order to make a UI that looks clean, getting some sort of transparency to work with FoxPro controls is pretty important. Although FoxPro supports most common image formats including GIF and PNG that support transparency, the transparency isn't supported everywhere within the product.
Specifically the FoxPro Image control supports transparency of the various transparent image formats. If you load up an image control with a GIF or PNG file, the transparency is preserved and the image displays correctly.
However, displaying transparent images on other controls that have a Picture property, unfortunately doesn't work as smoothly. Specifically, here's an example of what I've been struggling with, which is buttons with associated icons:
The first button doesn't show transparency and looks terrible, while the second button properly shows transparency and appears like it should using one of the approaches mentioned below. Similar situation arises with other controls that have picture properties like OptionButton, Checkboxes. ComboBoxes and Listboxes as well, although it's less of a problem there because the background of lists tend to be white. As a matter of fact all container controls have a picture property and the same rules apply.
By default only the Image control supports transparency properly for all GDI+ image formats.
The good news is that with a bit of trickery you can get FoxPro to render transparent images. The bad news is that there are tradeoffs and extra work required to make it work properly.
Old School Transparency Support: BMP Image
GDI+ image support for non-BMP images is actually a relatively new feature in FoxPro. GDI+ support for graphics was introduced with Visual FoxPro 8. Prior to version 8 only BMP images were directly supported in the product and there are a couple of mechanisms used for handling transparency with BMPs.
The most reliable way to get transparency in FoxPro involves using BMP files and a matching BMP Mask file. The mask file basically holds black dots for each of the pixels that should display and white content for anything that should be transparent. As you might imagine creating MASK files is a pain and adds clutter to your image management - anytime the image changes the mask has to be changed too and keeping things in sync is terrible. To me this has always been a non-starter.
You can also get transparency support with only BMP images. By default BMP images display white pixels as transparent, so as long as you can easily represent your transparent content as white it's easy to do. This is not always so cut and dried however because the image 'content' may also contain white pixels. In this case you can fill the white pixels with a slightly off white color like RGB(254,254,254).
Both of these approaches are painful but they are very reliable. Once you got your image set up it always works without fail. But both approaches require that you at the very least convert them to BMP format and potentially tweak your images for transparency or by creating a mask.
Resource Loading in FoxPro
There's another approach that works in most situations because of a quirk in FoxPro that I just found out about last week. FoxPro loads images as resources and you can trick FoxPro by loading images with transparency by first loading them into an Image control which as mentioned earlier does support transparency. The trick is that you have to load the image into the image control BEFORE it gets loaded into a picture property of another control. The quirk is that FoxPro caches image data once loaded from a path, so loading it into an image control first caches the transparent image which then gets loaded in subsequent loads that reference the same disk image (or compiled in image resource).
The image control has to only be loaded up and the control can then be released. The key is that this 'pre-load' has to happen BEFORE you load the same image into other controls. The image control doesn't need to stick around, so I use a small function that I only need to pass a file path to, create the Image control and set the picture property:
************************************************************************ * LoadImageResource **************************************** *** Function: Pre-load an image file so transparency is respected *** Assume: *** Pass: *** Return: ************************************************************************ FUNCTION LoadImageResource(lcFile) LOCAL loImg as Image loImg = CREATEOBJECT("Image") loImg.Picture = FULLPATH("bmps\search_small.png") ENDFUNC * LoadImageResource
When the function exits the Image control is released, but since the image was effectively loaded into the control the image is now cached inside of VFP's image cache. Now when you load an image that has been called with the function into a Picture property of another control like a button it will display GIF and PNG transparency properly.
In an application I tend to have quite a few image resources I need to load up, so centralize one place when I do all the image pre-loading during startup. I create a function that I call from the application's startup, typically right after I display a startup splash screen.
The method is simply a conglomeration of LoadImageResource() calls:
************************************************************************ * LoadImageResources **************************************** *** Function: Method is used to load up transparent images into *** UI. This method should be called on startup *** Assume: *** Pass: *** Return: ************************************************************************ FUNCTION LoadImageResources() LOCAL loImg as Image LoadImageResource("bmps\search_small.png") LoadImageResource("bmps\zipfile.gif") … ENDFUNC * LoadPictureResources
This is a simple approach that works fine and allows using PNG and GIF images with transparency on buttons and other controls.
Caveats with Preloading
In some instances however you can still end up with non-transparent resources. Basically FoxPro caches resources internally, but there are several ways that resources can get un-cached. Explicitly, if your code calls the CLEAR RESOURCES command anywhere, resources will get released and you'll lose any cached resources at that point. If you go back to a form with a transparent image on a control the image will display again with opaque background. Note that CLEAR RESOURCES is not affected by commands like CLEAR ALL or RELEASE ALL.
Implicitly, FoxPro can on certain occasions also unload its image resources internally when memory is really low. This should be very rare but it's possible and I have seen occasions when this does occur. But it's really rare so it's probably OK to ignore that possibility. If you're concerned about this scenario you can either have some code that calls LoadImageResources() or something like it during certain key application points that fire occasionally to explicitly force reloading of the images, or you can load resources as needed everytime a given form is fired up. This will ensure that resources are always fresh, but keep in mind that this causes some extra overhead.
Summary
I sure wish this was easier to accomplish or more obvious. For the longest time I didn't even know this hack of image pre-loading. The safest solution is to use BMP files, but it's definitely way more convenient to use Web ready files like PNGs or GIFs that support transparency both for designing the icons and re-using them across applications and platforms (Web/Desktop as I do).
Having to pre-load resources for transparent images in this fashion is a big hassle as opposed just loading up image resources directly. And worse it's an ugly hack that's impossible to discover on your own. I found a vague reference to this on a message board message somewhere so it's even hard to search for. Hopefully this blog entry will help clarfiy the subject a little better, even if it's just for me to remember in the future.
What really sucks about this problem is the fact that it DOES works with preloaded image resources. This seems to suggest that transparency actually works on controls but there's a small implementation detail in Visual FoxPro that prevents this from working all the time. It seems this would have been such a minor thing for the Fox team to fix and get working out of the box if the resources to do so had been there. But alas, we're stuck with a few minor and undiscoverable hacks. I take a hack over it not working at all which was what I thought until a week ago.
Thanks to Cesar Chalom, Joel Leach, Dragan Nedeljkovich for providing some ideas for this post and Mike Lewis for providing the inspiration to look further into this and getting me to a working solution in Html Help Builder.
Luis Maria Guayan
January 27, 2012