Chapter 5. Transform Management

A transform converts pixel data from an input color space to an output color space; it is the central processing step in a color management application. You build a transformation algorithm by specifying a sequence of one or more primitive color manipulations, described by profiles, and a CMM. You then apply the transform to pixel data.

For example, a common two-profile transform converts from an input color space to an output color space; typically the input profile is a monitor profile and the output is a printer profile. A useful three-profile transform simulates on one device the output of another device. The sequence of profiles is first the input, second the simulated device, and third the simulating device: for example, first a scanner, second a printer, and third a monitor.

These are sections of this chapter:

Features of Transform Management Tools

This section introduces the features of the Coloratura transform tools.

Selecting a CMM

The color manipulation module that the Coloratura CMS uses determines the details of the transformation algorithm. Different CMMs can give different results from the same input image and set of profiles, depending on how the CMMs interpret the discrete, and perhaps sparse, data in each profile. When you create a transform, you can select a CMM explicitly, or you can use the default CMM that is included in the Coloratura CMS or, if you have other CMMs, you can have the Coloratura CMS let the profiles determine which CMM to use.

Saving a Transform

You can save a transform, which is convenient if you have a sequence of profiles that you use repeatedly. For example, if you commonly transform data from your monitor to output on a specific printer, you can build a device-link profile, which does not represent a particular device, but provides a one-profile characterization of the transformation between devices.

Gamut Checking

As discussed earlier, the set of possible colors for a device is called its color gamut. A central concern for color management is a mismatch between the gamuts of an input and output device. The severity of this mismatch and the distribution of mismatches over an image affect the appearance of your output. A fairly common gamut mismatch is that between a monitor and a printer; any monitor typically has a gamut larger than most printers.

Output devices necessarily have rules to handle out-of-gamut data. If you do not like the results, you can modify the source image data or, conceivably, develop an abstract profile to perform a gamut mapping to adjust out-of-gamut image data. How you accommodate out-of-gamut image data is a substantial component of an acceptable color conversion, and much of the art of the process.

Before applying a transform, you may want to know how much of the transformed image is out of gamut, rather than observe the effects on the output image. The Coloratura CMS provides gamut-checking tools to give you that information.

Transforming Pixel Data

To create a transform with the Coloratura CMS, you supply a list of profiles and a CMM specifier to the function cmsCreateTfm(). You then call cmsApplyTfm() to apply the transform to a pixel buffer.

During the process of developing a transform, you probably need to delete a current version. You delete a transform by calling cmsDeleteTfm(). To save a transform, use cmsTFMToLUT(). The applications mkdevlink and applydevlink in /usr/cms/examples/ illustrate how to use the output of cmsTFMToLUT().

Data Structure for Transforms: CMSTfm

The pointer CMSTfm refers to an opaque structure that stores transform data. This is the data type declaration:

typedef struct _CMSTfm  *CMSTfm;

Data Structure for Pixels: CMSPixelBuffer

Transforms operate on CMSPixelBuffer data structures, which hold color image data. The two Coloratura functions that accept a CMSPixelBuffer argument are cmsCheckGamut() and cmsApplyTfm().

The Coloratura CMS makes the following assumptions about the storage of image data included in a CMSPixelBuffer:

  • All the channels for a single pixel are stored together.

  • Each channel aligns to byte boundaries. Any padding is in the most significant bits.

  • The data is encoded according to one of the valid color ICC encodings.

If a pixel holds non-color information, such as the OpenGL opacity parameter alpha, then the number of bytes per pixel will be greater than the number of bytes in the image channel data. When implementing transforms, the Coloratura CMS preserves without modification the additional information.

The Coloratura CMS does not store color encoding formats. For the Coloratura CMS to manipulate data with a particular encoding, there must be at least one profile in storage that has that encoding. For example, if no profile has the HSV encoding, the Coloratura CMS cannot process an image in HSV format.

  • This is the data type declaration for CMSPixelBuffer:

    typedef struct  _CMSPixelBuffer {
        uint32         width;
        uint32         height;
        uint32         bitsPerChannel;
        uint32         bytesPerPixel;
        uint32         channels;
        uint32         encode;
        unsigned void  *data;
    }  CMSPixelBuffer;

  • These are the fields in the declaration for CMSPixelBuffer:

    width 

    The width of the image in pixels.

    height 

    The height of the image in pixels.

    bitsPerChannel 

    The number of bits may range from 1 to 12.

    bytesPerPixel 

    The number of bytes must align on 32-bit boundaries.

    channels 

    The number of color channels. Possible values are: 1, 3, 4, 5, or 6.

    encode 

    These are the possible color space signatures. They correspond to a the values of the enumerated type icColorSpaceSignature, which is defined in the header file ic.h.

    data 

    A pointer to the image data.

Creating a Transform: cmsCreateTfm()

