Vista includes support for a new image format named Windows Media Photo. This image format consists of a codec (offering features such as lossless encoding, high dynamic range encoding, highly efficient operation…) and a new container format. In other words, lots of features to make you want to try it out. My aim for this post was to write some code to create my first Window Media Photo file. The tools at our disposal to write this application are the Windows Media Photo (I’d like to abbreviate to WMP but that’s taken so I’ll go with WMPhoto) codec itself and the Windows Imaging Component (WIC) that will allow us to access that codec. WIC is another new feature for Vista, documented in the Windows SDK) so we get to play with two new technologies for the price of one!
WIC abstracts away the specifics of discovering and working with still image codecs. For this application it will allow us to locate a codec for our source image, decode it, locate the WMPhoto codec, pass the decoded source image for encoding and save to a file. The bulk of the application is with WIC and the steps it takes are as follows:
WIC defines a factory object (IWICImagingFactory) that allows an application to create the specific objects needed to do its work. For decoding an image we can ask the factory to create the required decoder object (IWICBitmapDecoder) from an exiting file by passing a filename. Other than looking at the file extension, each codec registered with WIC can include one or more entries that contain the first few bytes that uniquely identify an image format (JPEG would be FF D8). Using this WIC can ‘sniff’ for the correct codec. Once we have the decoder object we can ask it for a frame object (IWICBitmapFrameDecode) that will allow access to the decoded image. The concept of frames exists because an image container can contain more than one image (TIFF being the prime example). The frame object inherits from the most fundamental image object in WIC (IWICBitmapSource) which can be thought of as corresponding to an image as it would be understood in GDI+.
That’s decoding covered, but we still need to get things into the WMPhoto format. Do this we first use the factory object to create a stream object (IWICStream) and initialise it to a file on the disk. We then use the factory again to ask it to create the WMPhoto encoding object (IWICBitmapEncoder) specifying the WMPhoto container identifier. Just we decompressed a frame from a decoder in the last paragraph, we will now ask the encoding object for a frame (IWICBitmapFrameEncode) that will store the encoded image. Because we specified the WMPhoto container, we encoding frame will use the WMPhoto codec and it will pass us an object to allow us to configure the codec with the options we choose.
I’ll break from WIC briefly to discuss the options passed to the codec. The options are configured through named properties. Some are defined by WIC and some are specific to the codec being used. For this application I’ll configure ‘ImageQuality’ (defined by WIC) to govern the level of lossy compression, a value between 0 and 1.0. Alternatively you could specify ‘lossless’ (defined by WIC) but that’s probably only worth doing if you have immaculate source images (RAW from a digital camera for example). I’ll also specify ‘UseCodecOptions’ (defined by WMPhoto) and set that to false. This tells the WMPhoto codec to pick default values for other properties we could configure, based on the ImageQuality value.
Continuing with the encoding, we can now set the pixel format and dimensions of the source frame, on the encoding frame. We ask the encoding frame to encode from the source frame (a chaining feature of WIC) and the image data is compressed. The finish off we then call commit on the frame and encoder objects, to flush everything out to the stream and close it.
WIC is a COM based API so to code this application I followed the same pattern as the WebCam code. That is to create a C++ class to do the COM work, wrap that it a C++\CLI class to expose it to the managed world (forward calls, convert parameters, handle errors) and then a C# application to make use of it. I created a simple command line app that takes an input file and an output file and successfully created my first WMPhoto image using the highest quality encoding.
There is a lot more to WMPhoto. I’ve only used JPG images as sources which is a bit pointless as quality has already been lost. Converting from RAW source would be interesting, as would looking at the functionality of the container format. The most interesting perhaps being the operations you can perform in the compressed domain. For example, to create a JPG thumbnail in GDI+ you would typically have to decode the JPEG, resize the decoded image and then re-encode it. With WMPhoto there is the prospect of performing this and other operations without decompressing, saving memory and CPU time. For more information about WMPhoto check out Bill Crow’s blog.