Harlequin RIP SDK
Color management module interface

The alternate color management module (CMM) API allows customer-supplied code to implement ICC profile based color transformations, and to implement custom color spaces. More...

Files

file  swcmm.h
 Header file defining the Color management module interface.
 

Data Structures

struct  sw_cmm_custom_colorspace
 A structure containing information about a custom color space. More...
 
struct  sw_cmm_instance
 An instance structure for the CMM API implementation. More...
 
struct  sw_cmm_init_params
 Collection structure for initialization parameters. More...
 
struct  sw_cmm_api
 The definition of an implementation of the alternate CMM interface. More...
 

Macros

#define SW_CMM_N_ICC_RENDERING_INTENTS   (4)
 The number of ICC rendering intents.
 
#define SW_CMM_N_SW_RENDERING_INTENTS   (6)
 The number of extended rendering intents, including absolute derivations of the ICC perceptual and saturation intents.
 

Typedefs

typedef HqnResult sw_cmm_result
 Type of return values from sw_cmm_api functions. More...
 
typedef uint8 sw_cmm_object_type
 Type of object_type values passed to some sw_cmm_api functions. More...
 
typedef uint8 sw_cmm_color_model
 Type of color_model values passed to some sw_cmm_api functions. More...
 
typedef struct sw_cmm_custom_colorspace sw_cmm_custom_colorspace
 A structure containing information about a custom color space. More...
 
typedef void * sw_cmm_profile
 An opaque structure defined by the alterate CMM implementation to represent profiles. More...
 
typedef void * sw_cmm_transform
 An opaque structure defined by the alternate CMM implementation to represent transforms. More...
 
typedef struct sw_cmm_api sw_cmm_api
 Type definition of the alternate CMM interface implementation.
 
typedef struct sw_cmm_instance sw_cmm_instance
 An instance structure for the CMM API implementation. More...
 
typedef struct sw_cmm_init_params sw_cmm_init_params
 Collection structure for initialization parameters.
 

Enumerations

enum  {
  SW_CMM_API_VERSION_20070525 = 2 , SW_CMM_API_VERSION_20070614 , SW_CMM_API_VERSION_20071109 , SW_CMM_API_VERSION_20160324 ,
  SW_CMM_API_VERSION_20160819 , SW_CMM_API_VERSION_20200117
}
 Version numbers defined for the CMM API. More...
 
enum  SW_CMM_RESULT {
  SW_CMM_SUCCESS = HQN_RESULT_SUCCESS , SW_CMM_ERROR = 1 , SW_CMM_ERROR_IOERROR , SW_CMM_ERROR_MEMORY ,
  SW_CMM_ERROR_INVALID , SW_CMM_ERROR_UNSUPPORTED , SW_CMM_ERROR_VERSION , SW_CMM_SUCCESS_USE_BUILTIN
}
 Return values for sw_cmm_api CMM functions. More...
 
enum  {
  SW_CMM_INTENT_PERCEPTUAL = 0 , SW_CMM_INTENT_RELATIVE_COLORIMETRIC , SW_CMM_INTENT_SATURATION , SW_CMM_INTENT_ABSOLUTE_COLORIMETRIC ,
  SW_CMM_INTENT_ABSOLUTE_PERCEPTUAL , SW_CMM_INTENT_ABSOLUTE_SATURATION
}
 Values of rendering intent for use in the intents array as passed to sw_cmm_api::open_transform() and sw_cmm_api::open_transform2(). More...
 
enum  {
  SW_CMM_TYPE_PICTURE = 0 , SW_CMM_TYPE_TEXT , SW_CMM_TYPE_SHFILL , SW_CMM_TYPE_VIGNETTE = SW_CMM_TYPE_SHFILL ,
  SW_CMM_TYPE_OTHER
}
 The types of graphic object. More...
 
enum  {
  SW_CMM_COLOR_MODEL_CMYK = 0 , SW_CMM_COLOR_MODEL_RGB , SW_CMM_COLOR_MODEL_GRAY , SW_CMM_COLOR_MODEL_NAMED_COLOR ,
  SW_CMM_COLOR_MODEL_CIE
}
 The color models of graphic objects reflecting the color space set by the job. More...
 