The function cmsCreateTfm() translates a set of profiles into a transformation algorithm. The algorithm is defined by a CMM and a sequence of profiles. The transformation is built from profiles in the sequence in which they are supplied: the first profile defines the first step in the transformation, typically from your input device, and the last profile defines the final step, typically to your output device.

  • This is the prototype for cmsCreateTfm():

    int32 cmsCreateTfm(CMSContext ctxt,
                       int32 profileCount,
                       CMSProfile *profs,
                       icSignature cmm,
                       CMSTfm *ptfm);

  • These are the arguments of cmsCreateTfm():

    ctxt 

    The context initialized by cmsOpen().

    profileCount 

    The number of profiles in a transform.

    profs 

    An array of profiles for the transform.

    cmm 

    The CMM to use. If you do not directly specify the CMM with a valid icSignature, a data type declared in ic.h, use one of the following two constants to direct CMM selection:

    CMS_USE_DEFAULT_CMM selects the default CMM.

    CMS_USE_PROFILE_CMM prompts the Coloratura CMS to search the CMMs specified by the profiles in the transform until it finds one that can perform the transformation. At least one CMM, the default, can always perform every transform.

    In searching, the Coloratura CMS examines the profiles from last to first, looking at each profile for a CMM that can perform the entire transformation. The search begins with the last profile because profiles later in the sequence tend to have the greatest effect on output, and a preferred CMM is likely to give the best transform results.

    ptfm 

    The new transform.

  • These are the error codes returned by cmsCreateTfm():

    CMS_OUT_OF_MEMORY 


    Not enough memory to create a transform.

    CMS_WRONG_DATA 


    The variable ctxt points to something that is not a context.

    CMS_BAD_INPLACE_CONVERT 


    It is not possible to build an in-place conversion for this transform.

    CMS_BAD_ENCODE 

    CMS_BAD_CONTEXT 

    CMS_TOO_MANY_CHANNELS 


    The CMM cannot support a transform with the requested number of channels.

    CMS_CMM_NOT_AVAILABLE 


    The requested CMM is not available.

Applying a Transform: cmsApplyTfm()

Once you have created a transform, call cmsApplyTfm() to apply the transform to pixel data. This function transforms a pixel buffer with one profile to a buffer associated with another profile.

Note that you may not always be able to perform in-place conversions; for example, if the output format requires more space than the input format (RGB to CMYK), or if the CMM does not support in-place conversions. cmsApplyTfm() returns an error if you attempt an in-place conversion for one of these cases.

If you are concerned about maintaining interactivity, you may want to transform an image piece-by-piece, to avoid the possibly slow process of transforming a whole image.

  • This is the prototype for cmsApplyTfm():

    int32  cmsApplyTfm(CMSContext ctxt, CMSTfm tfm,
                       CMSPixelBuffer *psrc, 
                       CMSPixelBuffer *pdest);

  • These are the arguments of cmsApplyTfm():

    ctxt 

    The context initialized by cmsOpen().

    tfm 

    The transform to use.

    psrc 

    The pixel buffer to be transformed.

    pdest 

    The resulting output pixel buffer.

  • These are the error codes returned by cmsApplyTfm():

    CMS_WRONG_DATA 


    Either of the pixel-buffer pointers refers to data in the wrong format for the transform.

    CMS_BAD_TFM 


    The argument tfm is either not a transform or it was not built with the supplied ctxt.

    CMS_BAD_PIXEL_BUF 


    Either of the pixel-buffer pointers refers to data in the wrong format for the transform.

    CMS_CONVERT_ERROR 


    The transform could not be applied. This error occurs if a transform could not be performed in place.

Saving a Transform as a Look-Up Table: cmsTfmToLUT()

The function cmsTfmToLUT() allows you to save transform information in a tag format, the AToB0Tag described in the ICC Profile Format Specification. To recover the transform from the tag data produced by cmsTfmToLUT(), do the following:

  1. Create a profile by calling cmsCreateProfile(), which is discussed in "Creating New Profiles, Getting and Setting Headers, and Saving Edits". You can specify any rendering intent for the profile when you create the profile header.

  2. Set the tag value by calling cmsSetTag(), which is discussed in "Setting Tag Data: cmsSetTag()". You can save the profile for later use by calling cmsSaveProfileAs(), which is discussed in "Saving to a New File on Disk: cmsSaveProfileAs()".

  3. Create a transform from the profile, by calling cmsCreateTfm().

The application mkdevlink in /usr/cms/examples illustrates the first two steps. The application applydevlink in the same directory illustrates the last step, and applies the transform to input data.

  • This is the prototype for cmsTfmToLUT():

    int32 cmsTfmToLUT(CMSContext ctxt, 
                      CMSTfm tfm, 
                      uint32 *psize, 
                      void **pdata);

  • These are the arguments of cmsTfmToLUT():

    tfm 

    The transform to save.

    psize 

    The size of the tag data.

    pdata 

    The tag data.

  • This is the error code returned by cmsTfmToLUT():

    CMS_NOT_SUPPORTED 


    The CMM does not create a look-up table from a transform.

