sup { vertical-align:text-top; }

Windows with C++

Decoding Windows Vista Icons With WIC

Kenny Kerr

Contents

The History of Windows Icons
The Windows Icon Format
A Basic Decoder
Discovery
What's Next

In the April issue of MSDN® Magazine I introduced the Windows® Imaging Component (WIC), showing you how you can use it to encode and decode different image formats. This month I'm going to show you how you can extend WIC by writing your own codecs. In particular, I'll walk through the initial steps for developing an alternative decoder for the Windows icon format. First, however, let me discuss the process of writing a codec.

For the most part, writing a codec is quite straightforward. A codec supporting the encoding of a particular image format must provide a COM class that implements the IWICBitmapEncoder interface. Similarly, a codec supporting the decoding of a particular image format must provide a COM class that implements the IWICBitmapDecoder interface. These are the same interfaces used by developers using WIC for image processing. In fact, the codec's COM classes are loaded in-process and accessed directly by the developer's application code. This provides a very efficient abstraction, allowing consumers to achieve high-performance image processing.

An encoder must also provide an implementation of the IWICBitmapFrameEncode interface, which supplies methods for encoding the individual frames of an image. Even image formats that don't support multiple frames still use the IWICBitmapFrameEncode interface to represent the image pixels. Similarly, a decoder must also supply an implementation of the IWICBitmapFrameDecode interface, which has methods for decoding the individual frames of an image.

Once the codec is written, it can be created directly by a consumer using the CoCreateInstance function, but that is not very interesting. To truly integrate the codec with WIC, a few more steps are required. In particular, the encoder and decoder must include additional metadata in the registry describing their capabilities. Any DLLs containing encoders or decoders must also be signed. The metadata allows WIC to identify whether or not the codec provides the necessary features when WIC attempts to find an appropriate encoder for a particular image format or a decoder for a particular image file. Since the particular implementation that a consumer receives may be determined at run time, WIC attempts to provide a certain degree of assurance to the consumer by only allowing signed codecs to participate in the discovery and arbitration process.

With the general concepts in place, I want to now take a look at a concrete example. I'll focus on the implementation of an alternative decoder of the Windows icon format. I chose the icon format for two reasons. Firstly, Windows Vista® introduced an update to the icon format, which is notable in itself; I'll explore the changes in a moment. Secondly, the built-in decoder that ships with Windows Vista does not actually support the updated icon format.

The History of Windows Icons

As with many facets of Windows, the icon format is steeped in history. In the early days of the 16-bit Windows operating system only the device-dependent bitmap (DDB) format was supported. Such a bitmap specified a width and height in pixels as well as a table that mapped pixels to entries in a particular device's color palette. Because different devices naturally supported different resolutions and had different color capabilities, these bitmaps were not easily moved from one device to another. DDBs were, however, very efficient and are in fact still used today for certain operations. A device-independent bitmap (DIB) format was introduced to correct all of the problems inherent in DDBs. Such a bitmap included its own color table independent of any particular device. The bitmap's pixels then mapped to the bitmap's color table, which unambiguously defined the pixel colors using red, green, and blue (RGB) color values.

Naturally, it made sense for the icon format to make use of the DIB format, but there was a problem. DIB predated the use of alpha channels for representing transparency. The hardware of the time also did not permit the extravagance of an extra byte per pixel that would have been required by an alpha channel. A different solution was required. The DIB would carry an extra bitmap following the color bitmap that was effectively a bitmask identifying the transparent and opaque pixels in the color bitmap. The bits in the bitmask could easily and, more importantly, efficiently, be combined with the target device's surface using Boolean operations by means of a bit-block transfer using the BitBlt function.

The icon format itself included multiple DIB structures, each with its associated bitmask for different sizes. This allowed a single icon to provide an image for different scenarios. For example, if an icon was displayed on the desktop, a 32×32 pixel DIB would be used. If an icon was displayed on a window's title bar, a 16×16 pixel DIB would be used. The icon format also needed to cater to different display capabilities, so additional DIBs with color tables of different sizes were also supported.

Eventually, icons could forgo the use of a color table and instead store 24 bits per pixel (bpp) directly, 8 bits per red, green, and blue value. This allowed for vibrant icons, but because a bitmask was still used to define the transparency, the edges were often quite harsh. Fortunately, icon bitmaps were usually small so this didn't prove to be a big problem.