Functions

HqnResult swcmm_result_translate (sw_cmm_result result)
 Translate a CMM API-specific error code to a generic HqnResult error code. More...
 
sw_api_result SwRegisterCMM (sw_cmm_api *implementation)
 This routine makes an alternate CMM implementation known to the rip. More...
 

Detailed Description

The alternate color management module (CMM) API allows customer-supplied code to implement ICC profile based color transformations, and to implement custom color spaces.

The alternate color management module interface

The sw_cmm_api structure type describes a CMM abstraction (a class). The skin registers a concrete implementation of this structure for each selectable CMM. The RIP will create an instance structure (a sw_cmm_instance) to represent the configured and selected CMM in use.

Calls to the CMM interface follow these stages:

For performance reasons, the default behavior of most CMMs is to create what is effectively a devicelink profile from a color transform of more than one ICC profile. The exact conversions from each component of the color transform is only used to populate the grid points in the devicelink.

Module registration

Each CMM module is identified by an internal name, for use in configuring the RIP, and an additional UTF-8 name for display purposes.

CMM modules are represented by instances of the the sw_cmm_api structure. CMM modules may be registered after the RIP is started by calling SwRegisterCMM(). This function performs some validation checks on the API structure, and then registers it in RDR as a named RDR, in the RDR_NAMES_CMMAPI namespace, using the internal name provided in the structure. This internal name is also used to select the CMM module. You may register CMM modules in RDR directly before the RIP is started, but doing so will not perform the validation checks that SwRegisterCMM() performs. If you miss out a required callback function pointer, selecting your CMM may crash the RIP. You may also de-register CMM modules from RDR by calling SwDeregisterNamedRDR(), if you have access to the pointer and length that the module was registered with.

Module initialization

Before any other calls are made to the CMM module, the RIP will call sw_cmm_api::init() to initialize data. This call is provided with a parameters structure that contains a pointer to a memory callback API instance. The CMM module should save the memory callback API instance pointer and use it to allocate any data it needs.

Module configuration

The PostScript configuration operator setalternatecmm is used to select a registered alternate CMM module, using the internal name defined in the sw_cmm_api::info.name field:

(alternate-cmm-name) setalternatecmm

The RIP calls sw_cmm_api::construct() to constructed a CMM module instance (sw_cmm_instance) on calling either setalternatecmm, or setpagedevice with the /AlternateCMM key. Normally, there will only be one instance of the CMM API constructed.

If the sw_cmm_api::declare_custom_colorspace() method pointer is not NULL, the RIP will repeatedly call it to enumerate and validate the custom colorspaces supported by the module.

Colorspace selection

When a colorspace is selected, the RIP will attempt to open a profile handle for each custom colorspace and ICC colorspace. These profiles will be combined into a transform, a chain of color and colorspace conversion operations. The CMM module may optimize multiple color conversion steps within each transform.

The precise timing of the calls in this section may vary widely due to caching strategies employed by the RIP. There may be many transforms and profiles open at any one time.

Custom colorspace selection

CMM custom color spaces are only used within the color configuration operators. Each use results in opening a profile for later use in sw_cmm_api::open_transform(). On calling setinterceptcolorspace or setreproduction using a CMM custom color space, the RIP will iterate over any custom color spaces in the configuration, looking for a matching color space name declared by the module. If it finds a matching custom color space, the RIP will open a profile handle for the colorspace by calling sw_cmm_api::open_custom_colorspace(), for later use constructing and invoking transforms.

In pseudo-code, this process is:

for (all CMM custom color spaces used in the config) {
for (uint32 i = 0 ; api->declare_custom_colorspace() != NULL; ++i) {
if (name of CMM custom color space matches the name in the config) {
result = api->open_custom_colorspace(instance, i, &profile) ;
break
}
}
}
unsigned int uint32
32-bit unsigned integer
Definition: hqtypes.h:126
#define NULL
Definition of NULL pointer.
Definition: hqtypes.h:37

