Icon files, and all that

03 May 2014
When I started the development of my icon editor IconSharp, my intention was to write an Icon manipulator rather than an icon editor, which in turn morphed into the goal of writing a utility that could edit multi-icon ICO files. As things panned out neither multi-icon and ICO support made the cut for the first public release. I've bitten the bullet and decided to formally give the multi-icon and ICO format support the axe, and here is various thoughts that as a result had nowhere to go. Most of this article is things I wrote while developing IconSharp when I still intended to include these features, and I felt it a waste to discard it all unpublished. This article coincides with the release of the source code.

Background

I have always found Windows application icons notoriously unpredictable, which is not helped by most icon editors out there being basically crap. As bitmap editors they are mostly OK, but they are far from helpful when trying to diagnose the idiosyncrasies a given version of Windows applies. As well as doing some caching based on filename, Windows also sometimes down-samples the 32x32 icon to 16x16 even when there is a 16x16 icon within the file, often with ugly results. I once found an icon library management program that got it right, but I have long forgotten what it was called.

Somehow I managed to coax Gimp into creating an icon file that seemed to work properly, and my plan was to take a copy of the ICO file specification and write my own utility to help me get nailed down one-and-for-all what is needed so that icons always look good under Windows. However I felt that the tool really needed to have at least some editing capability, and eventually it was the latter that got my attention.

The .ico format

The ICO file format itself is a container format where (at least in the original variant) the pixel data for the individual icons is stored using BMP format data-structures. BMP is a bit of a beast in itself, but mercifully ICO files only use uncompressed BMP pixel data which is a vast simplification. It was clearly “designed” with re-use of built-in data-structures and functions present in Windows in mind, but the way it is done means that writing an ICO reader/writer from scratch is loaded with red herrings and pitfalls. These are in large part because the BMP pixel data is not a completely self-contained BMP resource, with some duplication of information between the ICO container and the BMP pixel data. Of particular interest is how the ICO format was extended so that the pixel data can use PNG pixel data as well as BMP, as the container does not have any fields that indicate what the pixel data format is. Basically a reader has to detect that the BMP header does not correspond to a sane icon, and then fall back to trying PNG loading routines. Couple this with all the quirks of both BMP and how it is integrated into ICO, and it is pretty clear that writing ICO read/write containers is a project in itself.

In hindsight I should not have mixed experimental investigation of the ICO file format with experimentation of C# file reading/writing, but I was not expecting the process to be as hit-n-miss as it turned out to be. Writing an experimental loader in C or Python then porting it to C# probably would have been less frustrating, but I am not sure if it would have been much faster overall development methodology. Had I gone down the C route, I probably would have succumbed to the temptation to do the whole lot in Win32 (including resource-based GUIs), but the Petzold-inspired route is one I was trying to steer clear of. As much as the Win32 API is a dream for people who like C with all the gory bits, I have already hit my high water mark for using it in a commercial context.

Pulling the plug

I got to the stage where I had a pile of C# code that was able to decode ICO files, but by this point it was the editor that had really caught my attention, and that had got to the point where I wanted to get something completed. This was partly driven by my parallel writing up of my experiences, but the main factor was that I felt that I had completed my learning objectives of working out GTK#'s graphics subsystem. Ultimately learning about a non-trivial API sub-system and having something to show for it is what motivated me the most, and I felt that as vehicle for finding stuff out it had run its course.

During all of this I came across icotool from icoutils which although I did not get around to testing in anger, looked like a good solution to getting ICO files right based on what I had found out about the format from my own development. While it did not have various controls I would have liked, it looked good enough to see off the last of the motivation I had for rolling my own. It was clear I had long lost sight of this original spark of interest, and there qere apparent third-party solutions available.

If I was to be true to my original objective I would go as far as supporting even 2- and 4-bit icons within the editor, but that would mean doing a load of palette management to make sure a given icon only used available colours. An alternative would be to assume 8-bit colour and fall back on 32-bit if a given icon used more than 256 colours, but then I would have to decide which compromises to make. And then you get into things such as creating auto-scaled icons of different resolutions. Tempting as a lot of the infrastructure is in place, but I have to consider the opportunity cost that going ahead would involve.

In short: Learning aspect depleted, not a technical ‘blocker’ problem due to alternatives, and marching onwards would soon bring up other technical problems. Yes I could wring a bit more interest out of the project, but I felt it better to polish it all off and move on.

Final thoughts

Ultimately I have to ask myself one question: “What is the longer-term benefit?” - what is there extra to gain in comparison to the opportunity cost? In this case I decided getting closure and potentially freeing up the time for something new was the thing to do.