Technical Articles


Windows Vista Technical Articles
How to Write a WIC-Enabled CODEC and Get Full Platform Support for Your Image Format
 

Peggi Goodwin
Lead Program Manager, Imaging
Windows Client Platform

September 2006

Applies to:
   Microsoft Windows Imaging Component (WIC)
   Microsoft .NET Framework 3.0
   Microsoft Windows Vista

Summary: The Windows Imaging Component (WIC) is an extensible platform for digital imaging on Windows Vista, and is also available on Windows XP and Windows Server 2003, either as part of the .NET Framework 3.0 or as a separate component that you can redistribute. WIC provides a layer of abstraction between applications and CODECs, eliminating the need for applications to have specialized knowledge of specific image formats. Any application using the Windows Imaging Component can access, display, process, save, and print images using a single set of consistent interfaces, regardless of the image format, if there's a WIC-enabled CODEC for the image format installed on the machine.

The Windows Vista Explorer, Photo Gallery, and Image Viewer are built on WIC so, once a WIC-enabled CODEC is installed on a Windows Vista system, Windows Vista provides the same level of support for the associated image format as for the standard image formats that ship with the platform. Because you write the CODEC for your own image format, you can ensure consistent quality wherever your image format is used, and you get the benefits of full platform support while protecting your IP investment. (45 printed pages)

Contents

How the Windows Imaging Component Works
   Discovery and Arbitration
   Decoding
   Encoding
   CODEC Lifetime
   Steps to WIC-Enable a CODEC
   IWICBitmapDecoder
Implementing a WIC-Enabled Decoder
   Decoder Interfaces
   IWICBitmapDecoder
   IWICBitmapCodecProgressNotification
   IWICMetadataBlockReader
   IWICBitmapSourceTransform
   IWICDevelopRaw
Implementing a WIC-Enabled Encoder
   Encoder Interfaces
   IWICBitmapEncoder
   IWICBitmapCodecProgressNotification
   IWICBitmapFrameEncode
   IWICMetadataBlockWriter
CODEC Installation and Registration
   Signing Your CODEC
   Registering YourCODEC
   Integration with the Windows Vista Photo Gallery and Windows Explorer
   Installing and Uninstalling Your CODEC
   Making Your WIC-Enabled CODEC Available to Users
Conclusion

How the Windows Imaging Component Works

Discovery and Arbitration

Before an image can be decoded, an appropriate CODEC must be found that can decode that image format. In most systems, the supported image formats are hard coded, so no discovery process is required. Because the WIC platform is extensible, it's necessary to be able to identify the format of an image and match it with an appropriate CODEC.