If a profile is successfully opened using sw_cmm_api::open_custom_colorspace(), it will be later closed by calling sw_cmm_api::close_profile().

ICC colorspace selection

On changing the color space within the job, e.g., with the setcolorspace PostScript operator, the RIP will construct a transform.

Profiles for CMM custom color spaces are available from the earlier call to sw_cmm_api::open_custom_colorspace(). ICC color spaces will have their profiles opened now, if they are not already open for another transform.

In pseudo-code, this process is:

if (colorspace is ICC) {
for (all profiles in the transform)
api->open_profile()
}

If a profile is successfully opened using sw_cmm_api::open_profile(), it will be later closed by calling sw_cmm_api::close_profile().

Transform construction

On changing the color space within the job, e.g., with the setcolorspace PostScript operator, when profiles for the custom colorspaces and ICC profiles have been created, the RIP will construct transforms.

A color transform is a concatenation of multiple ICC profiles or alternate color spaces in a sequence, with the condition that the sequence does not begin or end with the PCS. Where a profile may convert colors either into or out of the PCS, the conversion used is determined by the profile's position in the sequence. Possible instances of a transform are:

Prior to creating a transform, the RIP ensures compatibility with the capabilities of the alternate CMM, as indicated by its optional capabilities. If the transform is not compatible, color conversion is attempted using the built-in CMM. If the built-in CMM also fails, the job is aborted.

In those workflows where a sequence of profiles are configured and the overall sequence is incompatible as a single transform, the sequence is broken down into multiple sub-transforms. Those which are compatible are processed by the alternate CMM, with the remainder processed by the built-in CMM.

The above definition of a transform may be at variance with the corresponding definition used by an individual alternate CMM, for example, an alternate CMM may not accept devicelink profiles within a sequence of input-output-devicelink. If so, it is the responsibility of the OEM glue code to break down the transform supplied by the RIP into sub-transforms acceptable to the alternate CMM. In this example, the OEM code may divide the transform into two sub-transforms, one containing the input-output profile pair, and one containing the devicelink.

In pseudo-code, this process is:

// Open the transform with the appropriate callback.
if (api->open_transform2 != NULL)
api->open_transform2()
else
api->open_transform()

Transforms may remain open for a considerable duration, and there may be multiple transforms open simultaneously. Transforms opened using sw_cmm_api::open_transform2() or sw_cmm_api::open_transform() will be later closed by calling sw_cmm_api::close_transform().

If an alternate CMM fails to load a profile, or create a transform from a set of profiles, the RIP will optionally attempt to create a transform using the built-in CMM. The alternate CMM can control this optional behavior. If an alternate CMM does not allow the built-in CMM to retry on error, or if the retry with the built-in CMM also resulted in an error, the current job is aborted.

Transform invocation

At various times during the lifetime of a colorspace, the RIP will invoke transforms to convert color values and color spaces. The RIP caches the results of transforms, so you will not see invocations for every pixel in any object, or even for every object that uses a colorspace.

The precise timing of these calls may vary widely due to caching strategies employed by the RIP. There may be many transforms and profiles open at any one time. It is only the order of calls for any one transform that is fixed.

In pseudo-code, this process is:

for (all uncached color conversions)
api->invoke_transform()

The RIP may have multiple threads actively converting colors using the same or different transforms. It is the responsibility of the module to synchronize threads as necessary.

Colorspace deselection

When a color space goes out of scope, the transforms and profiles associated with them will be closed. In pseudo-code, this process is:

api->close_transform()
// The ICC profiles will be closed when they are not used by any transforms.
if (colorspace is ICC) {
for (all profiles in the transform)
api->close_profile()
}

Instance destruction

When the gstate containing an alternate CMM configuration goes out of scope, normally when the job has completed, sw_cmm_api::destruct() is called to destroy the sw_cmm_instance API instance created by sw_cmm_api::construct().

Module finalization

After all other calls are made to the CMM module, sw_cmm_api::finish() is called to allow it to finalize data. The module should free any data it allocated.