Deleting a Transform: cmsDeleteTfm()

The function cmsDeleteTfm() disposes of all data structures associated with a transform.

  • This is the prototype for cmsDeleteTfm():

    int32  cmsDeleteTfm(CMSContext ctxt, CMSTfm tfm);

  • These are the arguments of cmsDeleteTfm():

    ctxt 

    The context initialized by cmsOpen().

    tfm 

    The transform.

Checking Gamut Mapping

You can perform a test for which output pixels have data that lies out of gamut, rather than apply a transformation and observe the effects of out-of-gamut image data. This helps you quantify the severity of your gamut mapping problem and to see clearly how out-of-gamut data affect your image. It may help you to develop a gamut mapping strategy.

To examine the gamut mismatch, you create a gamut check with cmsCreateGamutCheck(), which takes the same arguments as cmsCreateTfm(). You then perform the check with cmsCheckGamut(). This function examines the pixel data stored in a CMSPixelBuffer data structure, and returns an unsigned char array to indicate how each pixel is mapped; zero values indicates the image pixel is in gamut, non-zero indicates out of gamut.

Preparing for a Gamut Map Test: cmsCreateGamutCheck()

The function cmsCreateGamutCheck() takes the same set of arguments as cmsCreateTfm(); it uses a CMM and a set of profiles to create a gamut checking transform. The gamut check is built from the profiles in the sequence in which they are supplied: the first profile defines the first step in the transformation, typically from your input device, and the last profile defines the final step, typically to your output device.

  • This is the prototype for cmsCreateGamutCheck():

    int32 cmsCreateGamutCheck(CMSContext ctxt,
                              int32 profileCount,
                              CMSProfile *profs,
                              icSignature cmm,
                              CMSTfm *ptfm);

  • These are the arguments of cmsCreateGamutCheck():

    ctxt 

    The context initialized by cmsOpen().

    profileCount 

    The number of profiles in a transform.

    profs 

    An array of profiles for the transform.

    cmm 

    The CMM to use. If you do not directly specify the CMM with a valid icSignature, use one of the following two constants to direct CMM selection:

    CMS_USE_DEFAULT_CMM selects the default CMM.

    CMS_USE_PROFILE_CMM prompts the Coloratura CMS to search the CMMs specified by the profiles in the transform until it finds one that can perform the transformation. See the next section "Creating a Transform: cmsCreateTfm()" for more details

    ptfm 

    The gamut-checking transform.

  • These are the error codes returned by cmsCreateGamutCheck():

    CMS_OUT_OF_MEMORY 


    Not enough memory to create a gamut check.

    CMS_WRONG_DATA 


    The ctxt variable points to something that is not a context.

    CMS_BAD_ENCODE 

    CMS_BAD_CONTEXT 

    CMS_TOO_MANY_CHANNELS 


    The CMM cannot support a gamut check with the requested number of channels.

    CMS_CMM_NOT_AVAILABLE 


    The requested CMM is unavailable.

Checking a Gamut Map: cmsCheckGamut()

Given a transform and set of pixels, the function cmsCheckGamut() tests whether the transform maps the pixels within the gamut of the output device. The order of the output bytes follows the order of the input pixels. A value of zero indicates that an output pixel is in gamut; a non-zero value indicates that the pixel is out of gamut.

If you are concerned about maintaining interactivity, you may want to check the gamut mapping of an image piece-by-piece, to avoid the possibly slow process of checking the whole image. The cmsCheckGamut() function runs at a rate comparable to that of the transform whose effect it reports.

  • This is the prototype for cmsCheckGamut():

    int32 cmsCheckGamut(CMSContext ctxt, CMSTfm tfm, 
                        CMSPixelBuffer *psrc, 
                        unsigned char pgamutmap);

  • These are the arguments of cmsCheckGamut():

    ctxt 

    The context initialized by cmsOpen().

    tfm 

    The transform to check.

    psrc 

    The input pixel buffer.

    pgamutmap 

    The resulting gamut-map buffer.

  • These are the error codes returned by cmsCheckGamut():

    CMS_OUT_OF_MEMORY 


    There is not sufficient memory available for gamut testing.

    CMS_BAD_PIXEL_BUF 


    The variable psrc does not point to a valid pixel buffer.

    CMS_BAD_GAMUT_MAP 


    The variable pgamutmap is not a valid gamut map buffer.

    CMS_BAD_TFM 


    The argument tfm is either not a transform, or was not built with the supplied ctxt.

    CMS_CONVERT_ERROR 


    The transform could not be applied.