Perl/Tk Transparent Icons
How to create transparent window icons for Perl/Tk.

Perl/Tk Transparent Icons

Update (2007-12-11): I've created a web-based XBM mask generator tool. Use it to create XBM masks.

A problem that a lot of programmers run into after working with Perl/Tk programs for a while is that it's not very easy to create a non-square icon for our windows. In other words, having a transparent window icon is not an easy task to accomplish.

First of all, we can't very easily use Windows .ico files for our window's icons. No, our icons have to be a regular image of some format, such as a PNG or a GIF image. And the typical way to create a custom icon for your window is:

Code:
use Tk;
use Tk::PNG;

my $mw = MainWindow->new();
my $icon = $mw->Photo (
   -file   => 'my_icon.png',
   -format => 'PNG',
);
$mw->Icon (-image => $icon);

MainLoop;
It doesn't take a Perl hacker very long to figure out the weakness with that: all icons have to be solid squares. The first instinct is to try and use an image with transparent pixels in it. In our example, we're going to use a Firefox icon. Our PNG image is 32x32 pixels wide and has a transparent background:

Firefox

Note: If you're on Internet Explorer 6 there may be a solid background color behind the image, but that's just because IE6 sucks. The image actually does have transparency.
Now, if we attempt to create a Tk::Photo of that PNG and then set that as our window icon, a problem becomes apparent: the transparent parts of our image aren't really transparent at all when applied as our icon. In most cases, all the transparent pixels will turn black.

Code:
use Tk;
use Tk::PNG;

my $mw = MainWindow->new (
	-title => 'Cuvou.com',
);
$mw->geometry ('200x30');

my $icon = $mw->Photo (
	-file   => 'firefox-icon.png',
	-format => 'PNG',
	-width  => 32,
	-height => 32,
);

$mw->iconimage ($icon);

MainLoop;
Tk Screenshot

The solution to having our transparent icon work is a little bit tricky. We'll need to create a mask file to define the transparent bits of our icon. Fortunately, some imaging programs out there can save images into the format required for the mask file. Namely, The GIMP. If you're a Linux user, you might already have this program installed, and if not it's probably easy to get through your package manager. For Windows users, don't fear: there's a GIMP for Windows too.

Simply open up the PNG image you wanted for your window icon, do a "File/Save As", and make sure you select to save it as an "X BitMap Image (xbm)" file.

Save as XBM

Go ahead and click "Save". It will warn you about how you should export your image first. Go ahead and click on "Export" to have it do it for you.

Export XBM

Pay extra care to the next part. While it's going to save our image as an XBM format (or X bitmap image), the thing we really want it to do is to create a mask XBM of our icon. So make sure that the "Write extra mask file" option is selected. Note: if this option is disabled, it means your image probably didn't have any transparency in it. Double check that if this is the case.

XBM Options

I also check the "Write hot spot values" too, only because the example XBM files from Tk's demo directory has hot spots at (16,16) on their 32x32 pixel image. Go ahead and click on "Ok" and it will save your XBM images.

It will create two images: "firefox-icon.xbm" and "firefox-icon_mask.xbm" in this case. We're only interested in the mask file. If you view it in a text editor, you would see this:

Source: firefox-icon.xbm
#define firefox_icon_mask_width 32
#define firefox_icon_mask_height 32
#define firefox_icon_mask_x_hot 16
#define firefox_icon_mask_y_hot 16
static unsigned char firefox_icon_mask_bits[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00,
   0x00, 0xfe, 0x07, 0x00, 0x00, 0xff, 0x3f, 0x00, 0x80, 0xff, 0x7f, 0x00,
   0xc0, 0xff, 0xff, 0x01, 0xe0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x03,
   0xf0, 0xff, 0xff, 0x07, 0xf0, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0xff, 0x1f,
   0xf8, 0xff, 0xff, 0x1f, 0xf8, 0xff, 0xff, 0x3f, 0xf8, 0xff, 0xff, 0x3f,
   0xf8, 0xff, 0xff, 0x3f, 0xf8, 0xff, 0xff, 0x1f, 0xf8, 0xff, 0xff, 0x1f,
   0xf0, 0xff, 0xff, 0x1f, 0xf0, 0xff, 0xff, 0x1f, 0xf0, 0xff, 0xff, 0x0f,
   0xe0, 0xff, 0xff, 0x0f, 0xe0, 0xff, 0xff, 0x07, 0xe0, 0xff, 0xff, 0x07,
   0xe0, 0xff, 0xff, 0x07, 0xe0, 0xff, 0xff, 0x07, 0xc0, 0xff, 0xff, 0x03,
   0x80, 0xff, 0xff, 0x03, 0x00, 0xfe, 0xff, 0x00, 0x00, 0xe0, 0x0f, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
You don't really need to know what's inside your mask file, though. But if you were for any reason curious as to what an X BitMap looks like inside, that's your answer.

Now, back to our code. Now that we have our mask bitmap, we can tell our MainWindow to use it as our icon mask (note: icon masks will only accept bitmaps).

Code:
use Tk;
use Tk::PNG;

my $mw = MainWindow->new (
	-title => 'Cuvou.com',
);
$mw->geometry ('200x30');

my $icon = $mw->Photo (
	-file   => 'firefox-icon.png',
	-format => 'PNG',
	-width  => 32,
	-height => 32,
);

$mw->iconimage ($icon);
$mw->iconmask ('@firefox-icon_mask.xbm');

MainLoop;
Note: the @ symbol is important. Tk has a handful of built-in bitmaps (such as questhead, info, and hourglass) and when your program wants to use a bitmap, it will assume you want one of the built-ins first. Using the @ symbol tells it to use your own bitmap file instead. Let's see our result:

Transparent Icon!

Success!

Now, I mentioned that this works by using X BitMaps, and that Tk has a handful of built-in bitmaps. Here is a screenshot of the `widget` program under Linux which lists all of the built-ins:

Built-in Bitmaps

So, if we wanted our window to use the questhead bitmap, we would simply tell it to use the bitmap for both the icon and the icon mask:

Code:
use Tk;
use Tk::PNG;

my $mw = MainWindow->new (
	-title => 'Cuvou.com',
);
$mw->geometry ('200x30');

$mw->iconbitmap ('questhead');
$mw->iconmask ('questhead');

MainLoop;
Questhead Icon

By now I hope that the elusive transparent window icon how-to has become clear. I know personally that this was quite an elusive monster to tackle and all I ever found via Google were others asking the very same question on forums all over the place, but nobody got any answers back.

~Casey Kirsle
Dec. 8, 2007

Downloads