Typedef Documentation

◆ sw_cmm_color_model

Type of color_model values passed to some sw_cmm_api functions.

The values are the set of SW_CMM_COLOR_MODEL_* values.

◆ sw_cmm_custom_colorspace

A structure containing information about a custom color space.

To the rip, a custom color space is an arbitrary conversion of color values. To the alternate CMM, it is a means of implementing non-ICC color conversions.

◆ sw_cmm_instance

An instance structure for the CMM API implementation.

This is the definition of an alternate CMM instance. The RIP allocates memory for the instances, fills in the implementation and memory instance fields, and calls the implementation's constructor to complete the remaining details. The RIP will construct one instance as a result of using either the setalternatecmm operator, or the setpagedevice operator with the /AlternateCMM key. These are commonly used in the RIP's configuration.

There will normally be at most one CMM instance active on each page. There may be more than one CMM instance active at a time if Harlequin Parallel Pages is in use. While discouraged, it is possible to call setalternatecmm multiple times on the same page; this may result in several CMM instances active on the same page which will have unpredictable results depending on the job structure.

The instance structure may be subclassed to hold private data by defining a subclass structure containing this structure as its first member, and using the size of that structure as the implementation's instance size. Individual methods may then downcast their instance pointer parameters to subclass pointers, and use the private data. e.g.,

typedef struct my_instance {
sw_cmm_instance super ; // must be first entry
struct my_data *dynamic ;
int32 other_fields ;
} my_instance ;
static sw_cmm_result RIPCALL my_construct(sw_cmm_instance *inst)
{
my_instance *myinst = (my_instance *)inst ; // downcast to subclass
// allocate private data:
myinst->dynamic = cmmegMemAlloc(inst, sizeof(myinst->dynamic)) ;
return myinst->dynamic != NULL ? SW_CMM_SUCCESS : SW_CMM_ERROR_MEMORY ;
}
static void RIPCALL my_destruct(sw_cmm_instance *inst)
{
my_instance *myinst = (my_instance *)inst ; // downcast to subclass
// free allocated data, if necessary:
cmmegMemFree(inst, myinst->dynamic) ;
}
const static sw_cmm_api my_impl = {
{
(const uint8 *)"myname",
(const uint8 *)("A long description of my module implementation"
"Copyright (C) 2021 Global Graphics Software Ltd."),
sizeof(my_instance), // RIP will allocate this amount for the subclassed instance
},
// ...more of sw_cmm_api definition...
my_construct,
my_destruct,
// ...rest of sw_cmm_api definition...
} ;
// use the PostScript fragment:
// (myname) setalternatecmm
// in the config to create the CMM instance and call my_construct().
void cmmegMemFree(sw_memory_instance *instance, void *p)
Release CMM memory.
Definition: cmm_common.c:36
void * cmmegMemAlloc(sw_memory_instance *instance, size_t n)
Allocate CMM memory.
Definition: cmm_common.c:24
unsigned char uint8
8-bit unsigned integer
Definition: hqtypes.h:124
signed int int32
32-bit signed integer
Definition: hqtypes.h:122
HqnResult sw_cmm_result
Type of return values from sw_cmm_api functions.
Definition: swcmm.h:358
@ SW_CMM_ERROR_MEMORY
Problem accessing blob.
Definition: swcmm.h:330
@ SW_CMM_SUCCESS
Success return value for sw_cmm_api methods.
Definition: swcmm.h:322
@ SW_CMM_API_VERSION_20200117
Current version.
Definition: swcmm.h:314
#define RIPCALL
The normal calling convention for RIP-exported APIs.
Definition: ripcall.h:27
The definition of an implementation of the alternate CMM interface.
Definition: swcmm.h:723
An instance structure for the CMM API implementation.
Definition: swcmm.h:570

The RIP will not touch memory beyond the size of the instance structure for the implementation version registered.

◆ sw_cmm_object_type

Type of object_type values passed to some sw_cmm_api functions.

The values are the set of SW_CMM_TYPE_* values.

◆ sw_cmm_profile