To support run-time discovery, each image format must have an identifying pattern that can be used to identify the appropriate decoder for that format. (We strongly recommend that, for new file formats, you use a GUID for the identifying pattern, because it's guaranteed to be unique.) The identifying pattern must be embedded in each image file that conforms to that image format. Each decoder has a registry entry that specifies the identifying pattern(s) of the image format(s) it can decode. When an application needs to open an image, it requests a decoder from WIC. WIC looks up the available decoders in the registry, and checks each registry entry for an identifying pattern that matches the pattern embedded in the image file. We'll discuss this further in Decoder-Specific Registry Entries.

When WIC finds a single decoder that matches the identifying pattern in the image, it instantiates the decoder and passes the image file to it. If WIC finds more than one match, it invokes a method called QueryCapability on each matching decoder to arbitrate among them and find the best match. We'll discuss this in greater detail in the section on the QueryCapability method of the IWICBitmapDecoder interface.

Note   Only signed CODECs can participate in the WIC discovery process.

Decoding

Once the appropriate decoder has been selected and instantiated, the application talks directly to the decoder. The decoder has several responsibilities, which it implements through various interfaces. These services can be classified as:

  • Container-level services
  • Frame-level services
  • Metadata enumeration services
  • Native decoder transforms
  • Progress notifications and cancellation support
  • Raw processing services

Container-level services include retrieving the top level thumbnail (if supported), preview, color context(s), palette (if applicable), and container format, as well as providing access to the individual image frames within the container. (Some containers only contain a single frame while others, for example TIFF, may contain multiple frames.) This set of services also includes providing information about the decoder itself, and its capabilities with respect to a specific image file.

Individual frames will have their own thumbnails, and may also have their own color contexts, palettes, etc, which are exposed at the frame-level. The most important operation performed at the frame-level, though, is the actual decoding of the image bits for that frame.

WIC provides metadata readers for the most common metadata formats (IFD, EXIF, IPTC, XMP, APP0, APP1, etc.), and also supports extensibility for 3rd party metadata formats. This frees the CODEC of the responsibility for parsing metadata, but the CODEC is responsible for enumerating the metadata blocks and requesting a metadata reader for each block. WIC performs discovery for metadata handlers the same way it does for CODECs, based on a pattern in the block header matching a pattern in the metadata handler's registry entry. We'll talk about this further in the section on Decoder-Specific Registry Entries, under CODEC Installation and Registration.

Decoders are not required to natively support transform operations, but doing so enables significant performance optimizations that provide a better end user experience. For example, an application can create a pipeline of various transforms (scaling, cropping, rotation, pixel format conversion) to perform upon an image before the image is rendered. (We'll discuss transform pipelines further in the section on IWICBitmapSource.) After creating a transform pipeline, the application requests the final transform in the pipeline to produce the bitmap that results from applying all the transforms to the image source. At that point, if the decoder itself is able to perform transform operations, WIC asks it which of the requested transforms it can perform. Any requested transforms the decoder cannot perform will be performed by WIC on the decoded image before returning it to the caller. This optimized transform pipeline provides better performance than performing each transform sequentially in memory, particularly when some or all of the transforms can be accomplished during the decoding process.

Raw processing services include tweaking camera settings, like exposure, contrast, sharpening, etc, or changing the color space before processing the raw bits.

Progress notifications and cancellation support enable an application to request progress notifications for lengthy operations, and also enable the application to give the user an opportunity to cancel an operation that's taking too long. This is important because if a user can't cancel an operation, they may feel the process is hung, and try to cancel it by closing the application.

These interfaces are described in detail in the section on Implementing a WIC-Enabled Decoder.

Encoding

Like decoders, encoders have responsibilities that they implement through interfaces. The services encoders provide are complementary to the services provided by decoders, except they write out image data rather than reading it. Encoders also provide services in the following categories:

  • Container-level services
  • Frame-level services
  • Metadata enumeration and update services
  • Progress notification and cancellation support

Container-level services for an encoder include setting the top-level thumbnail (if supported), preview, palette (if applicable), and iterating through the individual image frames so they can be serialized into the container.

Frame-level services for an encoder mirror those for the decoder, except that they write out the image data, thumbnail, and any associated palette, etc., rather than reading them.

Likewise, metadata enumeration services for an encoder include iterating through the metadata blocks to be written, and invoking the appropriate metadata writers to serialize the metadata to disk.

These interfaces are described in detail in the section on Implementing a WIC-Enabled Encoder.

CODEC Lifetime

A WIC CODEC is instantiated to handle a single image, and usually has a short lifetime. It's created when an image is loaded and is released when the image is closed. An application may use a large number of CODECs simultaneously with overlapping lifetimes (think of scrolling through a directory containing hundreds of images), and multiple applications may be doing this at the same time.

You may have previously written CODECs whose lifetime is scoped to the lifetime of the process within which they live. This is not the case with WIC CODECs. The Windows Vista Photo Gallery, Explorer, and Photo Viewer, as well as numerous other applications, are built on WIC and will be using your CODEC to display images and thumbnails. If the lifetime of your CODEC were scoped to the lifetime of the process, every time an image or thumbnail was displayed in the Windows Vista Explorer, the CODEC instantiated to decode that image would remain in memory until the next time the user rebooted their machine. If your CODEC is never unloaded, its resources are, in effect, "leaked" because they can't be used by any other component in the system

Steps to WIC-Enable a CODEC

  1. Implement a container-level decoder class and a frame-level decoder class that exposes the required WIC interfaces for decoding images and iterating through blocks of metadata. This enables all WIC-based applications to interact with your CODEC the same way they interact with standard image formats.
  2. Implement a container-level encoder class and a frame-level encoder class that exposes the required WIC interfaces for encoding images and serializing blocks of metadata into an image file.
  3. If your container format is not based on a TIFF or JPEG container, you may need to write metadata handlers for the common metadata formats (EXIF, XMP). However, if you use a TIFF or JPEG based container format, this is not necessary because you can delegate to the system-provided metadata handlers. (Because most CODEC authors will not need to implement their own metadata handlers, this whitepaper will not discuss how to write a metadata handler.)
  4. Embed a unique identifying pattern (we recommend a GUID) in all your image files. This enables your image format to be matched against your CODEC during the discovery. (If you're writing a WIC wrapper for an existing image format, you need to find some pattern of bits that the encoder always writes into its image files that's unique to that image format, and use this as the identifying pattern.)
  5. Sign your CODEC.
  6. Register your CODEC at installation time. This enables your CODEC to be discovered at run time by matching the identifying pattern in the registry with the pattern embedded in the image file.

Important note

In the interface descriptions in this article, wherever a method is listed as Required, that means the method requires a full implementation. When a method is listed as Optional, the method still must be included in classes that implement the interface, but it's acceptable to return an appropriate error code rather than providing an actual implementation.

For certain methods, you should return a specific error code to notify the caller that the requested feature is not supported. For example, if you don't support global thumbnails, you must return WINCODEC_ERR_CODECNOTHUMBNAIL from the GetThumbnail method. If the image you're encoding/decoding doesn't use an indexed pixel format, or if you don't support a container-level palette, you must return WINCODEC_ERR_PALETTEUNAVAILABLE from the SetPalette method. For any other unsupported methods, return WINCODEC_ERR_UNSUPPORTEDOPERATION.

Throughout this article, I will occasionally use code samples to illustrate how to do something. For the sake of brevity, I've omitted all error checking and reference counting. If you cut and paste any of these samples, please remember that the code is incomplete without error checking and ref-counting.

Implementing a WIC-Enabled Decoder

Implementing a WIC decoder requires writing two classes. The interfaces on these classes correspond directly to the decoder responsibilities outlined above in the Decoding section of How the Windows Imaging Component Works.

One of the classes provides container-level services as described above, and implements the IWICBitmapDecoder interface. If your image format supports container-level metadata, you will also need to implement the IWICMetadataBlockReader interface on this class. We recommend that you support the IWICBitmapCodecProgressNotification interface on both the decoder and encoder to support a better user experience.

The other class you will implement provides frame-level services and does the actual decoding of the image bits for each frame in the container. This class implements the IWICBitmapFrameDecode interface and the IWICMetadataBlockReader interface. If you're writing a decoder for a Raw format, you'll also implement the IWICDevelopRaw interface on this class. In addition to the required interfaces, it's highly recommended that you implement the IWICBitmapSourceTransform interface on this class to enable the best possible performance for your image format.

One of the objects provided by WIC is the ImagingFactory. You'll frequently use the IWICComponentFactory interface on this object to create various components. Since you'll be using it frequently, you'll probably want to keep a reference to it as a member property on both your decoder and encoder classes.

IWICImagingFactory* m_piImagingFactory = NULL;
IWICComponentFactory* m_piComponentFactory = NULL;
HRESULT hr;

hr = CoCreateInstance(CLSID_WICImagingFactory, NULL,
     CLSCTX_INPROC_SERVER, IID_IWICImagingFactory,
     (LPVOID*) m_piImagingFactory);

hr = m_piImagingFactory->QueryInterface(
     IID_IWICComponentFactory, (void**)&m_piComponentFactory);

Decoder Interfaces

The following tables show the interfaces implemented by WIC decoders, and the class diagram shows the inheritance hierarchy.

Container-Level Decoder Interfaces

InterfaceResponsibilitiesImplementation
IWICBitmapDecoderContainer-level servicesRequired
IWICBitmapCodecProgressNotificationProgress notification & cancellation supportRecommended
IWICMetadataBlockReaderMetadata enumeration Optional (Required only for formats that support container-level metadata)

Frame-Level Decoder Interfaces

InterfaceResponsibilitiesImplementation
IWICBitmapFrameDecodeFrame-level servicesRequired
IWICMetadataBlockReaderMetadata enumeration Required
IWICBitmapSourceTransformNative decoder transformsRecommended
IWICDevelopRawRaw processing servicesRequired for Raw formats only

Aa905327.wiccodec01(en-us,MSDN.10).jpg

Figure 1. WIC Decoder interfaces

IWICBitmapDecoder

When an application requests a decoder, the first point of interaction with the CODEC is through the IWICBitmapDecoder interface. This is the container-level interface that provides access to the top-level properties of the container and, most important, the frames it contains. This is the primary interface on your container-level decoder class.

interface IWICBitmapDecoder : IUnknown

{

// Required methods

          HRESULT QueryCapability ( IStream *pIStream,
                                                   DWORD *pdwCapabilities );

          HRESULT Initialize ( IStream *pIStream,
                                         WICDecodeOptions cacheOptions );

          HRESULT GetContainerFormat ( GUID *pguidContainerFormat );

          HRESULT GetDecoderInfo ( IWICBitmapDecoderInfo **pIDecoderInfo );

          HRESULT GetFrameCountgetframecount ( UINT *pCount );

          HRESULT GetFramegetframe ( UINT index,
                                     IWICBitmapFrameDecode **ppIBitmapFrame );

// Optional methods

          HRESULT GetPreviewgetpreview ( IWICBitmapSource **ppIPreview );

          HRESULT GetThumbnailgetthumbnail ( IWICBitmapSource **ppIThumbnail );

          HRESULT GetColorContextsgetcolorcontexts ( UINT cCount,
                                                     IWICColorContext **ppIColorContexts,
                                                     UINT *pcActualCount );

          HRESULT GetMetadataQueryReadergetmetadataqueryreader ( IWICMetadataQueryReader
                                                                                **ppIMetadataQueryReader );

          HRESULT CopyPalettecopypalette ( IWICPalette *pIPalette );

}

Some image formats have global thumbnails, color contexts, and/or metadata, while many image formats only provide these on a per-frame basis, so the methods for accessing these are optional on IWICBitmapDecoder, but are required on IWICBitmapFrameDecode. Likewise, some CODECs do not use indexed pixel formats and so do not need to implement the CopyPalette methods on either interface. We'll discuss the methods that are optional on IWICBitmapDecoder in the section on IWICBitmapFrameDecode, where they're most commonly implemented.

QueryCapability is the method used for CODEC arbitration. (See Discovery and Arbitration in the section on How the Windows Imaging Component Works at the beginning of this paper.) This is a very important method because, in the event that two CODECs are capable of decoding the same image format, or in the case of a pattern collision where two CODECs happen to use the same identifying pattern, it allows us to select the CODEC that can best handle any specific image.

When invoking this method, WIC will pass you the actual stream containing the image. It's your responsibility to verify whether you can decode each frame within the image and enumerate through the metadata blocks, in order to accurately declare what capabilities this decoder has with respect to the specific file stream passed to it. This is important for all decoders, but particularly important for image formats based on TIFF containers. The discovery process works by matching patterns associated with decoders in the registry to patterns in the actual image file. Declaring your identifying pattern in the registry guarantees that your decoder will always be detected for images in your image format, but it doesn't guarantee that your decoder won't be detected for images in other formats. For example, all TIFF containers include the TIFF pattern, which is a valid identifying pattern for the TIFF image format. This means that, during discovery, at least two identifying patterns will be found in image files for any image format that's based on a TIFF-style container. One will be the TIFF pattern and the other will be the actual image format pattern. While less likely, there could be pattern collisions between other unrelated image formats as well. This is why discovery and arbitration is a two stage process. Always verify that the image stream passed to QueryCapabilities is actually a valid instance of your own image format. Also, if your CODEC decodes an image format for which you don't own the specification, your QueryCapabilities implementation should check for the presence of any feature that may be valid under the image format specification that your CODEC doesn't implement. This will ensure that users have a good experience with your CODEC, and experience unnecessary decoding failures or get unexpected results.

Before performing any operation on the image, you must save the current position of the stream, so you can restore it to its original position before returning from the method.

The enumeration that specifies the capabilities is defined as follows:

enum WICBitmapDecoderCapabilities
{        WICBitmapDecoderCapabilitySameEncoder,
         WICBitmapDecoderCapabilityCanDecodeAllImages,
         WICBitmapDecoderCapabilityCanDecodeSomeImages,
         WICBitmapDecoderCapabilityCanEnumerateMetadata,
         WICBitmapDecoderCapabilityCanDecodeThumbnail    }

You should only declare WICBitmapDecoderCapabilitySameEncoder if the image was encoded by your encoder. After verifying whether you can decode each frame in the container, declare either WICBitmapDecoderCapabilityCanDecodeSomeImages if you can decode some but not all of the frames, WICBitmapDecoderCapabilityCanDecodeAllImages if you can decode all of the frames, or neither if you can't decode any of them. (These two enums are mutually exclusive; if you return WICBitmapDecoderCapabilityCanDecodeAllImages, WICBitmapDecoderCapabilityCanDecodeSomeImages will be ignored.) Declare WICBitmapDecoderCapabilityCanEnumerateMetadata after verifying that you can enumerate through the metadata blocks in the image container. You don't have to check for a thumbnail in every frame. If there's a global thumbnail and you can decode it, you can declare WICBitmapDecoderCapabilityCanDecodeThumbnail. If there is no global thumbnail, then attempt to decode the thumbnail for Frame 0. If there is no thumbnail in either of these places, do not declare this capability.

After determining the capabilities of the decoder with respect to the image stream passed to this method, logically OR (|) together the WICBitmapDecoderCapabilities you've verified that this decoder can perform on this image, and return the result. Remember to restore the stream to its original position before returning!

Initialize is invoked by an application after a decoder has been selected to decode a specific image. The image stream is passed to the decoder, and a caller may optionally specify a cache option for dealing with the metadata in the file.

enum WICDecodeOptions
{          WICDecodeMetadataCacheOnDemand,
           WICDecodeMetadataCacheOnLoad            }

Some applications use metadata more than others. Most applications don't need to access all the metadata in an image file, and will request specific metadata as they need it. Other applications would rather cache all the metadata up front than keep the file stream open and perform disk I/O every time they need to access metadata. If the caller doesn't specify a metadata cache option, the default caching behavior should be cache on demand. This means no metadata should be loaded into memory until the application makes a specific request for that metadata. If the application specifies WICDecodeMetadataCacheOnLoad, the metadata should be loaded in memory immediately and cached. When metadata is cached on load, the file stream may be released after the metadata has been cached.

GetContainerFormat is a simple method to implement. Just return the GUID of the image format of the image for which the decoder is instantiated. This method is also implemented on IWICMetadataBlockReader and IWICBitmapEncoder.

GetDecoderInfo returns an IWICBitmapDecoderInfo object. To get the IWICBitmapDecoderInfo object, just pass the GUID of your decoder to the CreateComponentInfo method on IWICComponentFactory, and then request the IWICDecoderInfo interface on it, as shown below.

IWICComponentInfo* piComponentInfo = NULL;
HRESULT hr;

hr = m_piComponentFactory->CreateComponentInfo(CLSID_This,
     &piComponentInfo);
hr = piComponentInfo->QueryInterface(IID_IWICBitmapDecoderInfo,
     (void**)ppIDecoderInfo);

GetFrameCount simply returns the number of frames in the container. Some container formats support multiple frames, and others only support one frame per container.

GetFrame is probably the most important method on the IWICBitmapDecoder interface because the frame contains the actual image bits, and the frame decoder object returned from this method is the object that does the actual decoding of the requested image. That is the other object you need to implement when writing a decoder. See IWICBitmapFrameDecode below for more information on this interface.

GetPreview returns a preview of the image. For a detailed discussion of previews, please see the SetPreview method on the IWICBitmapEncoder interface.

If your image format contains an embedded JPEG preview, you don't have to write a JPEG decoder to decode it. In fact, we strongly recommend that you don't do that. Instead, you should delegate to the JPEG decoder that ships with the WIC platform for decoding previews and thumbnails. To do this, seek to the beginning of the preview image data in the stream and invoke the CreateDecoderFromStream method on the imaging factory.

IWICBitmapDecoder* piPreviewDecoder = NULL;
IWICBitmapFrameDecode* piPreviewFrame = NULL;
IWICBitmapSource* piPreview = NULL;
HRESULT hr;

hr = m_piImagingFactory->CreateDecoderFromStream(m_piStream,
     NULL, WICDecodeMetadataCacheOnDemand, &piPreviewDecoder);
hr = piPreviewDecoder->GetFrame(0, piPreviewFrame);
hr = piPreviewFrame->QueryInterface(IID_IWICBitmapSource, 
     (void**)&piPreview);

IWICBitmapCodecProgressNotification

When a CODEC is performing an I/O operation like CopyPixels or WritePixels on a large image, it may take several seconds or even minutes to complete. One of the most common complaints end users have about applications is that, when they're unable to interrupt a long running operation, they think the application is hung. Often a user will close an application, or even reboot the machine, in an attempt to regain control of their computer when an application becomes unresponsive.

This interface enables an application to specify a callback function that the CODEC can call at specified intervals to notify the caller of the progress of the current operation. The application can use this callback function to display progress UI to notify the user of the status of the operation. If a user clicks the Cancel button on the progress dialog, the application returns WINCODEC_ERR_ABORTED from the callback function. When this happens, the CODEC must cancel the specified operation and propagate this HRESULT back to the caller of the method that was performing the operation.

This interface should be implemented on your container-level decoder class.

interface IWICBitmapCodecProgressNotification : public IUnknown

{

       HRESULT RegisterProgressNotification (
                                                 PFNProgressNotification pfnProgressNotification,
                                                 LPVOID pvData,
                                                 DWORD dwProgressFlags );

}


    

RegisterProgressNotification is invoked by an application to register a callback function that the CODEC can call at specified intervals. The first parameter, pfnProgressNotification, is a pointer to the callback function the CODEC should call at regular intervals.

The pvData parameter points to some object that the caller wants the CODEC to pass back to the callback function whenever the callback function is invoked. This object may be anything at all and has no particular significance to the CODEC.

The dwProgressFlags parameter specifies when the CODEC should call the callback function. There are two enumerations that can be logically OR'ed (|) together for this parameter. These are WICProgressOperation and WICProgressNotification.

The WICProgressOperation enum specifies whether to call the callback function during decoding (WICProgressOperationCopyPixels), during encoding (WICProgressOperationWritePixels), or both (WICProgressOperationAll).

enum WICProgressOperation
{        WICProgressOperationCopyPixels,
         WICProgressOperationWritePixels,
         WICProgressOperationAll            };

The CODEC should call the callback function at regular intervals throughout the operation, but the caller may specify certain requirements. The WICProgressNotification enum indicates at what point in the operation to call the callback function. If the caller specifies WICProgressNotificationBegin, you must call it at the beginning of the operation (0.0). If the caller does not specify this, it's optional. Likewise, if the caller specifies WICProgressNotificationEnd, you must call it when the operation is completed (1.0). If the caller specifies WICProgressNotificationAll, you must call it at the beginning and end, as well as at regular intervals throughout the operation. The caller may also specify WICProgressNotificationFrequent, which indicates that they want to be called back at frequent intervals, perhaps after every couple of scanlines. (A caller will usually only use this flag for a very large image.) Otherwise, it's reasonable to call back at intervals of approximately 10 percent increments of the total number of scanlines to be processed.

enum WICProgressNotification
{        WICProgressNotificationBegin,
         WICProgressNotificationEnd,
         WICProgressNotificationFrequent,
         WICProgressNotificationAll            };

Only one callback can be registered at a time for a given decoder or encoder instance. If an application calls RegisterProgressNotification more than once, replace the previously registered callback with the new one. To cancel a callback registration, a caller will set the pfnProgressNotification parameter to NULL.

PFNProgressNotification

The callback function will have the following signature.

      typedef HRESULT (*PFNProgressNotification) ( LPVOID pvData,
                                                   ULONG uFrameNum,
                                                   WICProgressOperation operation,
                                                   double dblProgress );

When you invoke the callback function, use the pvData parameter to pass back the same pvData that the application specified when it registered the callback function.

The uFrameNum parameter should indicate the index of the frame that's being processed.

Set the operation parameter to WICProgressOperationCopyPixels when decoding and WICProgressOperationWritePixels when encoding.

The dblProgress parameter should be a number between 0.0 (the beginning of the operation) and 1.0 (the completion of the operation). The value should reflect the proportion of scanlines already processed relative to the total number of scanlines to be processed.

IWICBitmapSource

IWICBitmapSource is a very important interface for dealing with images from an application perspective. It represents the highest level abstraction for an image source, and all WIC interfaces that represent an image, including IWICBitmapFrameDecode, IWICBitmap, and all the transform interfaces (IWICBitmapScaler, IWICBitmapClipper, IWICBitmapFlipRotator, and IWICFormatConverter) are derived from it. At any given time, an IWICBitmapSource object may or may not be backed by an actual bitmap in memory. This enables very efficient processing by an application, because an image can be dealt with as an abstraction, and transform operations can be chained in a transform pipeline without consuming memory resources until the application is ready to render or print the image, at which time it invokes the CopyPixels method on the final transform to get a bitmap in memory of the image with the selected transforms applied.

interface IWICBitmapSource : IUnknown

{

// Required methods

          HRESULT GetSize ( UINT *puiWidth,
                            UINT *puiHeight );

          HRESULT GetPixelFormat ( WICPixelFormatGUID *pPixelFormat );

          HRESULT GetResolution ( double *pDpiX,
                                  double *pDpiY );

          HRESULT CopyPixels ( const WICRect *prc,
                               UINT cbStride,
                               UINT cbBufferSize,
                               BYTE *pbBuffer );

// Optional method

          HRESULT CopyPalette ( IWICPalette *pIPalette );

}

From a CODEC perspective, the IWICBitmapSource methods are implemented on the frame decoder object, so we'll describe these methods below, along with the other methods on IWICBitmapFrameDecode, which is derived from IWICBitmapSource.

IWICBitmapFrameDecode

IWICBitmapFrameDecode is the frame-level interface that provides access to the actual image bits. You'll implement this interface on your frame-level decoding class. It's derived from IWICBitmapSource, so your implementation of IWICBitmapFrameDecode will include an implementation of the IWICBitmapSource methods. The additional methods on IWICBitmapFrameDecode provide access to the frame-level thumbnail, the color context(s) for the image, and the metadata query reader for the frame.

interface IWICBitmapFrameDecode : IWICBitmapSource

{

// Required methods

          HRESULT GetThumbnail ( IWICBitmapSource **ppIThumbnail );

          HRESULT GetColorContexts ( UINT cCount,
                                                    IWICColorContext **ppIColorContexts,
                                                    UINT *pcActualCount );

          HRESULT GetMetadataQueryReader ( IWICMetadataQueryReader
                                                                             **ppIMetadataQueryReader );

// Methods inherited from IWICBitmapSource (required)

          HRESULT GetSize ( UINT *puiWidth,
                                      UINT *puiHeight );

          HRESULT GetPixelFormat ( WICPixelFormatGUID *pPixelFormat );

          HRESULT GetResolution ( double *pDpiX,
                                               double *pDpiY );

          HRESULT CopyPixels ( const WICRect *prc,
                                          UINT cbStride,
                                          UINT cbBufferSize,
                                          BYTE *pbBuffer );

// Optional method

          HRESULT CopyPalette ( IWICPalette *pIPalette );

}

GetThumbnail returns the thumbnail for the current frame. For performance reasons, thumbnails are most commonly encoded in a JPEG format. Just like with the Preview on the decoder, it is not necessary or recommended to provide your own JPEG decoder for thumbnails. Instead, you should delegate to the JPEG decoder provided by WIC.

For more information on thumbnails, see the SetThumbnail method on the WICBitmapFrameEncode interface.

GetColorContexts returns the valid color context(s) (also known as color profiles) associated with the image in this frame. In most cases, this will only be one, but there could be cases where there are two or, rarely, more. The caller will pass in one or more IWICColorContext objects, setting the cCount parameter to indicate how many they're passing in. This method populates the IWICColorContext object(s) with the actual color context data for the color profile(s) associated with the image. Set the pcActualCount parameter to the actual number of color contexts associated with the image, even if this is greater than the number you can return. (In the case where there are more color contexts available than the number of IWICColorContext objects passed in by the caller, this lets the caller know there are other(s) available.)

GetMetadataQueryReader returns an IWICMetadataQueryReader that an application can use to retrieve metadata from the image frame. This interface is implemented by a metadata handler, and allows an application to query for specific metadata properties belonging to a particular metadata format. We'll discuss metadata handlers more in the section on IWICMetadataBlockReader.

To instantiate a MetadataQueryReader, call CreateQueryReaderFromBlockReader on the ComponentFactory.

IWICMetadataQueryReader* piQueryReader = NULL;
HRESULT hr;

hr = m_piComponentFactory->CreateQueryReaderFromBlockReader( 
     static_cast<IWICMetadataBlockWriter*>(this),&piQueryReader);

GetSize, GetPixelFormat, and GetResolution are self-explanatory, and simply return the requested properties of the image.

CopyPixels is the most interesting method on IWICBitmapSource. This is the method an application calls when it wants to create a bitmap in memory that can be rendered to the display or printer. This is the method that does the actual decoding of the image bits. The parameters are a rectangle, which represents the region of interest in the source image to copy into memory, the stride, which specifies the number of bytes in one scanline, the size of the buffer in memory that has been allocated by the application, and a pointer to the buffer into which the requested image bits should be copied. (To prevent potential buffer overruns from introducing security holes, be sure to only copy as much image data into the buffer as the cbBufferSize parameter specifies.)

CopyPalette only has to be implemented by CODECs that have indexed pixel formats. If an image uses an indexed format, use this method to return the palette of colors used in the image. If your CODEC doesn't have an indexed format, return WINCODEC_ERR_PALETTEUNAVAILABLE.

IWICMetadataBlockReader

There are often multiple blocks of metadata within an image, each exposing different types of information in different formats. In the WIC model, metadata handlers are distinct components that, like decoders, are discoverable at run time. There's a separate handler for each metadata format, and each of these metadata handlers can be used with any image format that supports the metadata format it handles. So, if your image format supports EXIF, XMP, IPTC, etc, you'll be able to take advantage of the standard metadata handlers for these formats that ship with WIC, and you won't need to write your own. Of course, if you create a new metadata format, you will need to write a metadata handler for it, which will be discovered and invoked at runtime just like the standard ones are, but that's beyond the scope of this whitepaper.

Note   If your image format is based on a TIFF or JPEG container, you won't need to write any metadata handlers (unless you develop a new or proprietary metadata format). In TIFF and JPEG containers, blocks of metadata are located within IFDs, and each container has a different IFD structure. WIC provides IFD handlers for both of these container formats that navigate the IFD structure and delegate to the standard metadata handlers to access the metadata within them. So, if your image format is based on either of these containers, you can automatically take advantage of the WIC IFD handlers. However, if you have a proprietary container format that has its own unique top level metadata structure, you'll need to write a handler that can navigate that top level structure and delegate to the appropriate metadata handlers, just like the IFD handlers do.

The same way WIC provides a layer of abstraction for applications that allows them to deal with all image formats in the same way through a consistent set of interfaces, WIC provides a layer of abstraction for CODEC authors with regard to metadata formats. As noted above, CODEC authors generally do not need to deal directly with the various metadata formats that may be present in an image. However, every CODEC author is responsible for providing a way to enumerate the blocks of metadata so an appropriate metadata handler can be discovered and instantiated for each block. That's the purpose of the IWICMetadataBlockReader interface.

You must implement this interface on your frame-level decoding class. You may also need to implement it on your container-level decoder class if your image format exposes global metadata outside of any individual image frames.

interface IWICMetadataBlockReader : IUnknown

{

// All methods required

          HRESULT GetContainerFormat ( GUID *pguidContainerFormat );

          HRESULT GetCount ( UINT *pcCount );

          HRESULT GetEnumerator ( IEnumUnknown **ppIEnumMetadata );

          HRESULT GetReaderByIndex ( UINT nIndex,
                                                      IWICMetadataReader **ppIMetadataReader );

}

GetContainerFormat is the same as the GetContainerFormat method on IWICBitmapDecoder.

GetCount returns the number of top-level metadata blocks associated with the frame.

GetEnumerator returns an enumerator that the caller can use to enumerate over the metadata blocks in the frame and read their metadata. To implement this method, you need to create a metadata reader for each block of metadata, and implement an enumeration object that enumerates over the collection of metadata readers. The enumeration object must implement IEnumUnknown so you can cast it to IEnumUnknown when you return it in the ppIEnumMetadata parameter.

When implementing the enumeration object, you can create all the metadata readers when you first create the IWICMetadataBlockReader object or when you first create the enumeration object, or you can create them lazily inside the implementation of the IEnumUnknown::Next method. In many cases, it's more efficient to create them lazily but, in the following example, (just to save space) I'm creating them all in the constructor.

public class MetadataReaderEnumerator : public IEnumUnknown
{
     UINT m_current;
     UINT m_blockCount;
   IWICMetadataReader** m_apiMetadataReader;
   IStream* m_piStream;
   MetadataReaderEnumerator() 
{
      // Set m_blockCount to the number of metadata blocks 
      //in the frame. 
      ...
      m_apiMetadataReader = 
           WICMetadataReader*[m_blockCount];
      m_current = 0;
      for (UINT x=0; x < m_blockCount; x++) 
{
   // Find the position in the file where the xth
   // block of metadata lives and seek m_piStream 
   // to that position.
   ...
   m_piComponentFactory-> 
     CreateMetadataReaderFromContainer(
     GUID_ContainerFormatTiff, NULL, 
     WICPersistOptions.WICPersistOptionsDefault |
        WICMetadataCreationOptions.WICMetadataCreationDefault   ,
     m_piStream, &m_apiMetadataReader[x]);
   }
   }

   // Implementation of IEnumUnknown and IUnknown interfaces
   ...
}

To create the metadata readers, you use the IWICComponentFactory:: CreateMetadataReaderFromContainer method. When invoking this method, you'll pass in the GUID of the container format in the guidContainerFormat parameter. If you have a preference of vendor for a metadata reader, you can pass the GUID of your preferred vendor in the pGuidVendor parameter. For example, if your company writes metadata handlers, and you'd prefer to use your own if present, you can pass in your vendor GUID. In most cases, you would just pass NULL, and let the system select the appropriate metadata reader. If you do request a specific vendor, and that vendor does have a metadata reader installed on the machine, WIC will return that vendor's reader. However, if the requested vendor does not have a metadata reader installed on the machine, and if there is an appropriate metadata reader available, that reader will be returned even though it is not from the preferred vendor. If there is no metadata reader on the machine for the type of metadata in the block, the component factory will return the Unknown Metadata Handler, which will treat the block of metadata as a BLOB, and will deserialize the block of metadata from the file without any attempt at parsing it.

For the dwOptions parameter, you'll OR the appropriate WICPersistOptions with the appropriate WICMetadataCreationOptions. The WICPersistOptions describe how your container is laid out. LittleEndian is the default.

enum WICPersistOptions
{       WICPersistOptionDefault,
        WICPersistOptionLittleEndian,
        WICPersistOptionBigEndian,
        WICPersistOptionStrictFormat,
        WICPersistOptionNoCacheStream,
        WICPersistOptionPreferUTF8        };

The WICMetadataCreationOptions specify whether you want to get back the UnknownMetadataHandler if no metadata reader is found on the machine that can read the metadata format of a particular block. AllowUnknown is the default, and you should always allow creation of the UnknownMetadataHandler. The UnknownMetadataHandler treats unrecognized metadata as a BLOB. It can't parse it, but it writes it out into the stream as a BLOB, and persists it intact when it's written back to the stream during encoding. This makes it safe to create metadata handlers for proprietary metadata or metadata formats that don't ship with the system. Because metadata is preserved intact, even if there's no handler present on the machine that recognizes it, when an appropriate metadata handler is later installed, the metadata will still be there and can be read. If you don't allow creation of the UnknownMetadataHandler, the alternative is discarding or overwriting unrecognized metadata. This is a form of data loss. (Note that, if you write your own metadata handler for proprietary metadata, you should never include references to anything outside the metadata block itself. Even though the UnknownMetadataHandler preserves metadata intact, metadata does get moved when files are edited, and any references to anything outside its own block will no longer be valid when this happens.)

enum WICMetadataCreationOptions
{       WICMetadataCreationDefault,
         WICMetadataCreationAllowUnknown,
         WICMetadataCreationFailUnknown            }

The pIStream parameter is the actual stream that you're decoding. Before passing in the stream, you should seek to the beginning of the metadata block for which you're requesting a reader. The appropriate metadata reader for the metadata block at the current position in the IStream will be returned in the ppiReader parameter.

GetReaderByIndex returns the metadata reader at the requested index in the collection.

IWICBitmapSourceTransform

Though optional, we highly recommend that every decoder implement this interface on your frame-level decoding class, because it can provide huge performance benefits. The idea behind this interface is that when an application requests a specific region of interest, size, orientation, or pixel format, instead of just decoding the whole image at full resolution and then applying the requested transforms, WIC will QI (invoke QueryInterface) for this interface on the IWICBitmapFrameDecode object. If the frame decoder supports it, WIC will call the appropriate method(s) to determine whether the frame decoder can perform the requested transform or ascertain the closest size or pixel format the decoder can provide to the one requested. If the decoder can perform the requested transform(s), WIC will invoke IWICBitmapSourceTransform::CopyPixels with the appropriate parameters. If the decoder can perform some, but not all of the requested transform(s), WIC will request the decoder to perform those that it can, and will use the WIC transform objects (IWICBitmapScaler, IWICBitmapClipper, IWICBitmapFlipRotator, and IWICFormatConverter) to perform the remaining transforms that could not be performed by the frame decoder on the result of the IWICBitmapSourceTransform::CopyPixels call. If the decoder doesn't support IWICBitmapSourceTransform, then WIC will have to use the transform objects to perform all of the transforms. It's usually much more efficient for the decoder to perform transforms during the decoding process than it is to decode the entire image and then perform the transforms. This is especially true for operations like scaling to a much smaller size or pixel format conversions.

interface IWICBitmapSourceTransform : IUnknown

{

         // Required methods

         HRESULT DoesSupportTransform ( WICTransformOptions dstTransform,
                                                           BOOL *pfIsSupported);

         HRESULT CopyPixels ( WICRect *prcSrc,
                                          UINT uiWidth,
                                          UINT uiHeight,
                                          WICPixelFormatGUID * pguidDstFormat,
                                          WICBitmapTransformOptions dstTransform,
                                          UINT nStride,
                                          UINT cbBufferSize,
                                          BYTE *pbBuffer );

// Optional methods

         HRESULT GetClosestSize ( UINT *puiWidth,
                                               UINT *puiHeight);

         HRESULT GetClosestPixelFormat ( WICPixelFormatGUID *pguidDstFormat);

}

DoesSupportTransform asks whether the decoder supports the requested rotation or flipping operation. The WICBitmapTransformOptions that may be requested are:

enum WICBitmapTransformOptions
{       WICBitmapTransformRotate0,          WICBitmapTransformRotate90,          WICBitmapTransformRotate180,          WICBitmapTransformRotate270,          WICBitmapTransformFlipHorizontal, WICBitmapTransformFlipVertical                }

CopyPixels is the method that does the actual work of decoding the image bits, like the CopyPixels method on the IWICBitmapSource interface, but the CopyPixels method on IWICBitmapSourceTransform is a lot more interesting and powerful, and can improve image processing performance significantly.

When multiple transform operations are requested, the result is dependent on the order in which the operations are performed. To ensure predictability and consistency across CODECs, it's important that all CODECs perform these operations in the same order. This is the canonical order for performing these operations.

  1. Scale
  2. Crop
  3. Rotate

Pixel format conversion can be performed at any time, since it has no effect on the other transforms.

The first parameter, prcSrc is used to specify the region of interest for clipping the image. By convention, scaling is performed before clipping so, if the image is to be scaled as well as clipped, the region of interest should be determined after the image has been scaled.

The second and third parameters indicate the size to which to scale the image.

The pguidDstFormat parameter indicates the requested pixel format for the decoded image. Because WIC has already called GetClosestPixelFormat, this should be a pixel format that the decoder has indicated that it supports.

The dstTransform parameter indicates the requested rotation angle, and/or whether to flip the image vertically and/or horizontally. Again, WIC will have already called

DoesSupportTransform, so the requested transform should be one that the decoder has already indicated that it supports. Remember that rotation should always be performed after scaling and clipping.

GetClosestSize takes two in/out parameters. The caller will use the puiWidth and puiHeight parameters to specify the size at which that the caller would ideally like the image to be decoded. However, a decoder can only decode an image to a size that's a multiple of its DCT size, and different image formats can have different DCT sizes. So the decoder should determine, based on its own DCT size, the closest it can come to the requested size, and set the puiWidth and puiHeight to those dimensions on return. If a larger size is requested, but the CODEC doesn't support upscaling, the original should be returned.

GetClosestPixelFormat is used to determine the closest pixel format to the requested pixel format that the decoder can provide without loss of data. The key point is that it's always better to convert to a wider pixel format than a narrower one, even though it will increase the size of the image, because it can always be reconverted to a more restrictive format if necessary but, once the data is lost, it can't be recovered.

IWICDevelopRaw

This interface exposes processing options specific to RAW image processing. All Raw CODECs must support the IWICDevelopRaw interface. Some Raw CODECs may not be able to support every setting exposed by this interface, but you should support all the setting that your CODEC is capable of performing. At minimum, every Raw CODEC is required to implement the SetRotation and SetRenderQuality methods.

Additionally, some methods and interfaces that are optional for other CODECs are strongly recommended for Raw CODECs. These include the GetPreview and GetThumbnail methods on the container-level decoder class, and the IWICBitmapSourceTransform interface on the frame-level decode class.

Settings set by using the IWICDevelopRaw methods should be persisted by the CODEC in a way that's consistent with the way other metadata is persisted, but you should never overwrite the original "As Shot" settings. By persisting the metadata and implementing LoadParameterSet and GetCurrentParameterSet, you enable Raw processing applications to retrieve and apply processing settings across sessions.

One key purpose of the IWICDevelopRaw interface is to enable application developers to build a user interface for adjusting Raw parameters that will work as consistently as possible across different CODECs. Assume that an end-user will adjust the parameters by using a slider control, with its minimum and maximum values mapped to the minimum and maximum ranges for the parameter. To support this, you should make every effort to treat all parameter ranges as linear. To ensure that the slider controls aren't overly sensitive, you should also support as broad a range as possible for each parameter, covering at least 50 percent of the maximum possible range. For example, if the maximum possible range of contrast is from pure gray to pure black/white, with the default value being mapped to 0.0, the minimum range supported by a CODEC would be from at least half way between the default value and pure gray on the low end (-1.0), to at least half way between the default value and pure black/white on the high end (+1.0).

interface IWICDevelopRaw : IWICBitmapFrameDecode

{

          HRESULT QueryRawCapabilitiesInfo ( WICRawCapabilitiesInfo *pInfo );

          HRESULT LoadParameterSet ( WICRawParameterSet parameterSet );

          HRESULT GetCurrentParameterSet ( IPropertyBag2 **ppCurrentParameterSet );

          HRESULT SetExposureCompensation ( double ev );

          HRESULT GetExposureCompensation ( double *pEV );

          HRESULT SetWhitePointRGB ( UINT Red, UINT Green, UINT Blue );

          HRESULT GetWhitePointRGB ( UINT *pRed, UINT *pGreen, UINT *pBlue );

          HRESULT SetNamedWhitePoint ( WICNamedWhitePoint whitePoint );

          HRESULT GetNamedWhitePoint ( WICNamedWhitePoint *pWhitePoint );

          HRESULT SetWhitePointKelvin ( UINT whitePointKelvin );

          HRESULT GetWhitePointKelvin ( UINT *pWhitePointKelvin );

          HRESULT GetKelvinRangeInfo ( UINT *pMinKelvinTemp,
                                                       UINT *pMaxKelvinTemp,
                                                       UINT *pKelvinTempStepValue );

          HRESULT SetContrast ( double contrast );

          HRESULT GetContrast (double *pContrast );

          HRESULT SetGamma ( double gamma );

          HRESULT GetGamma (double *pGamma );

          HRESULT SetSharpness ( double sharpness );

          HRESULT GetSharpness ( double *pSharpness );

          HRESULT SetSaturation ( double saturation );

          HRESULT GetSaturation ( double *pSaturation );

          HRESULT SetTint ( double tint );

          HRESULT GetTint ( double *pTint );

          HRESULT SetNoiseReduction ( double noiseReduction );

          HRESULT GetNoiseReduction ( double *pNoiseReduction );

          HRESULT SetDestinationColorContext (const IWICColorContext
                                                                                             *pColorContext );

          HRESULT SetToneCurve ( UINT cbToneCurveSize,
                                                const WICRawToneCurve *pToneCurve );

          HRESULT GetToneCurve ( UINT cbToneCurveBufferSize,
                                                WICRawToneCurve *pToneCurve,
                                                UINT *pcbActualToneCurveBufferSize );

          HRESULT SetRotation ( double rotation );

          HRESULT GetRotation ( double *pRotation );

          HRESULT SetRenderQuality ( WICRawRenderQuality renderQuality );

          HRESULT GetRenderQuality ( WICRawRenderQuality *pRenderQuality );

          HRESULT SetNotificationCallback ( IWICDevelopRawNotificationCallback
                                                                                                      *pCallback );

}

QueryRawCapabilitiesInfo returns the set of supported capabilities for this raw file. The IWICRawCapabilitiesInfo struct is defined as follows.

struct WICRawCapabilitiesInfo
{
         UINT cbSize;
         UINT CodecMajorVersion;
         UINT CodecMinorVersion;
         WICRawCapabilities ExposureCompensationSupport;
         WICRawCapabilities ContrastSupport;
         WICRawCapabilities RGBWhitePointSupport;
         WICRawCapabilities NamedWhitePointSupport;
         UINT NamedWhitePointSupportMask;
         WICRawCapabilities KelvinWhitePointSupport;
         WICRawCapabilities GammaSupport;
         WICRawCapabilities TintSupport;
         WICRawCapabilities SaturationSupport;
         WICRawCapabilities SharpnessSupport;
         WICRawCapabilities NoiseReductionSupport;
         WICRawCapabilities DestinationColorProfileSupport;
         WICRawCapabilities ToneCurveSupport;
         WICRawRotationCapabilities RotationSupport;
}

The WICRawCapabilities enumeration used in this struct is defined as:

enum WICRawCapabilities
{       WICRawCapabilityNotSupported,
         WICRawCapabilityGetSupported,
         WICRawCapabilityFullySupported        }

The final field is a WICRawRotationCapabilities enumeration, defined as:

enum WICRawRotationCapabilities
{       WICRawRotationCapabilityNotSupported,
        WICRawRotationCapabilityGetSupported,
        WICRawRotationCapabilityNinetyDegreesSupported
        WICRawRotationCapabilityFullySupported            }

LoadParameterSet enables the user to specify whether to use As Shot settings, user adjusted settings, or request the decoder to auto correct the image.

enum WICRawParameterSet
{       WICAsShotParameterSet,
        WICUserAdjustedParameterSet,
        WICAutoAdjustedParameterSet        }

GetCurrentParameterSet returns an IPropertyBag2 with the current parameter set. The caller can then pass this parameter set to the encoder to use as the encoder options.

Set/GetExposureCompensation indicates the exposure compensation to apply to the final output. The valid range for EV is -5.0 to +5.0 stops.

Set/GetWhitePointRGB, Set/GetNamedWhitePoint, Set/GetWhitePointKelvin, and GetKelvinRangeInfo all provide ways to get and set the white point, either as an RGB value, a preset named value, or as a Kelvin value. The acceptable range for Kelvin is 1,500 - 30,000.

SetContrast/GetContrast indicates the amount of contrast to apply to the output. The valid range to specify contrast is -1.0 to +1.0, with the default contrast being 0.0.

Set/GetGamma indicates the gamma to apply. The valid range for Gamma is 0.2 to 5.0, with 1.0 being the default. Gamma typically is implemented using the traditional Gamma power function (a linear power function with unity gain). Brightness is increased with increasing Gamma and decreased as Gamma approaches zero. (Note that the minimum value is non-zero, since zero would result in a divide by zero error in traditional Gamma calculations. The logical minimum limit is 1/max, which is why the minimum is 0.2.)

Set/GetSharpness indicates the amount of sharpening to apply. The valid range is -1.0 to +1.0, with 0.0 being the default amount of sharpening, and -1.0 indicating no sharpening at all.

Set/GetSaturation indicates the amount of saturation to apply. The valid range to specify saturation is -1.0 to +1.0, with 0.0 being normal saturation, -1.0 representing complete desaturation, and +1.0 representing full saturation.

Set/GetTint indicates the tint to apply, on a green/magenta bias. The valid range is -1.0 to +1.0, with green being on the negative side of the scale and magenta on the positive. The tint scale is defined as orthogonal to color temperature.

Set/GetNoiseReduction indicates the amount of noise reduction to apply. The valid range to is -1.0 to +1.0, with 0.0 indicating the default amount of noise reduction, -1.0 indicating no noise reduction and +1.0 indicating maximum noise reduction.

SetDestinationColorContext specifies the color profile to apply to the image. You can call IWICBitmapFrameDecode::GetColorContext to retrieve the current color profile.

Set/GetToneCurve specifies the tone curve to apply. Assume linear interpolation between points. The pToneCurve is a WICRawToneCurve struct, which contains an array of WICRawToneCurvePoint structs, and a count of the number of points in the array.

struct WICRawToneCurve
{
          UINT cPoints;
          WICRawToneCurvePoint aPoints[];
}

A WICRawToneCurvePoint contains an input value and an output value.

struct WICRawToneCurvePoint
{
          double Input;
          double Output;
}

When the caller passes NULL in the pToneCurve parameter, you should pass back the required size for the WICRawToneCurve in the pcbActualToneCurveBufferSize parameter.

Set/GetRotation indicates the degree of rotation to apply. A rotation of 90.0 would specify a rotation of 90 degrees clockwise. (The difference between using IWICDevelopRaw::SetRotation and setting rotation via the IWICBitmapSourceTransform::CopyPixels method is that that the rotation angle set using IWICDevelopRaw::SetRotation should be persisted by the CODEC, while setting rotation through IWICBitmapSourceTransform::CopyPixels only rotates the image in memory.

Set/GetRenderQuality indicates the quality level of output the caller requires. When a user is tweaking parameters, the application wants to be able to display a very fast approximation of what the actual image will look like if the changes are applied. For this purpose, the image is usually displayed at screen resolution or less, rather than the actual image resolution, and the most important thing is to provide immediate feedback to the user. This is when an application would request Draft Mode quality, so this should be very fast. When the user has made all their changes, previewed them in draft mode, and decided they want to decode the full image with the current settings, the application will request a Best Quality decode. This is usually also requested for printing. Where a reasonable tradeoff between speed and quality is required, the application will request Normal Quality.

enum WICRawRenderQuality
{       WICRawRenderQualityDraftMode,          WICRawRenderQualityNormalQuality,          WICRawRenderQualityBestQuality        }

SetNotificationCallback registers a callback function for the decoder to call when any of the Raw processing parameters change. The signature for the IWICDevelopRawNotificationCallback has only one method, called Notify. Notify has a single parameter, which is a mask that indicates which of the Raw processing parameters have changed.

HRESULT Notify ( UINT NotificationMask );

The following values can be OR'd together for the NotificationMask.

WICRawChangeNotification_ExposureCompensation
WICRawChangeNotification_NamedWhitePoint
WICRawChangeNotification_KelvinWhitePoint
WICRawChangeNotification_RGBWhitePoint
WICRawChangeNotification_Contrast
WICRawChangeNotification_Gamma
WICRawChangeNotification_Sharpness
WICRawChangeNotification_Saturation
WICRawChangeNotification_Tint
WICRawChangeNotification_NoiseReduction
WICRawChangeNotification_DestinationColorContext
WICRawChangeNotification_ToneCurve
WICRawChangeNotification_Rotation
WICRawChangeNotification_RenderMode

Implementing a WIC-Enabled Encoder

Just like implementing a WIC decoder, implementing a WIC encoder requires writing two classes. The interfaces on these classes correspond directly to the encoder responsibilities outlined in the Encoding section of How the Windows Imaging Component Works.

One of the classes provides container-level services and manages the serialization of the individual image frames within the container. This class implements the IWICBitmapEncoder interface. If your image format supports container-level metadata, you'll also need to implement the IWICMetadataBlockWriter interface on this class.

The other class provides frame-level services and does the actual encoding of the image bits for each frame in the container. It also iterates through the metadata blocks for each frame and requests the appropriate metadata writers to serialize the blocks. This class implements the IWICBitmapFrameEncode interface and the IWICMetadataBlockWriter interface. This class should have an IStream member that the container-level class initializes on instantiation, into which the Commit method will serialize the frame data

In some cases, such as Raw formats, the author doesn't necessarily want applications to be able to encode or re-encode to the Raw format, because the purpose of a Raw file is to contain the sensor data exactly as it came from the camera. In cases where the CODEC author doesn't want to enable encoding, it's still necessary to implement a rudimentary encoder simply to enable adding metadata. In that case, the encoder need only support those methods necessary for writing metadata, and can copy the image bits untouched from the decoder.

Encoder Interfaces

The following table shows the interfaces implemented by WIC encoders, and the class diagram shows the inheritance hierarchy.

Container-Level Encoder Interfaces

InterfaceResponsibilitiesImplementation
IWICBitmapEncoderContainer-level servicesRequired
IWICBitmapCodecProgressNotificationProgress notification and cancellation supportRecommended
IWICMetadataBlockWriterMetadata serialization servicesOptional (Required only if format supports container-level metadata)

Frame-Level Encoder Interfaces

InterfaceResponsibilitiesImplementation
IWICBitmapFrameEncodeFrame-level servicesRequired
IWICMetadataBlockWriterMetadata serialization servicesRequired

Aa905327.wiccodec02(en-us,MSDN.10).jpg

Figure 2. WIC encoder interfaces

You'll notice that the encoder interfaces are almost mirror images of the decoder interfaces, and that most of the methods on these interfaces correspond to methods on the related decoder interfaces. Now that you're familiar with the implementation of a WIC-enabled decoder, the implementation of a WIC-enabled encoder will seem familiar as well.

IWICBitmapEncoder

This interface is the counterpart to the IWICBitmapDecoder interface and is the starting point for encoding an image file. Just as IWICBitmapDecoder is used for retrieving container-level properties and individual frames from the image container, IWICBitmapEncoder is used for setting container-level properties and serializing individual image frames into the container. You'll implement this interface on your container-level encoder class.

interface IWICBitmapEncoder : public IUnknown

{

// Required methods

          HRESULT Initialize ( IStream *pIStream,
                                       WICBitmapEncoderCacheOption cacheOption );

HRESULT GetContainerFormat ( GUID *pguidContainerFormat );

          HRESULT GetEncoderInfo ( IWICBitmapEncoderInfo **pIEncoderInfo );

          HRESULT CreateNewFrame ( IWICBitmapFrameEncode **ppIFrameEncode,
                                                    IPropertyBag2 **ppIEncoderOptions );

          HRESULT Commit ( void );

// Optional methods

          HRESULT SetPreview ( IWICBitmapSource *pIPreview );

          HRESULT SetThumbnail ( IWICBitmapSource *pIThumbnail );

          HRESULT SetColorContexts ( UINT cCount,
                                                    IWICColorContext **ppIColorContext );

          HRESULT GetMetadataQueryWriter ( IWICMetadataQueryWriter
                                                                               **ppIMetadataQueryWriter );

          HRESULT SetPalette ( IWICPalette *pIPalette);

};

You'll recall from our discussion on decoders that some image formats have global thumbnails, color contexts, and/or metadata, while many image formats only provide these on a per-frame basis. Therefore, the methods for setting these are optional on IWICBitmapEncoder, but are required on IWICBitmapFrameEncode. We'll discuss the methods that are optional on IWICBitmapEncoder in the section on IWICBitmapFrameEncode, where they're most commonly implemented.

If you don't support global thumbnails, return WINCODEC_ERR_CODECNOTHUMBNAIL from the SetThumbnail method on IWICBitmapEncoder. If you don't support a container-level palette, or if the image you're encoding doesn't have an indexed format, return WINCODEC_ERR_PALETTEUNAVAILABLE from the SetPalette method. For any other unsupported methods, return WINCODEC_ERR_UNSUPPORTEDOPERATION.

Initialize is the first method invoked on an IWICBitmapEncoder after it's been instantiated. An image stream is passed to the encoder, and a caller may optionally specify a cache option. In the case of the decoder, the stream is read-only, but the stream passed to an encoder is a writeable stream, into which the encoder will serialize all the image data and metadata. The cache options on the encoder are different as well.

enum WICBitmapEncoderCacheOption
{       WICBitmapEncoderCacheInMemory,          WICBitmapEncoderCacheTempFile,          WICBitmapEncoderNoCache        }

The application has a choice of requesting the encoder to cache the image data in memory, cache it in a temporary file, or to write it directly into the disk file with no caching. When asked to cache the data in a temporary file, the encoder should create a temporary file on disk and write directly to that file without caching in memory. When the caller selects the no cache option, each frame must be committed in order before the next frame can be created.

GetContainerFormat is implemented the same way as the GetContainerFormat method on IWICBitmapDecoder.

GetEncoderInfo returns an IWICBitmapEncoderInfo object. To get the IWICBitmapEncoderInfo object, just pass the GUID of your encoder to the CreateComponentInfo method on IWICComponentFactory, and then request the IWICEncoderInfo interface on it.

See the example under IWICBitmapDecoder::GetDecoderInfo.

CreateNewFrame is the encoder counterpart of GetFrame on IWICBitmapDecoder. This method returns an IWICBitmapFrameEncode object, which is the object that actually serializes the image data for a given frame within the container.

One of the benefits of WIC is that it provides a layer of abstraction for applications that enables them to deal with all image formats in the same way, but clearly not all image formats are exactly the same. Some image formats have capabilities that others don't have and, for applications to be able to take advantage of those unique capabilities, it's necessary to provide a way for the CODEC to expose them. That's the purpose of encoder options. If your CODEC supports any encoder options, you should create an IPropertyBag2 object that exposes the encoder options you support, and return it in the ppIEncoderOptions parameter of this method. The caller can then use this IPropertyBag2 object to ascertain what encoder options your CODEC supports. If the caller wants to specify values for any of the supported encoder options, they'll assign the value to the relevant property in the IPropertyBag2 object and pass it to the newly created IWICBitmapFrameEncode object in its Initialize method.

To instantiate an IPropertyBag2 object, you first need to create a PROPBAG2 struct to specify each encoder option your encoder supports and its data type for each property. Then you need to implement an IPopertyBag2 object that enforces the value ranges for each property on write, and reconciles any conflicting or overlapping values. For simple sets of non-conflicting encoder options, you can invoke the IWICComponentFactory::CreateEncoderPropertyBag method, which will create a simple IPropertyBag2 object for you using the properties you specify in your PROPBAG2 struct. You'll still need to enforce the value ranges. For more advanced encoder options, or if you need to reconcile conflicting values, you'll want to write your own IPropertyBag2 implementation.

UINT cuiPropertyCount = 0;
IPropertyBag2* piPropertyBag = NULL;
PROPBAG2* pPropBagOptions;
HRESULT hr;

// Insert code here to initialize piPoropertyBag with the 
// supported options for your encoder, and to initialize 
// cuiPropertyCount to the number of encoder option properties
// you're exposing.
...

hr = piComponentFactory->CreateEncoderPropertyBag( 
                   pPropBagOptions, cuiPropertyCount, &piPropertyBag );

WIC provides a small set of canonical encoder options that are used by some of the common image formats. All of the canonical encoder options are optional, and CODECs are not required to support any of them. The reason they're provided as canonical options is because many applications expose UI for users to specify these options when saving an image file in a format that supports them. Providing a canonical way to specify these options makes it easy for applications to communicate them to encoders in a consistent way. The canonical encoder options are listed in the table below.

Encoder OptionVARTYPEValue Range
LosslessVT_BOOLTrue/False
ImageQualityVT_R40-1.0
CompressionQualityVT_R40-1.0
BitmapTransformVT_UI1WICBitmapTransformOptions

If your CODEC supports lossless encoding, you should expose the Lossless encoder option as a way for applications to request that an image be losslessly encoded. If a caller sets this property to True, you should ignore the ImageQuality option and encode the image losslessly.

The ImageQuality option allows an application to specify the degree of fidelity with which to encode the image. This option lets a user make a tradeoff between image quality versus speed and/or file size. JPEG is an example of an image format that supports this tradeoff. A value of 0.0 indicates that fidelity is of low importance and the encoder should use its most lossy algorithm. A value of 1.0 indicates that fidelity is paramount and the encoder should preserve the highest fidelity possible. (Depending on your CODEC, this may be synonymous with the Lossless option. However, if your CODEC supports lossless encoding, and if the Lossless option is set to True, the ImageQuality option should be ignored.)

The CompressionQuality option allows an application to specify the efficiency of compression to use when encoding the image. A very efficient algorithm may produce a smaller image file with the same quality as a less efficient compression algorithm, but may take longer to encode. This option lets a user specify a tradeoff between file size versus speed of encoding, while preserving the same level of quality. TIFF is an example of an image format that supports this tradeoff. (Note that a format like JPEG supports different levels of compression, but a higher rate of compression results in lower image quality. Therefore, a JPEG CODEC would expose the ImageQuality option rather than the CompressionQuality option.) A value of 0.0 for this option indicates that you should compress the image as quickly as possible, without reducing fidelity, at the expense of a larger file size. A value of 1.0 indicates that you should create the smallest possible file size (at the same level of quality), regardless of how long it may take to encode it. A CODEC may support both the ImageQuality option and the CompressionQuality option, where the ImageQuality option specifies the acceptable degree of lossiness, and the CompressionQuality option offers a size/speed tradeoff at the specified quality level.

The BitmapTransform option provides a way for the caller to specify a rotation angle or vertical or horizontal flip orientation when encoding. The WICBitmapTransformOptions enum used to specify the requested transform is the same enum used when requesting a transform during decoding through the IWICBitmapSourceTransform interface.

Note that encoders are not limited to the canonical encoder options. The purpose of encoder options is to enable encoders to expose their capabilities, and there is no limit to the types of capabilities you can expose. You should be sure you encoder options are well documented because, even though an application can use the property bag you return from this method to discover the names, types, and value ranges for the options you support, the only way for them to find out their meanings, or how to expose them in UI, is from your documentation.

Commit is the method you call after all the image data and metadata have been serialized into the stream. You should use this method to serialize the Preview image data into the stream, and any global thumbnails, metadata, palette, etc., if applicable. This method should not close the file stream, because the application that opened the stream is expected to close it.

The section on the IWICBitmapFrameEncode:Commit method has details on how the IWICBitmapEncoderCacheOptions affect the behavior of this method.

SetPreview is used to create a preview of the image. While it is not strictly required that every image have a preview, it's highly recommended. Modern digital cameras and scanners generate very high resolution images, which tend to be very large and, consequently, take significant processing time to decode. Images from the next generation of cameras will be even larger. In the interest of providing a good experience for the user, it's very advisable to provide a smaller, lower resolution version of an image, typically in JPEG format, that can be quickly decoded and displayed "instantly" when a user requests it. An application may request a preview before requesting the actual image to be decoded so they can provide a better experience for the user, and at least show them a screen-size representation of the image while they're waiting to decode the actual image. While it's advisable for all CODECs to provide previews, CODECs that do not support IWICBitmapSourceTransform should definitely do so.

If you provide a JPEG preview, you don't have to write a JPEG encoder to encode it. You should delegate to the JPEG encoder that ships with the WIC platform for encoding both previews and thumbnails.

IWICBitmapCodecProgressNotification

The IWICBitmapCodecProgressNotification interface is optional, but recommended to be implemented on the container-level encoder class. This interface is discussed in detail in the Decoder section of this paper. The implementation is the same for either the decoder or the encoder.

IWICBitmapFrameEncode

This interface is the encoder's counterpart to the IWICBitmapFrameDecode interface. You'll implement this interface on your frame-level encoding class, which is the class that does the actual encoding of the image bits for each frame.

interface IWICBitmapFrameEncode : public IUnknown

{

// Required methods

          HRESULT >Initialize ( IPropertyBag2 *pIEncoderOptions );

          HRESULT SetSize ( UINT width,
                                      UINT height );

          HRESULT SetResolution ( double dpiX,
                                               double dpiY );

          HRESULT SetPixelFormat (WICPixelFormatGUID *pPixelFormat);

          HRESULT SetColorContexts ( UINT cCount,
                                                    IWICColorContext **ppIColorContext );

          HRESULT GetMetadataQueryWriter ( IWICMetadataQueryWriter
                                                                               **ppIMetadataQueryWriter );

          HRESULT SetThumbnail ( IWICBitmapSource *pIThumbnail );

          HRESULT WritePixels ( UINT lineCount,
                                           UINT cbStride,
                                           UINT cbBufferSize,
                                           BYTE *pbPixels );

          HRESULT WriteSource ( IWICBitmapSource *pIWICBitmapSource,
                                             WICRect *prc );

          HRESULT Commit ( void );

// Optional method

          HRESULT SetPalette ( IWICPalette *pIPalette );

};

Initialize is the first method invoked on an IWICBitmapFrameEncode object after it's been instantiated. This method has one parameter, which is optional. This parameter represents the encoder options, and is the same IPropertyBag2 instance that you created in the CreateNewFrame method on the container-level decoder, and passed back to the caller in the pIEncoderOptions parameter of that method. At that time, you populated the IPropertyBag2 struct with properties that represent the encoding options supported by your frame-level encoder. The caller has now provided values for those properties to indicate the desired encoding option parameters, and is passing the same object back to you to initialize the IWICBitmapFrameEncode object. You should apply the specified options when encoding the image bits.

SetSize and SetResolution are self-explanatory. The caller will use these methods to specify the size and resolution for the encoded image.

SetPixelFormat is used to request a pixel format in which to encode the image. If the requested pixel format is not supported, you should return the GUID of the closest pixel format that is supported in pPixelFormat, which is an in/out parameter.

SetColorContexts is used to specify one or more valid color contexts (also known as color profiles) for this image. It's important to specify the color context(s), so an application that decodes the image at a later time can convert from the source color profile to the destination profile of the device being used to display or print the image. Without a color profile, it's impossible to get consistent colors across different devices, which is a source of great frustration for end users when their photos look different when viewed on different monitors, and they can't get the prints to match what they see on screen.

GetMetadataQueryWriter returns an IWICMetadataQueryWriter that an application can use to insert or edit specific metadata properties in a metadata block on the image frame.

There are a couple of ways to instantiate an IWICMetadataQueryWriter from the IWICComponentFactory. You can either create it from your IWICMetadataBlockWriter, the same way we created an IWICMetadataQueryReader from an IWICMetadataBlockReader in the GetMetadataQueryReader method on the IWICBitmapFrameDecode interface.

IWICMetadataQueryWriter* piMetadataQueryWriter = NULL;
HRESULT hr;

hr = m_piComponentFactory->CreateQueryWriterFromBlockWriter( 
       static_cast<IWICMetadataBlockWriter*>(this), 
       &piMetadataQueryWriter);

You can also create it from an existing IWICMetadataQueryReader, like the one we obtained using the aforementioned method.

hr = m_piComponentFactory->CreateQueryWriterFromReader( 
       piMetadataQueryReader, pguidVendor, &piMetadataQueryWriter);

The pguidVendor parameter allows you to specify a particular vendor for the QueryWriter to use when instantiating a metadata writer. For example, if you provide your own metadata writers, you may want to specify your own vendor GUID. This parameter is optional, and you can pass NULL to it if you don't have a preference.

SetThumbnail is used to provide a thumbnail. All images should provide a thumbnail either globally or on each frame, or both. The GetThumbnail and SetThumbnail methods are optional at the container-level but, if a CODEC returns WINCODEC_ERR_CODECNOTHUMBNAIL from the GetThumbnail method on IWICBitmapDecoder, WIC will invoke the GetThumbnail method on the IWICBitmapFrameDecode for Frame 0. If no thumbnail is found in either place, WIC will have to decode the full image and scale it to thumbnail size, which could incur a large performance penalty for larger images.

The thumbnail should be of a size and resolution that makes it quick to decode and display. For this reason, thumbnails are most commonly JPEG images. Note that, if you use JPEG for your thumbnails, you don't have to write a JPEG encoder to encode them, or a JPEG decoder to decode them. You should always delegate to the JPEG CODEC that ships with the WIC platform for encoding and decoding thumbnails.

WritePixels is the method used to pass in scanlines from a bitmap in memory for encoding. The method will be called repeatedly until all the scanlines have been passed in. The lineCount parameter indicates how many scanlines are to be written in this call. The cbStride parameter indicates the number of bytes per scanline. cbBufferSize indicates the size of the buffer passed in the pbPixels parameter, which contains the actual image bits to be encoded. If the combined height of the scanlines from cumulative calls is greater than the height specified in SetSize method, return WINCODEC_ERR_TOOMANYSCANLINES.

When the WICBitmapEncoderCacheOption is WICBitmapEncoderCacheInMemory, the scanlines should be cached in memory until all scanlines have been passed in. If the encoder cache option is WICBitmapEncoderCacheTempFile, the scanlines should be cached in a temporary file, created when initializing the object. In either of these cases, the image should not be encoded until the caller calls Commit. In the case where the cache option is WICBitmapEncoderNoCache, the encoder should encode the scanlines as they're received, if possible. (In some formats, this is not possible, and the scanlines have to be cached until Commit is called.)

It's likely that most Raw CODECs will not implement WritePixels, because they don't support altering the image bits in the Raw file. Raw CODECs should still implement WriteSource, however, because when metadata is added, it may increase the size of the file, requiring the file to be rewritten on the disk. In that case, it's necessary to be able to copy the existing image bits, which is what the WriteSource method does.

WriteSource is used to encode an IWICBitmapSource object. The first parameter is a pointer to an IWICBitmapSource object. The second parameter is a WICRect that specifies the region of interest to encode. This method may be called multiple times in succession, as long as the width of each rectangle is the same width of the final image to be encoded. If the width of the rectangle passed in the prc parameter is different than the width specified in the SetSize method, return WINCODEC_ERR_SOURCERECTDOESNOTMATCHDIMENSIONS. If the combined height of the scanlines from cumulative calls is greater than the height specified in SetSize method, return WINCODEC_ERR_TOOMANYSCANLINES.

The cache options work the same way with this method as with the WritePixels method described previously.

Commit is the method that serializes the encoded image bits to the file stream, and iterates through all the metadata writers for the frame requesting them to serialize their metadata to the stream. In the case where the encoder cache option is WICBitmapEncoderCacheInMemory or WICBitmapEncoderCacheTempFile, this method is also responsible for encoding the image, and when the cache option is WICBitmapEncoderCacheTempFile, the Commit method should also delete the temporary file used to cache the image data before encoding.

This method is always invoked after all the scanlines or rectangles that make up the image have been passed in using either the WritePixels or WriteSource method. The size of the final rectangle that is composed through the accumulated calls to WritePixels or WriteSource must be the same size as was specified in the SetSize method. If the size does not match the expected size, this method should return WINCODECERROR_UNEXPECTEDSIZE.

To iterate through the metadata writers and tell each metadata writer to serialize its metadata to the stream, invoke GetWriterByIndex to iterate through the writers for each block, and invoke IWICPersistStream.Save on each metadata writer.

IWICMetadataWriter* piMetadataWRiter = NULL;
IWICPersistStream* piPersistStream = NULL;
HRESULT hr;
for (UINT x=0; x < m_blockCount; x++) 
{
    hr = GetWriterByIndex(x, & piMetadataWriter);
    hr = piMetadataWriter->QueryInterface( 
         IID_IWICPersistStream,(void**)&piPersistStream);
    
    hr = piPersistStream->Save(m_piStream, 
       WICPersistOptions.WicPersistOptionDefault, true);
    ...
}

SetPalette only has to be implemented by CODECs that have indexed formats. If an image uses an indexed format, use this method to specify the palette of colors used in the image. If your CODEC doesn't have an indexed format, return WINCODEC_ERR_PALETTEUNAVAILABLE from this method.

IWICMetadataBlockWriter

The frame-level encoding class implements this interface to expose all the metadata blocks and requests the appropriate metadata writer for each block. If your image format supports global metadata, outside of any individual frame, you'll also want to implement this interface on the container-level encoder class. For a more detailed discussion of metadata handlers, refer to the section on the IWICMetadataBlockReader in the section on Implementing a WIC-Enabled Decoder.

interface IWICMetadataBlockWriter : IWICMetadataBlockReader

{

// All methods required

          HRESULT InitializeFromBlockReader ( IWICMetadataBlockReader
                                                                                         *pIMDBlockReader );

          HRESULT GetWriterByIndex ( UINT nIndex,
                                                     IWICMetadataWriter **ppIMetadataWriter );

          HRESULT AddWriter (IWICMetadataWriter *pIMetadataWriter );

          HRESULT SetWriterByIndex ( UINT nIndex,
                                                     IWICMetadataWriter *pIMetadataWriter );

          HRESULT RemoveWriterByIndex ( UINT nIndex );

}

InitializeFromBlockReader uses an IWICMetadataBlockReader to initialize the block writer. You can get the IWICMetadataBlockReader from the decoder that decoded the image.

UINT blockCount = 0;
IWICMetadataReader* piMetadataReader = NULL;
IWICMetadataWriter** apiMetadataWriter = NULL;
HRESULT hr;

hr = m_piBlockReader->GetCount(&blockCount);
apiMetadataWriter = IWICMetadataWriter*[blockCount];
for (UINT x=0; x < blockCount; x++)
{
   hr = m_piBlockReader-   >GetReaderByIndex(&piMetadataReader);
   hr = m_piComponentFactory->CreateMetadataWriterFromReader(
        piMetadataReader, NULL, &apiMetadataWriter[x]);
}

Initializing the IWICMetadataBlockWriter with an IWICMetadataBlockReader instantiates a metadata writer for each metadata reader exposed by the IWICMetadataBlockReader object, so the application doesn't have to explicitly request a writer for each block of metadata.

GetWriterByIndex returns the IWICMetadataWriter object for the nth metadata block, where n is the value passed in the nIndex parameter. If there is no metadata writer registered that can handle the type of metadata in the nth block, the component factory will return the Unknown Metadata Handler, which will treat the block of metadata as a BLOB. It will serialize it out as a bit stream without attempting to parse it.

AddWriter allows a caller to add a new metadata writer. This is required if an application wants to add metadata of a different format than any of the existing metadata blocks. For example, an application may want to add some XMP metadata. If there is no existing XMP metadata block, the application will have to instantiate an XMP metadata writer and use the AddWriter method to include it in the collection of metadata writers.

SetWriterByIndex is used to add a metadata writer at a specific index in the collection. If a metadata writer is currently exists at that index, the new one should replace it.

RemoveWriterByIndex is used to remove a metadata writer from the collection.

CODEC Installation and Registration

When you install a CODEC, you need to register it in the registry, and also make sure the thumbnail cache gets updated in case there are any images in your format already present on the computer.

In this section, we'll discuss all the registry keys you need to write to register your CODEC and enable it to participate in discovery, as well as the entries to enable it to participate in the metadata search and update facilities of the Vista Photo Gallery and Windows Explorer. We'll also discuss the API you need to call in your CODEC installer to update the thumbnail cache for any images in your image format already present on the computer. Finally, we'll discuss how you can make it easy for users to find and install your CODEC when they have images on their computer in your image format.

Signing Your CODEC

As noted in the section on Discovery and Arbitration, all CODECs must be signed to participate in the WIC discovery process. WIC will not load any CODEC that is not signed by a trusted certificate authority (e.g., Verisign). For more information on signing, please see the Windows Vista SDK documentation.

Registering Your CODEC

When you register a CODEC, you're actually registering two components—the encoder and the decoder. You'll also need to make registry entries to register your container format with the metadata handlers for the metadata formats that your image format supports.

Registry Entries for Both Encoders and Decoders

The following registry entries need to be made separately for both the decoder and the encoder.

HKEY_CLASSES_ROOT\CLSID\{Encoder/Decoder CLSID},Author = "Author's Name"
HKEY_CLASSES_ROOT\CLSID\{Encoder/Decoder CLSID},Description = "Description of encoder or decoder"
HKEY_CLASSES_ROOT\CLSID\{Encoder/Decoder CLSID},DeviceManufacturer = "Camera Manufacturer "
HKEY_CLASSES_ROOT\CLSID\{Encoder/Decoder CLSID},DeviceModels = "Device,Device"
HKEY_CLASSES_ROOT\CLSID\{Encoder/Decoder CLSID},FriendlyName = "Name of encoder or decoder"
HKEY_CLASSES_ROOT\CLSID\{Encoder/Decoder CLSID},FileExtensions = ".foo, .abc, .xyz"
HKEY_CLASSES_ROOT\CLSID\{Encoder/Decoder CLSID},Date = "mm-dd-yyyy"
HKEY_CLASSES_ROOT\CLSID\{Encoder/Decoder CLSID},VendorGUID = {Vendor GUID}
HKEY_CLASSES_ROOT\CLSID\{Encoder/Decoder CLSID},ContainerFormat = {ContainerFormat GUID}
HKEY_CLASSES_ROOT\CLSID\{Encoder/Decoder CLSID},Version = "Major.Minor.Build.Number"
HKEY_CLASSES_ROOT\CLSID\{Encoder/Decoder CLSID},SpecVersion = "Major.Minor.Build.Number"
HKEY_CLASSES_ROOT\CLSID\{Encoder/Decoder CLSID},MimeTypes = "image/foo,image/foo"
HKEY_CLASSES_ROOT\CLSID\{Encoder/Decoder CLSID},SupportAnimation = 0 or 1
HKEY_CLASSES_ROOT\CLSID\{Encoder/Decoder CLSID},SupportChromakey = 0 or 1
HKEY_CLASSES_ROOT\CLSID\{Encoder/Decoder CLSID},SupportLossless = 0 or 1
HKEY_CLASSES_ROOT\CLSID\{Encoder/Decoder CLSID},SupportMultiframe = 0 or 1
HKEY_CLASSES_ROOT\CLSID\{Encoder/Decoder CLSID},Formats\{PixelFormat GUID}
HKEY_CLASSES_ROOT\CLSID\{Encoder/Decoder CLSID},InProcServer32\(Default) = "drive:\path\yourdll.dll"
HKEY_CLASSES_ROOT\CLSID\{Encoder/Decoder CLSID},InProcServer32\ThreadingModel = "Apartment" | "Both"

The FriendlyName, VendorGUID, ContainerFormat, MimeTypes, FileExtensions, and Formats entries are required. All the others are optional.

While most of these entries are self-explanatory, a couple of them require some explanation. The DeviceManufacturer and DeviceModels entries are specific to Raw CODECS and refer to the camera manufacturer and camera models that the CODEC is applicable to. The spec version is the version of the image format spec with which the CODEC complies. The Formats entry specifies the pixel formats supported by the CODEC. A CODEC may support more than one pixel format. In that case, you would enter multiple keys under HKEY_CLASSES_ROOT\CLSID\{Encoder/Decoder CLSID}\Formats.

Encoder-Specific Registry Entries

In addition to the entries listed above for the encoder, it's also necessary to register your encoder under the category of WIC Encoders so the discovery engine can find it. You do this by making the following registry entries. The first GUID in the following entries is the CATID for WICBitmapEncoders.

HKEY_CLASSES_ROOT\CLSID\{AC757296-3522-4E11-9862-C17BE5A1767E}\Instance\{Encoder CLSID},CLSID = {Encoder CLSID}
HKEY_CLASSES_ROOT\CLSID\{AC757296-3522-4E11-9862-C17BE5A1767E}\Instance\{Encoder CLSID},Friendly Name = "Name of Encoder"

Registering a New Container Format with Metadata Writers

If you create a new container format for your CODEC, you'll also need to create registry entries to support metadata writers for the metadata blocks in your images. The following entries need to be created under the CLSID of the metadata writer for each metadata format supported in your container format. If your CODEC uses a TIFF container, then this information is already in the registry and you won't need to create these entries.

HKEY_CLASSES_ROOT\CLSID\{Metadata Writer CLSID}\Containers\{Container Format GUID},WritePosition = Offset relative to its container
HKEY_CLASSES_ROOT\CLSID\{Metadata Writer CLSID}\Containers\{Container Format GUID},WriteHeader = Pattern used for metadata header
HKEY_CLASSES_ROOT\CLSID\{Metadata Writer CLSID}\Containers\{Container Format GUID},WriteOffset = Offset from beginning of header

If you use a TIFF- or JPEG-style container format, you'll need to register an association between your container and that container format. Please see the section below on Integration with the Windows Property Store for a discussion of this.

Decoder-Specific Registry Entries

In addition to the registry entries required for all encoders and decoders, the following registry entries are required specifically for decoders.

These entries register your decoder under the category of WIC Decoders. The first GUID in these entries is the CATID for WICBitmapDecoders.

HKEY_CLASSES_ROOT\CLSID\{7ED96837-96F0-4812-B211-F13C24117ED3}\Instance\{Decoder CLSID},CLSID = {Decoder CLSID}
HKEY_CLASSES_ROOT\CLSID\{7ED96837-96F0-4812-B211-F13C24117ED3}\Instance\{Decoder CLSID},Friendly Name = "Name of Decoder"

As noted in the earlier section on Discovery and Arbitration, the mechanism that enables an appropriate decoder for a given image to be discovered at runtime is based on matching an identifying pattern embedded in the image file with a pattern specified in the decoder's registry entry. To enable run-time discovery of decoders, you need to register the unique identifying pattern for your image format as follows. All of these registry entries are required except for the EndOfStream entry, which is optional, as described below.

HKEY_CLASSES_ROOT\CLSID\{Decoder CLSID}\Patterns\0,Position = Offset in block
HKEY_CLASSES_ROOT\CLSID\{Decoder CLSID}\Patterns\0,Length = Length of pattern
HKEY_CLASSES_ROOT\CLSID\{Decoder CLSID}\Patterns\0,Pattern = Pattern to match
HKEY_CLASSES_ROOT\CLSID\{Decoder CLSID}\Patterns\0,Mask = FF FF FF FF
HKEY_CLASSES_ROOT\CLSID\{Decoder CLSID}\Patterns\0,EndOfStream= 0 or 1

The Position value indicates the offset into the file where the pattern can be found.

The Length value declares the length of the pattern.

The Pattern value specifies the actual bits that make up the pattern. These are the bits that are matched against the identifying pattern in an image file during discovery.

The Mask value allows for wildcard values in patterns. The mask is applied by performing a logical AND operation on the pattern and the mask. Any bit in the pattern that corresponds to a bit in the mask with a value of 0 are ignored.

The EndOfStream value, if present, indicates whether the offset of the identifying pattern should be calculated from the end of the stream, rather than the beginning. Some image formats place the identifying pattern at or near the end of the file. The default is to seek from the beginning so, unless your pattern is near the end of the file, you can omit this entry.

A CODEC may support more than one identifying pattern. In that case, you would repeat all of the keys under HKEY_CLASSES_ROOT\CLSID\{Decoder CLSID}\Patterns, and use the numerical key (0 in the example) to distinguish between the different patterns. You must include each of the four values under the key for each pattern.

Registering a New Container Format with Metadata Readers

If you create a new container format for your CODEC, you'll also need to create registry entries to support discovery of metadata readers for the metadata blocks in your images, just as you did for the metadata writers. The following entries need to be created under the CLSID of the metadata reader for each metadata format your container format supports. (Note that, if your CODEC uses a TIFF container, then this information is already in the registry.)

HKEY_CLASSES_ROOT\CLSID\{Metadata Reader CLSID}\Containers\{Container Format GUID}\0,Position = Offset relative to its container
HKEY_CLASSES_ROOT\CLSID\{Metadata Reader CLSID}\Containers\{Container Format GUID}\0,Pattern = Pattern used for metadata header
HKEY_CLASSES_ROOT\CLSID\{Metadata Reader CLSID}\Containers\{Container Format GUID}\0,Mask = FF FF FF FF
HKEY_CLASSES_ROOT\CLSID\{Metadata Reader CLSID}\Containers\{Container Format GUID}\0,DataOffset = Offset from beginning of header

Because the entries for metadata readers are also used for discovery, they are very similar to the entries for decoders. These entries are used by the component factory to find the metadata readers supported by your container, and to select the appropriate one, when your IWICMetadataBlockReader implementation requests a metadata reader.

The Position value indicates the offset in the metadata block's container where the metadata header can be found. For top-level metadata blocks, this will be the offset in the file stream. For metadata blocks nested in other metadata blocks, it will be the offset relative to the containing metadata block.

The Pattern for the metadata header is generally defined by the metadata handler, and you should use the standard metadata header for each reader unless there's a compelling reason that the pattern needs to have a different format in your container.

The DataOffset is optional, and indicates the offset from the beginning of the metadata header at which the actual data begins. In cases where the metadata isn't located at a specific offset from the header, this entry can be omitted.

Integration with the Windows Vista Photo Gallery and Windows Explorer

To enable the Vista Photo Gallery and Windows Explorer to display thumbnails and search and update standard image metadata, a CODEC needs to have an implementation of the IThumbnailProvider interface and the IPropertyStore interface associated with it. The IThumbnailProvider interface is used to retrieve thumbnails and populate the thumbnail cache, and the IPropertyStore is used for searching and updating metadata associated with a file. All types of files in Windows Vista have thumbnails and metadata, but different types of files require different implementations of these interfaces to retrieve or generate the thumbnails and metadata for them. Windows Vista provides default implementations of these interfaces. The default implementation of IThumbnailProvider can be used for any WIC-enabled image format. The default implementation of IPropertyStore can be used with any WIC-enabled image format that's based on a TIFF or JPEG container. To associate your image format with the default implementations of both of these interfaces, you just need to add a few registry entries.

The following entries indicate to the Vista Photo Gallery and Windows Explorer that a file extension (.ext) and its associated mimetype are associated with an image format.

HKEY_CLASSES_ROOT\.ext,ContentType = "Mimetype of image format"HKEY_CLASSES_ROOT\.ext,PerceivedType = "image"

Integration with the Windows Property Store

Sometimes the same metadata properties are exposed in different metadata schemas, often with different property names. When one of these properties is updated, but the other(s) are not, the metadata within the file can get out of sync. The Windows photo property handler provides the default IPropertyStore implementation for images, and is used by applications as well as by the Windows Vista Photo Gallery and Windows Explorer to ensure that all the metadata in an image stays in sync, and that the properties displayed by applications are consistent with those displayed by the Windows Vista Photo Gallery and Windows Explorer. When the photo property handler updates metadata, it makes sure that these properties are updated consistently across all the common metadata formats that are present in the file.

To do its job, the photo property handler needs to understand the container format and how to locate the various properties within it. In general, it isn't possible for the photo property handler to know how the various metadata blocks are laid out in a proprietary container format but, if the metadata in your container format is laid out the same way as the metadata in either a TIFF container format or a JPEG container format, the photo property handler can take advantage of that knowledge to update metadata consistently in your container format as well.

You can register this association by creating the following registry entry. This entry notifies the photo property handler that the container format identified by this GUID understands the same metadata query language paths as the container format with the GUID 163bcc30-e2e9-4f0b-961d-a3e9fdb788a3. (163bcc30-e2e9-4f0b-961d-a3e9fdb788a3 is the GUID for the TIFF container format.)

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\ PhotoPropertyHandler\ContainerAssociations,{Container Format GUID} = {163bcc30-e2e9-4f0b-961d-a3e9fdb788a3}

The following entry associates the photo property handler's default implementation of IPropertyStore with files that have the extension ".ext". The first GUID is the IID of the IPropertyStore interface, and the second is the GUID of the photo property handler's implementation of it.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\PropertySystem\PropertyHandlers\.ext,(Default) = "{a38b883c-1682-497e-97b0-0a3a9e801682}"

CODECs that use a proprietary format that is not compatible with either the TIFF or JPEG container format will need to write their own IPropertyStore implementation.

Integration with the Windows Vista Photo Gallery

Windows Vista has a new Photo Viewer that's built on WIC and can display any WIC-enabled image format for which the CODEC is installed. To notify Windows Vista that your image format can be opened in the new Photo Viewer, you need to create a file association with it, which you do by making the following registry entries.

HKEY_CLASSES_ROOT\.ext,(Default) = "ProgID" (e.g.extfile)
HKEY_CLASSES_ROOT\.ext\OpenWithProgids,ProgID
HKEY_CLASSES_ROOT\.ext\OpenWithList\PhotoViewer.dll
HKEY_CLASSES_ROOT\.ext\ShellEx\ContextMenuHandlers\ShellImagePreview, (Default) = "{FFE2A43C-56B9-4bf5-9A79-CC6D4285608A}"

HKEY_CLASSES_ROOT\SystemFileAssociations\.ext\OpenWithList\PhotoViewer.dll
HKEY_CLASSES_ROOT\SystemFileAssociations\.ext\ShellEx\ContextMenuHandlers\ ShellImagePreview,(Default) = "{FFE2A43C-56B9-4bf5-9A79-CC6D4285608A}"

HKEY_CLASSES_ROOT\Image Format ProgID,(Default) = "Name of Image Format"
HKEY_CLASSES_ROOT\Image Format ProgID\DefaultIcon,(Default) = "Path to icon for file type,Icon index"

HKEY_CLASSES_ROOT\Image Format ProgID\shell\open\command,(Default) ="%SystemRoot%\System32\rundll32.exe "%ProgramFiles%\Windows Photo Gallery\PhotoViewer.dll", ImageView_Fullscreen %1"

HKEY_CLASSES_ROOT\Image Format ProgID\shell\open,MuiVerb = "@%PROGRAM_FILES%\Windows Photo Gallery\photoviewer.dll,-3043"

HKEY_CLASSES_ROOT\Image Format ProgID\shell\open\DropTarget,Clsid = "{FFE2A43C-56B9-4bf5-9A79-CC6D4285608A}"

HKEY_CLASSES_ROOT\Image Format ProgID\shell\printto\command,(Default) = "%SystemRoot%\System32\rundll32.exe "%ProgramFiles%\Windows Photo Gallery\PhotoViewer.dll", ImageView_PrintTo /pt "%1" "%2" "%3" "%4"

The ProgID is usually the file extension appended with the word "file". (For example, if the file extension is .foo, the ProgID would usually be "foofile".)

There are other standard registry entries you need to make for file associations but, because they're not specific to WIC or Windows Vista, they're beyond the scope of this article. You can find more information on file associations in the Windows SDK documentation.

Integration with the Windows Vista Thumbnail Cache

The following two entries indicate that the standard WIC thumbnail provider implementation can be used to retrieve thumbnails for files with this extension. The first GUID is the IID of the IThumbnailProvider interface, and the second is the GUID of the standard Windows Vista implementation of this interface. (All entries under HLCR\.ext\ShellEx\ are repeated under HKCR\SystemFileAssociations\.ext\ShellEx\.)

HKEY_CLASSES_ROOT\.ext\ShellEx\{e357fccd-a995-4576-b01f-234630154e96},(Default) = "{C7657C4A-9F68-40fa-A4DF-96BC08EB3551}"

HKEY_CLASSES_ROOT\SystemFileAssociations\.ext\ ShellEx\{e357fccd-a995-4576-b01f-234630154e96},(Default) = "{C7657C4A-9F68-40fa-A4DF-96BC08EB3551}"

Installing and Uninstalling Your CODEC

In addition to registering your CODEC, your installer must also update the thumbnail cache, to notify it that there's a new CODEC available and ensure that your decoder is used to extract thumbnails for images already present on your machine.

Updating the Thumbnail Cache When Installing Your CODEC

When a CODEC is installed, the installer needs to call the following Windows API after writing its registry entries.

   SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL) 

This call notifies Windows that new file association information is available. If there are any images in your image format already present on the computer, the thumbnail cache will contain default thumbnails for them because there was no decoder available that could extract the thumbnails when the images were first acquired. When you notify Windows that a new file association is available, the thumbnail cache will discard any empty thumbnails and extract the actual thumbnails from the files that can now be decoded.

Uninstalling Your CODEC

Normally, when you uninstall a component, you uninstall all dependencies that you installed with it. This is not the case with WIC.

When you download the redistributable Windows Imaging Component, you'll be required to accept a licensing term that you'll never uninstall WIC. The reason for this is that WIC is a system component, on which many installed applications depend. Because it's a system component, there's only one instance of WIC on any computer at any given time. So, if any component or application were to uninstall it, all the applications on the computer that depend on it would break.

The only reason an uninstaller is included in the WIC package is to allow end users to uninstall it using the Add Remove Programs utility in Control Panel.

Important:   Under no circumstances should any CODEC or application ever uninstall the Windows Imaging Component.

Making Your WIC-Enabled CODEC Available to Users

If you're a camera manufacturer, you can ship your Raw CODECS in the box with your cameras. You can also post your CODECs on the Download page on your Web site. However, if a user acquires an image file in your format from some other source, such as a friend or business associate, or some other Web site, they won't necessarily know where to go to get the CODEC to decode it with.

To help mitigate this problem, Windows Vista offers an easier way for users of your image format to find your CODEC and download it onto their machine. If the Windows Vista Photo Gallery recognizes a file extension as an image format, and the CODEC for that format is not installed, it will show a dialog informing the user that the photo can't be displayed, and asking if they'd like to download the software required to display it. When the user accepts, they'll be taken to a Microsoft-hosted Web site that has a link to the CODEC manufacturer's download site. (Optionally, you may request that users be taken directly to your download site.)

If you'd like your image format's file extensions to be recognized by the Windows Vista Photo Gallery, so users can be directed to your download site, you need to do the following:

  1. Provide a download site for your CODEC. (You can have a separate page for each CODEC you provide, or one page that provides downloads for all your CODECs.)
    • The download site should be localized and easily searchable by camera model.
  2. Provide Microsoft with a list of extensions for your image formats, and the URL(s) for your download site(s).

You need to inform Microsoft of the extensions for any new CODECs you develop in the future, and of any changes to the URL(s) of your download site(s), so that the new information can be added to the Photo Gallery.

Conclusion

Building your CODEC on the Windows Imaging Component platform makes it possible for all applications built on WIC to get the same platform support for your image format as they get for the common image formats shipped with the platform. It also enables the Windows Vista Photo Gallery, Explorer, and Photo Viewer to display thumbnails and previews of images in your format using the decoder that you provide. For Raw formats, it enables more sophisticated imaging applications to take advantage of your decoder's Raw processing capabilities. Depending on the encoder options you support, you can also expose unique capabilities of your encoder to enable applications to take full advantage of the advanced features of your image format.

Developing a WIC-enabled CODEC requires you to implement some new interfaces. In many cases, you can write a wrapper for your existing CODEC that implements these interfaces. When you install your CODEC, you need to make some registry entries to make your CODEC discoverable by the WIC platform and associate it with the appropriate metadata handlers. You also need to invoke an API to clear the thumbnail cache of any default (empty) thumbnails that may have previously associated with images in your format. If you wish, you can enable the Windows Vista Photo Gallery to provide users with a link to download your CODEC when the Photo Gallery encounters an image with your file extension. To do this, you need to provide Microsoft with information about your CODEC's file extension and the URL for your download site.

Developing CODECs on the WIC platform provides great benefits for users of your image format, and makes it easy for third-party applications to support your image format as well.

Page view tracker