During the development of Windows XP, Microsoft wanted to introduce larger, higher quality icons and the bitmask approach for specifying transparency just did not cut it. An alpha channel was required so that each pixel could have any degree of transparency, allowing for effects such as shadows. Icons in Windows XP started using 32bpp, accommodating an alpha channel where each pixel specified the degree of transparency it required. These icons still carried with them a bitmask for compatibility, but it was quite obvious when a legacy application incorrectly used the bitmask instead of the alpha channel as the icon's outline was often very jagged and lacked the shadows and other visual effects the icon's designers had intended. Nevertheless, an application properly written for Windows XP could include stunning 32bpp icons. Windows XP also began promoting larger icons (up to 48×48 pixels in size) further adding to the visual effect.

During the development of Windows Vista, Microsoft realized that the trend it had begun in Windows XP could not continue. Although the DIB format is very efficient at run time, as the bitmaps become larger they start consuming far too much space on disk and in memory. Increases in computing power meant that it was feasible to employ a far more capable image format, and Microsoft chose PNG. Icons in Windows Vista can embed PNG images instead of DIBs, and these are now used throughout Windows to allow icons to provide bitmaps 256×256 pixels in size at a stunning 32bpp and at a fraction of the space requirements that DIBs would demand. Figure 1 shows some icons in Windows Vista.

fig01.gif

Figure 1 PNG-Based Icons in Windows Vista

Windows Vista takes advantage of these high quality PNG images inside of icons to create the illusion that icons are vector images that can be scaled from 16×16 pixels all the way to 256×256 pixels right in front of your eyes. If you want to see this in action, open Windows Explorer, hold the Ctrl key and scroll the mouse wheel. The effect is impressive. Of course Windows Vista goes beyond this by creating folder icons with a preview of the folder's contents, but this is beyond the scope of the icon format itself and a feature specific to Windows Explorer.

The Windows Icon Format

The structure of the icon format is depicted in Figure 2. The first WORD (2 bytes) is always 0. The second WORD is always 1. The latter can be thought of as the resource type. Icons and cursors happen to have a very similar format. The way to tell them apart is by checking the second WORD, which is always 1 for icons and 2 for cursors. The third WORD indicates the number of images that are contained within the icon.

fig02.gif

Figure 2 Icon Format Structure

Following these three WORD values is a series of structures describing the images. The only values in this substructure that are truly necessary are the image size and image offset values. The rest of the values are merely a hint and should not be relied upon because they may not always be populated correctly. In most cases you should simply ignore them since you can learn everything you need to know about the images by inspecting the DIB or PNG image bits themselves. The image size indicates how many bytes are occupied by the image. The image offset indicates the offset from the beginning of the file to the start image bits.

A Basic Decoder

Now let's take a look at developing a basic WIC decoder for icons. For the most part, it is just a matter of implementing the IWICBitmapDecoder interface appropriately. However, you'll need to know some information about the eight most important interface methods: QueryCapability, Initialize, GetFrame, GetContainerFormat, GetDecoderInfo, CreateComponentInfo, GetFrameCount, and GetFrame.

The QueryCapability method inspects a given stream and identifies whether or not it is capable of decoding the image. If it is, it indicates whether it can decode all the frames within the image (WIC­BitmapDecoderCapabilityCanDecodeAllImages) or only a subset of the frames (WICBitmap-DecoderCapabilityCanDecodeSomeImages). Other capability flags may also be specified. Remember to restore the stream position before returning. QueryCapability is where you might read the basic structure of the icon format I described in the previous section to ensure that it is what your decoder expects. You might also want to enumerate the images within the icon to ensure that they can be loaded.

The Initialize method provides the stream that the decoder should read from. Depending on the provided options, it should either load all frames immediately (WICDecodeMetadataCacheOnLoad) or only load them when they are specifically requested (WICDecode­MetadataCacheOnDemand) using the GetFrame method. In the case of the latter, you will need to hold onto the stream until all the frames have been loaded.

Here's something that I do and that you might find helpful: I find it useful to create a substream based on the image size and offset for each image in the icon and pass these substreams to my IWICBitmapFrameDecode interface implementations. Fortunately, the WIC imaging factory object that I introduced in my April 2008 column makes this quite easy. Figure 3 shows my CreateSubStream helper function for creating substreams with the help of the WIC stream object.

Figure 3 Creating Substreams

HRESULT CreateSubStream(IWICImagingFactory* factory,
                               IStream* stream,
                               ULONGLONG offset,
                               ULONGLONG maxSize,
                               IStream** subStream)
{
    ASSERT(0 != factory);
    ASSERT(0 != stream);
    ASSERT(0 != subStream);

    *subStream = 0;

    CComPtr<IWICStream> wicStream;
    HR(factory->CreateStream(&wicStream));

    ULARGE_INTEGER quadOffset = { 0 };
    quadOffset.QuadPart = offset;
    ULARGE_INTEGER quadMaxSize = { 0 };
    quadMaxSize.QuadPart = maxSize;

    HR(wicStream->InitializeFromIStreamRegion(stream,
                                              quadOffset,
                                              quadMaxSize));

    return wicStream.QueryInterface(subStream);
}