typedef void* sw_cmm_profile

An opaque structure defined by the alterate CMM implementation to represent profiles.

The alternate CMM implementation can use this type to supply the RIP with references to opened profiles, which will be used when constructing transforms later.

◆ sw_cmm_result

Type of return values from sw_cmm_api functions.

This is a subclass of HqnResult that also supports some specific extra error codes generated by sw_cmm_api functions (declared as the SW_CMM_RESULT enumeration). Before assigning to values of HqnResult type or any of its other subclasses, sw_cmm_result values must be converted to change the API specific values to HQN_RESULT_SUCCESS or a monitor UID error code greater than MON_CLASS_ERROR. This can be done by calling the function swcmm_result_translate().

◆ sw_cmm_transform

typedef void* sw_cmm_transform

An opaque structure defined by the alternate CMM implementation to represent transforms.

The alternate CMM implementation can use this type to supply the RIP with references to opened transforms, which will be used when invoking the transforms to convert colors.

Enumeration Type Documentation

◆ anonymous enum

anonymous enum

Version numbers defined for the CMM API.

Enumerator
SW_CMM_API_VERSION_20070525 

Obsolete as of 20071109.

SW_CMM_API_VERSION_20070614 

Obsolete as of 20071109.

SW_CMM_API_VERSION_20071109 

Obsolete as of 20160324.

SW_CMM_API_VERSION_20160324 

Obsolete as of 20160819.

SW_CMM_API_VERSION_20160819 

Obsolete as of 20200117.

SW_CMM_API_VERSION_20200117 

Current version.

◆ anonymous enum

anonymous enum

Values of rendering intent for use in the intents array as passed to sw_cmm_api::open_transform() and sw_cmm_api::open_transform2().

The first four of these intents correspond to the standard ICC rendering intents: SW_CMM_INTENT_PERCEPTUAL, SW_CMM_INTENT_RELATIVE_COLORIMETRIC, SW_CMM_INTENT_SATURATION, and SW_CMM_INTENT_ABSOLUTE_COLORIMETRIC.

The remaining two of these intents may also be used if supported by the alternate CMM: SW_CMM_INTENT_ABSOLUTE_PERCEPTUAL, SW_CMM_INTENT_ABSOLUTE_SATURATION. They are absolute variants of Perceptual and Saturation that are derived in the same way that AbsoluteColorimetric is derived from RelativeColorimetric.

Enumerator
SW_CMM_INTENT_PERCEPTUAL 

ICC perceptual.

SW_CMM_INTENT_RELATIVE_COLORIMETRIC 

ICC relative colorimetric.

SW_CMM_INTENT_SATURATION 

ICC saturation.

SW_CMM_INTENT_ABSOLUTE_COLORIMETRIC 

ICC absolute colorimetric.

SW_CMM_INTENT_ABSOLUTE_PERCEPTUAL 

Extended absolute perceptual.

SW_CMM_INTENT_ABSOLUTE_SATURATION 

Extended absolute saturation.

◆ anonymous enum

anonymous enum

The types of graphic object.

The alternate CMM may choose to color manage object types differently.

Enumerator
SW_CMM_TYPE_PICTURE 

Most images, e.g., not 1 bit per pixel.

SW_CMM_TYPE_TEXT 

Text.

SW_CMM_TYPE_SHFILL 

Shaded fills.

SW_CMM_TYPE_VIGNETTE 

Shaded fills (old name for backward compatibility).

SW_CMM_TYPE_OTHER 

Linework, or vector objects.

◆ anonymous enum

anonymous enum

The color models of graphic objects reflecting the color space set by the job.

The color space in the job may or may not be tagged with a profile, e.g., the cmyk model applies to both DeviceCMYK and ICCBased color spaces with a cmyk profile. The alternate CMM may choose to color manage color models differently. Note that the color model of an object may not match the profile that is being used. This may arise when gray objects are converted using a cmyk profile, or when a named color is converted to equivalent cmyk values and a cmyk profile is used, amongst other scenarios.

Enumerator
SW_CMM_COLOR_MODEL_CMYK 

CMYK.

SW_CMM_COLOR_MODEL_RGB 

RGB.

SW_CMM_COLOR_MODEL_GRAY 

Gray.

SW_CMM_COLOR_MODEL_NAMED_COLOR 

Named colors.

SW_CMM_COLOR_MODEL_CIE 

None of the above, e.g., Lab color spaces.

◆ SW_CMM_RESULT

Return values for sw_cmm_api CMM functions.

Enumerator
SW_CMM_SUCCESS 

Success return value for sw_cmm_api methods.

SW_CMM_ERROR 

Non-specific error, also minimum error value. Please avoid using this if possible.

SW_CMM_ERROR_MEMORY 

Problem accessing blob.

SW_CMM_ERROR_INVALID 

Memory allocation failed.

SW_CMM_ERROR_UNSUPPORTED 

Invalid profile or transform.

SW_CMM_ERROR_VERSION 

Unsupported configuration.

SW_CMM_SUCCESS_USE_BUILTIN 

Callback API version is insufficient.

May only be returned by a call to sw_cmm_api::invoke_transform() for an ICC transform.

If set, the RIP will ignore color values returned by sw_cmm_api::invoke_transform() and use the built-in CMM to perform the ICC transform for this color value.

Function Documentation

◆ swcmm_result_translate()

HqnResult swcmm_result_translate ( sw_cmm_result  result)

Translate a CMM API-specific error code to a generic HqnResult error code.

Parameters
[in]resultOne of the SW_CMM_RESULT values, or an error value greater than MON_CLASS_ERROR.
Returns
Either HQN_RESULT_SUCCESS, or an error value greater than MON_CLASS_ERROR.

◆ SwRegisterCMM()

sw_api_result SwRegisterCMM ( sw_cmm_api implementation)

This routine makes an alternate CMM implementation known to the rip.

It can be called any number of times with different implementations of the alternate CMM API. Within SwRegisterCMM(), sw_cmm_api::init() will be called for the implementation. After that, it will remain dormant until the sw_cmm_api::construct() is called as part of using the setalteratecmm operator or the /AlternateCMM key of the setpagedevice operator. Both the sw_cmm_api::init() and sw_cmm_api::finish() callbacks are set to NULL in many implementations because the alternate CMM has nothing to do at this point.

This function may be called at any time after the RIP is initialized, and before the RIP is shut down.

Parameters
[in]implementationThe API implementation to register. This pointer will be returned through the sw_cmm_api::init() and sw_cmm_api::finish() calls, and also will be in the implementation member field of every instance created, so the pointer can be in dynamically allocated memory.
Returns
SW_API_REGISTERED if the invocation succeeded, one of the SWAPI_RESULT error codes otherwise.

Implementations may be subclassed to hold class-specific private data by defining a subclass structure containing the sw_cmm_api structure as its first member. Individual methods may then downcast their implementation pointers to subclass pointers, and use those to get at the class data. e.g.,

static HqBool RIPCALL my_init(sw_cmm_api *impl, const sw_cmm_init_params *params)
{
// as an example of what might be done in the init() callback -
// inform an external module that it should now get ready for work
return TRUE ;
}
static void RIPCALL my_finish(sw_cmm_api *impl)
{
// as an example of what might be done in the finish() callback -
// inform an external module that it is no longer needed
}
const static sw_cmm_api my_impl = {
{
(const uint8 *)"myname",
(const uint8 *)("A long description of my module implementation"
"Copyright (C) 2021 Global Graphics Software Ltd."),
sizeof(sw_cmm_instance), // RIP will allocate the size of the standard instance
},
my_init,
my_finish,
// ...rest of sw_cmm_api definition...
} ;
// Call SwRegisterCMM(&my_impl) after SwInit() to register the 'my_impl' CMM module.
int HqBool
Harlequin standard boolean type.
Definition: hqtypes.h:502
#define TRUE
HqBool boolean true value.
Definition: hqtypes.h:508
Collection structure for initialization parameters.
Definition: swcmm.h:702