The GetContainerFormat method indicates the image format that the decoder represents. In this particular case, we can simply return the built-in icon container format identifier (GUID_ContainerFormatIco).

The GetDecoderInfo method returns an IWICBitmapDecoder­Info interface describing the decoder. Although not particularly complicated, the interface is quite large, and it would certainly be tedious to implement. Happily, it really only acts as an abstraction over the codec's metadata stored in the registry. The WIC API provides a stock implementation that you can take advantage of. Again, this is provided courtesy of the imaging factory object. If you look at Figure 4, you'll see a typical GetDecoderInfo implementation. All you need to do is pass the decoder's CLSID to the CreateComponentInfo method and it will create an object populated with the decoder's metadata from the registry.

Figure 4 Implementing GetDecoderInfo

HRESULT Kerr::IconDecoder::GetDecoderInfo(__out IWICBitmapDecoderInfo** decoderInfo)
{
    if (0 == m_imagingFactory)
    {
        return E_UNEXPECTED;
    }

    if (0 == decoderInfo)
    {
        return E_POINTER;
    }

    *decoderInfo = 0;

    CComPtr<IWICComponentInfo> componentInfo;

    HR(m_imagingFactory->CreateComponentInfo(__uuidof(IconDecoder),
                                             &componentInfo));

    return componentInfo.QueryInterface(decoderInfo);
}

As you might expect, the GetFrameCount method returns the number of frames within the image being decoded.

And finally, the GetFrame method returns an IWIC­Bitmap­FrameDecode interface representing a particular frame within the image.

So far I've talked about implementing the IWICBitmapDecoder interface. Given such an implementation, you would be able to use the CoCreateInstance to create an instance of the decoder, but with a little more work you could fully integrate your codec into the discovery and arbitration framework provided by WIC so that applications can automatically take advantage of it without prior knowledge of your codec.

Applications can take advantage of the available codecs on a particular computer in a number of ways. For starters, they can literally enumerate all of the encoders and decoders to find the one they want to use or to provide the user with the option of choosing a preferred codec. Look at Figure 5 for an example of how you can enumerate encoders. Note that the resulting IWICBitmapEncoderInfo interface provides a wealth of helpful information about the capabilities of each encoder.

Figure 5 Enumerating Encoders

HRESULT EnumerateEncoders()
{
    CComPtr<IWICImagingFactory> imagingFactory;
    HR(imagingFactory.CoCreateInstance(CLSID_WICImagingFactory));

    CComPtr<IEnumUnknown> enumerator;

    HR(imagingFactory->CreateComponentEnumerator(WICEncoder,
                 WICComponentEnumerateDefault, &enumerator));

    CComPtr<IUnknown> unknown;

    while (S_OK == enumerator->Next(1,
                                    &unknown,
                                    0)) // ignored
    {
        CComQIPtr<IWICBitmapEncoderInfo> info(unknown);
        unknown.Release();

        if (info)
        {
            CLSID clsid = { 0 };
            HR(info->GetCLSID(&clsid));

            UINT count = 0;

            HR(info->GetFriendlyName(0, // no buffer length
                                     0, // no buffer
                                     &count));

            CString friendlyName;

            HR(info->GetFriendlyName(count,
                                     friendlyName.GetBufferSetLength(count),
                                     &count));

            friendlyName.ReleaseBuffer(count - 1);

            TRACE(L"%s\n", friendlyName);
        }
    }

    return S_OK;
}

Alternatively, an application may want to rely on WIC to automatically select an appropriate codec. As I mentioned in my last column, the imaging factory provides the CreateDecoder method, allowing you to create a decoder for a particular image format without regard for its implementation. The CreateDecoderFromStream method goes further by choosing an appropriate decoder for you automatically by examining the stream and comparing that with the registration information from the available decoders on the computer.

What's Next

That's all for this month. Although the Windows Imaging Component doesn't fully support the new high-resolution icon format in Windows Vista, you can extend WIC with your own codec to handle it. At the end of the day, WIC is perhaps not that well suited for decoding icon images as there is no clear way to distinguish between different image frames with different resolutions but the exercise of writing a decoder is helpful in understanding how WIC is designed. The LoadImage function is still the best way to load icon images in most cases.

Kenny Kerr is a software craftsman specializing in software development for Windows. He has a passion for writing and teaching developers about programming and software design. Reach Kenny at weblogs.asp.net/kennykerr.