KCMS CMM Developer's Guide

Chapter 3 KCMS Framework Operations

In This Chapter

The Kodak Color Management System (KCMS) is a flexible and powerful framework for developing color management technology. You can add attributes to the current list and incorporate new color processing technology.

This chapter provides an overview of the KCMS framework architecture. It introduces you to how the framework works by showing excerpts form KCMS "C" API programs and comparing these excerpts to the underlying framework actions.

KCMS Framework Architecture

Figure 3-1 illustrates the KCMS framework architecture. It provides an overview of how the KCMS classes are used to implement the KCMS framework. Basically, the framework is implemented by manipulating an array of KcsProfile objects within a set of "C" wrapper functions. The "C" wrapper functions are C-to-C++ calls that make up the KCMS API. Use this figure as a general reference as you read this chapter.

Figure 3-1 KCMS Framework Architecture

Graphic

KcsProfile

KcsProfile objects are created from a static store which is a KcsIO object. KcsProfile objects are described using one of the types in the KcsProfileDesc structure which is defined in the kcstypes.h header file. Objects can read from and write to data in a static store. Examples of a static store include a file and memory. KcsProfile objects generated internally by the framework use a KcsMemoryBlock object.

The KcsProfile class static member function, createProfile() reads the CMM Id from the static store and generates a pointer to the KcsProfile derivative. The CMM Id is located at byte 4 in the ICC profile format. If the CMM Id has no associated runtime derivative, the default KcsProfile derivative, KcsProfileKCMS, is used.


Note -

The CMM Id must be in a set location in the file that is the same location as used by the ICC profile format. For details, see "ICC Profile Header".


The KcsProfile class contains a set of public member functions that correspond to the KCMS "C" API functions shown in the following table.

Table 3-1 Mapping of API Functions to KcsProfile Class Member Functions

KCMS API Function 

KcsProfile Member Functions

KcsLoadProfile()

load()

KcsSaveProfile()

save()

KcsSetAttribute()

setAttribute()

KcsGetAttribute()

getAttribute()

KcsConnectProfiles()

connect()

KcsEvaluate()

evaluate()

KcsUpdateProfile()

updateXforms()

KcsProfileFormat

Each KcsProfile base class contains a pointer to a KcsProfileFormat object. This allows the architecture to link different profile formats and keep the KcsProfile class independent of the actual profile format. The KcsProfileFormat object is created based on the profile format Id (also called profile file signature) and profile version number. The ICC profile format Id is acsp, located at byte 36 in the profile header. (See "ICC Profile Header".) The version number is derived from the profile version number; ICC profile byte 8. The framework uses the version number with the profile format Id so that it can handle different versions of profile formats. For non-ICC profile formats the format Id and version number must be at the same byte location in the static store.

KcsAttributeSet

Each KcsProfileFormat base class contains a pointer to a KcsAttributeSet object and handles all of the functionality for attributes.Using the KcsIO class associated with the parent KcsProfile, the KcsAttributeSet object can load itself from the static store. KcsAttributeSet does not use the KcsIO class directly; it uses the KcsChunkSet utility class to access the static store. KcsChunkSet knows how to handle the mapping from desired information blocks to its actual location in the static store. KcsChunkSet and KcsIO have no knowledge of the contents of the data. That is left to the calling class.

KcsXform

The KcsXform base class contains an array of pointers to KcsXforms. The primary function of KcsXform (or transformation) is to manipulate color data. KcsXform also uses the KcsChunkSet class to load from and save to static store.

KCMS Framework Flow Examples

The following examples will help you better understand the flow of control and data between the KCMS "C" API and the KCMS framework. Use Figure 3-1 as a reference.

Loading a Profile

This example explains what the KCMS framework does when an application makes a KCMS "C" API call to load a profile.

  1. Using the KcsIO derivative, the framework determines the CMM Id of the profile.

  2. The framework calls the KcsProfile::createProfile() static method and loading starts. It uses the CMM Id of the profile as a key to determine the particular KcsProfile derivative to load. The CMM Id is associated with the dynamically loadable module using entries in the OWconfig file.

    Once dynamically loaded, the module returns a pointer to a KcsProfile object. If the particular CMM Id has no match in the OWconfig file, the framework uses the default KcsProfile derivative, KcsProfileKCMS. There is a special CMM Id key dflt entry in the OWconfig file, so that your CMM can override the default KcsProfile class. (See "KcsProfile Example" for details.) If you want your CMM to override the default KcsProfile class, it must duplicate all of the functionality that the default class handles.

  3. The framework calls the load() method on the KcsProfile object pointer that was created in step 2. This causes a KcsProfileFormat object pointer to be created using an entry in the OWconfig file. Then the KcsProfileFormat object loads itself.

    The profile format Id (also called the profile signature or magic number, which starts at byte 36 of the ICC profile format) is used as the key to this entry in the OWconfig file. (For details on the ICC profile format, see "ICC Profile Header".)

  4. The KcsProfileFormat object contains pointers to a KcsAttributeSet object and an array of pointers to KcsXform objects. The framework also creates these objects and calls their load() methods so they load themselves from the static store.

    Your CMM can derive directly from the KcsAttributeSet object, since it is statically linked into the KCMS framework. The KcsXform array has an OWconfig entry that uses a 4-byte identifier as a key. For ICC-based profiles, use the 8- and 16-bit LUT tags, mft1 and mft2. (See "KcsXform Example" for details.)

  5. If all pieces of the profile load successfully, the framework returns a KCS_SUCCESS status to the calling application.

Getting Attributes

Once a profile is loaded (see "Loading a Profile" ), an application can call the KcsGetAttribute() "C" API function to retrieve the profile's attributes. The following outlines the flow of control at the framework level to obtain the profile's attributes (see Figure 3-2):

  1. For the appropriate object in the KcsProfile object array, the framework calls its getAttribute() method.

  2. The KcsProfile()XXXX::getAttribute ()calls its KcsProfileFormat::getAttribute() method.

  3. This in turn calls its KcsAttributeSet::getAttribute() method.

  4. The KcsAttributeSet::getAttribute() method gets the attribute and returns it back up the chain to the API layer.

    Figure 3-2 KcsGetAttribute() Flow Example

    Graphic

A similar flow of control is true for the other KCMS "C" API calls.

KCMS Framework Primary Operations

The remainder of this chapter describes how the KCMS framework operates from the perspective of the KCMS "C" API. Code examples show KCMS "C" API calls that perform the following tasks:

Within the primary framework, events are illustrated and described in sequence to explain what actually takes place when each "C" API call is made.

Loading a Profile From the Solaris File System

The framework must have a profile with which to operate. Example 3-1 is a KCMS "C" API code excerpt that loads a scanner profile with a file name.


Example 3-1 Loading a Profile from the Solaris File System

KcsProfileId      

scannerProfile; KcsProfileDesc						scannerDesc; KcsStatusId						status;

char			*in_prof= "kcmsEKmtk600zs";  scannerDesc.type =

KcsSolarisProfile; scannerDesc.desc.solarisFile.fileName = in_prof;

scannerDesc.desc.solarisFile.hostName = NULL;

scannerDesc.desc.solarisFile.oflag = O_RDONLY;

scannerDesc.desc.solarisFile.mode = 0;  /* Load the scanner profiles */ status

= KcsLoadProfile(&scannerProfile, &scannerDesc,  			KcsLoadAllNow); if

(status != KCS_SUCCESS) { 	fprintf(stderr,"scanner KcsLoadProfile failed

error =  			0x%x\n", status); 	return(-1); }

In the example, the KCMS API layer calls KcsLoadProfile() to inform the KCMS framework that a profile description of type KcsSolarisProfile is to be loaded. The name of the profile and the options for opening that file are also specified using the solarisFile entry in the KcsProfileDesc structure.

Figure 3-3 Creating a KcsIO Object

Graphic

Creating a KcsIO Object

As a result of the call to KcsLoadProfile() the framework creates a KcsIO object. Figure 3-3 illustrates how the KcsLoadProfile() API call is implemented. The legend indicates the source of each call (KCMS "C" API layer, framework, "C" wrapper). In addition, the legend shows where the OWconfig file and the loadable CMM module fit into the overall scheme of loading the profile to creating a KcsIO object.

Looking ahead for a moment, Figure 3-4, Figure 3-5, and Figure 3-6 show the progression of framework calls that ultimately load the profile as chunks of data and return the profile Id to the calling application.

Returning now to Figure 3-3, the KCMS framework and the dynamic loading mechanism perform the following task sequence when KcsLoadProfile() is called:

  1. The framework gets a new profile Id. The framework maintains a dynamically allocated global array of profiles. The getNewValidProfileIndex() method allocates a new profile entry.

  2. The framework creates a KcsIO pointer. (All profiles access their data using the KcsIO independent access mechanism.) The KcsIO pointer is created based on the type field of the KcsProfileDesc structure pointer passed in from KcsLoadProfile().

    Two externally available types are built into the libkcs library, KcsFile and KcsMemoryBlock. A third derivative, KcsRemoteFile, is used with the KcsSolarisFile and KcsXWindow classes. In this particular example, KcsSolarisFile is not built into the libkcs library, so the dynamic loading mechanism creates one.

  3. The dynamic loading mechanism turns the KcsProfileDesc->type structure pointer field into a 4-character string and searches entries in the OWconfig file for the entries that correspond to loadable KcsIO classes. If it finds a match, it dynamically loads the KcsIO module. This action supplies the framework with a shared object to load. (See "KcsIO Example" for details.)

    The KcsIO module contains calls to a list of known function names. The framework uses dlsym(3X) to bring these functions into the framework to create and load a pointer to a KcsIO derivative.

  4. Once the KcsSolarisFile object pointer is loaded, the framework uses the fileName, hostName, and open(2) arguments to search for the profile. First, it checks the hostName to see if the file is on a local or remote machine. Depending on the location, the KcsSolarisFile object reuses the existing KcsIO class derivatives.

    If the file is on a local machine, the fileName is opened using open(2), and a KcsFile object pointer is created. If, however, the file is located on a remote machine, the fileName and hostName are passed to KcsRemoteFile and a KcsRemoteFile object pointer is created.

    As shown in Example 3-2, the KcsFile or KcsRemoteFile pointer that the KcsSolarisFile file object contains is then used to override the KcsIO methods of the same name.


    Example 3-2 Overriding KcsIO Methods With KcsSolarisFile

    // Just call myIO
    
    version of the call KcsStatus KcsSolarisFile::relRead(const long aBytesWanted,
    
    void *aBuffer, const char *aCallersName) {     KcsStatus status;      status =
    
    myIO->relRead(aBytesWanted, aBuffer, aCallersName);     return (status);
    
    }
    

Creating a KcsProfile Object

Once a KcsIO object has been created, the profile can be loaded. Figure 3-4 illustrates creating a KcsProfile object.

Figure 3-4 Creating a KcsProfile Object

Graphic

In Figure 3-4, the first step to loading the profile is to create a new KcsProfile object with the createProfile() static KcsProfile method. This method uses the CMM Id of the profile, which is located in a fixed place (bytes 4-7) in the profile header. (See "ICC Profile Header" for details.) The CMM Id determines the KcsProfile derivative to be created. If the CMM Id has no corresponding entry in the OWconfig file, the default KcsProfile class is created.

Creating a KcsProfileFormat Object

Once a KcsProfile object has been created, you can ask it to load itself using the generated KcsIO. Figure 3-5 illustrates the process.

Figure 3-5 Creating a KcsProfileFormat Object

Graphic

In Figure 3-5, the KcsProfile object creates a KcsProfileFormat object pointer using createProfileFormat(). Then createProfileFormat() searches the OWconfig file for loadable entries based on the profile format Id. For ICC profiles, the profile format Id (also called the profile file signature) is always acsp. (See "ICC Profile Header" for details.) Once the KcsProfileFormat object is created, the library generates a KcsAttributeSet object and an array of pointers to KcsXform objects.

Loading a KcsProfileFormat Object

The pointers to objects contained within the KcsProfileFormat object load themselves using the KcsChunkSet class. Figure 3-6 illustrates the process.

Figure 3-6 Loading a KcsProfileFormat Object

Graphic

In Figure 3-6, the KcsChunkSet class returns the blocks of data from the file, which were requested by the KcsAttributeSet and KcsXform objects. These objects interpret the block of data, turning it into tables for processing color data or sets of attributes. The KcsIO and KcsChunkSet classes do not interpret the data.

If the Solaris file system profile is successfully loaded, the framework increments the number of entries in the global profile array, and the profile Id is returned to the application.

Loading an X11 Window System Profile

In the next example, the framework loads a profile associated with a particular X11 Window System visual. The KcsXWindow object converts the display, visual, and screen information into a profile loaded into the KCMS framework. Example 3-3 is a KCMS "C" API code excerpt that shows this.


Example 3-3 Loading an X11 Window System Profile

if ((dpy =

XOpenDisplay(hostname)) == NULL) { 	fprintf(stderr, "Couldn't open

the X display \n"); 	exit(1); }  profileDesc.type = KcsWindowProfile;

profileDesc.desc.xwin.dpy = dpy; profileDesc.desc.xwin.visual =

DefaultVisual(dpy,  			DefaultScreen(dpy)); profileDesc.desc.xwin.screen =

DefaultScreen(dpy);  status = KcsLoadProfile(&profile, &profileDesc, 

			KcsLoadAttributesNow); if (status != KCS_SUCCESS) { 	status =

KcsGetLastError(&errDesc); 	fprintf(stderr,"KcsLoadProfile failed

error = %s\n",  			errDesc.desc); 	exit(1); }

The only difference between this example and Example 3-2, is the type of KcsIO class loaded. That example showed how to load a KcsSolarisFile object rather than a KcsXWindow object.

Connecting Two Loaded Profiles

Example 3-4 is a "C" API code excerpt that shows how to connect two profiles together once they have been loaded.


Example 3-4 Connecting Two Loaded Profiles

profileSequence[0] =

scannerProfile; profileSequence[1] = monitorProfile; status =

KcsConnectProfiles(&completeProfile, 2, profileSequence, op,

&failedProfileNum); if (status != KCS_SUCCESS) { 	fprintf(stderr,

"Connect Profiles failed in profile number  			%d\n",

failedProfileNum); 	KcsFreeProfile(monitorProfile);

	KcsFreeProfile(scannerProfile); 	return(-1); }

The KCMS framework implements the K()csConnectProfiles() API call as follows:

  1. It calls getNewValidProfileIndex()method to get a new valid index for the connected profile from the dynamically allocated global array of profiles.

  2. The new connected profile needs a KcsIO class to handle its I/O. Currently, this is stored in memory only, so it creates a KcsMemoryBlock object.

  3. It creates a KcsProfile object that can link together sequences of profiles.

  4. It attaches each profile in the sequence with attach() to the newly created KcsProfile object. The attach()method reference counts the objects. (Note that all classes are reference counted through inheritance from the KcsShareable class.)

  5. Once attached, the KcsAttributeSet object composes the attributes from the two KcsProfile members in the array into a single set of attributes for the newly created profile object.

  6. It links the KcsXform array so that the "into" and "out of" profile connection space (PCS) transforms of each KcsProfile object (profile) can be connected. When color data is processed through this sequence, it moves from input profile to PCS and from PCS to output profile.

  7. Once connected, it returns the new profile Id to the calling application for later reference, and generally cleans up the classes.

Evaluating Data Without Optimization

The evaluation path of data is different for unoptimized and optimized sequences. Figure 3-7 shows both paths.

Figure 3-7 Optimized And Unoptimized Evaluation

Graphic

In the unoptimized case, when evaluate() is called, the color data is moved from input space to PCS and from PCS to output space. This is achieved by passing the data through the appropriate KcsXform object in the KcsXform object array. The KCMS "C" API code excerpt shown in Example 3-5 evaluates data without optimization.


Example 3-5 Evaluating Data Without Optimization

/* set up the pixel

layout and color correct the image */ if (depth == 24)

	setupPixelLayout24(&pixelLayoutIn, image_in); else

	setupPixelLayout8(&pixelLayoutIn, red, green, blue,  			maplength); 

status = KcsEvaluate(completeProfile, op, &pixelLayoutIn, 

			&pixelLayoutIn); if (status != KCS_SUCCESS) { 	fprintf(stderr,

"EvaluateProfile failed\n"); 	KcsFreeProfile(monitorProfile);

	KcsFreeProfile(scannerProfile); 	KcsFreeProfile(completeProfile);

	return(-1); }

Evaluating Data With Optimization

When a profile sequence is optimized for speed, a set of tables is generated that does not require the color data to be passed through the PCS. As a result, the connected profile contains a composed KcsXform object that moves data directly from input space to output space. (Composition reduces multiple transforms into a single transform.) The KCMS "C" API code excerpt shown in Example 3-6 evaluates data with optimization for speed.


Example 3-6 Evaluating Data With Optimization for Speed

status =

KcsOptimizeProfile(completeProfile, KcsOptSpeed,  			KcsLoadAllNow); if

(status != KCS_SUCCESS) { 	 fprintf(stderr, "OptimizeProfile

failed\n"); 	 KcsFreeProfile(monitorProfile); 	

KcsFreeProfile(scannerProfile); 	 return(-1); }  /* set up the pixel layout

and color correct the image */ setupPixelLayout24(&pixelLayoutIn,

image_in);  status = KcsEvaluate(completeProfile, op, &pixelLayoutIn, 

			&pixelLayoutIn);  if (status != KCS_SUCCESS) { 	fprintf(stderr,

"EvaluateProfile failed\n"); 	KcsFreeProfile(monitorProfile);

	KcsFreeProfile(scannerProfile); 	KcsFreeProfile(completeProfile);

	return(-1); }

Freeing a Profile

Freeing a profile causes each of the objects pointed to by the profile Id in the framework's global array to release all of its associated data. If a given object is a shared or reference-counted object, the memory is released only if the reference count drops to zero.

Freeing a profile loaded via KcsSolarisProfile or KcsXWindowProfile closes the associated file descriptor or remote procedure call (RPC) connection if the file is located on a remote machine. Use the KcsFreeProfile(profile)() KCMS "C" API call to free a profile.

Attributes

The examples below show how to get and set attributes.

Setting an Attribute

When setting an attribute, the KcsProfile object in the KcsProfile object array passes the setting of the attribute to the KcsAttributeSet object contained in its KcsProfileFormat object. This is illustrated in Figure 3-2 and in the KCMS API code excerpt shown in Example 3-7.


Example 3-7 Setting an Attribute

/* double2icFixed

converts a double float to a signed 15 16 fixed point   * number */ /* Set

white point */ test_double[] = 0.2556; test_double[1] = 0.600189;

test_double[2] = 0.097794; attrValue.base.countSupplied = 1

attrValue.base.type = icSigXYZType; attrValue.base.sizeof(icXYZNumber);

attrValue.val.icXYZ.[0].X = double2icfixed(test_double[0], 

		icSigS15Fixed16ArrayType); attrValue.val.icXYZ.[0].Y =

double2icfixed(test_double[1],  		icSigS15Fixed16ArrayType);

attrValue.val.icXYZ.[0].Z = double2icfixed(test_double[2], 

		icSigS15Fixed16ArrayType); rc = KcsSetAttribute(profileid,

icSigMediaWhitepointTag, &attrValue); if (rc != KCS_SUCCESS {

	KcsGetLastError(&errDesc); 	fprintf(stderr, "unable to set

whitepoint: %s\n", errDesc.desc); 	KcsFreeProfile(profileid); 	return

(-1); }

Getting an Attribute

When getting an attribute, the KcsProfile object in the array passes the getting of the attribute to the KcsAttributeSet object contained in its KcsProfileFormat object (replacing set with get). This is illustrated in Figure 3-2 and in the KCMS API code excerpt shown in Example 3-8.


Example 3-8 Getting an Attribute

/* Get the colorants */

/* icfixed2double converts signed 15.16 fixed point number to a double   *

float */ /*Red */ attrValuePtr = (KcsAttributeValue *)

malloc(sizeof(KcsAttributeBase) +  	sizeof(icXYZNumber));

attrValuePtr->base.type = icSigXYZArrayType;

attrValuePtr->base.countSupplied = 1; status = KcsGetAttribute(profileid,

icSigRedColorantTag, attrValuePtr); if (status != KCS_SUCCESS) { 	status =

KcsGetLastError(&errDesc); 	printf("GetAttribute error: $s\n",

errDesc.desc); 	KcsFreeProfile(profileid); 	exit(1); }  XYZval = (icXYZNumber

*)attrValuePtr->val.icXYZ.data; printf("Red X=%f Y=%f Z=%f\n", 

		icfixed2double(XYZval->X, icSigS15Fixed16ArrayType), 

		icfixed2double(XYZval->Y, icSigS15Fixed16ArrayType),     

icfixed2double(XYZval->Z, icSigS15Fixed16ArrayType),

Characterization and Calibration

Characterization and calibration are accessed using the following KCMS "C" API calls:

See the SDK manual KCMS Application Developer's Guide for more information on these calls.

The KcsProfile base class contains virtual methods to characterize and calibrate two types of devices: scanners and monitors. You must decide whether to override the base functionality to take characterization and calibration data and turn it into the appropriate KcsXform data.


Note -

Currently, the default CMM supports monitor and scanner characterization and calibration only. It does not support printer characterization and calibration. However enabling hooks exist in the source so you can write a CMM that supports printers.


Attributes are set using the normal mechanisms. The KCMS "C" API code excerpt in Example 3-9 shows characterization and calibration.


Example 3-9 Characterization and Calibration

KcsCalibrationData      

       *calData;     KcsCharacterizationData														*charData;    

float														Luminance_float_out[3][256];    

double														test_double[3];   /* this is a test which does not use

real data - just a gamma curve for the *  calibration structure and the same

curve *.75 for the characterization curve. */      /*create luminance tables

with a gamma = 2.22 */  	for (j=0; j<levels; j++) { 			input_val = j *

(1.0/255.0); 			Luminance_float_out[0][j] = pow(input_val, 2.22);

			Luminance_float_out[1][j] = pow(input_val, 2.22);

			Luminance_float_out[2][j] = pow(input_val, 2.22);     }     /* Fill out the

measurement structures - The illuminant must be D50 */     test_double[0] =

0.9642;     test_double[1] = 1.0;     test_double[2] = 0.8249;     sizemeas =

(int) (sizeof(KcsMeasurementBase) + sizeof(long) + 		  	

sizeof(KcsMeasurementSample) * levels);     charData =

(KcsCharacterizationData *) malloc(sizemeas);     

charData->base.countSupplied = levels;     charData->base.numInComp = 3;

    charData->base.numOutComp = 3;     charData->base.inputSpace =

KcsCIEXYZ;     charData->base.outputSpace = KcsRGB;     for (i=0; i<

levels; i++) { 			charData->val.patch[i].weight = 1.0;

			charData->val.patch[i].standardDeviation = 0.0;

			charData->val.patch[i].sampleType = KcsChromatic; 

			charData->val.patch[i].input[KcsRGB_R] = (float)i/255;

			charData->val.patch[i].input[KcsRGB_G] = (float)i/255;

			charData->val.patch[i].input[KcsRGB_B] = (float)i/255;

			charData->val.patch[i].input[3] = 0.0; 

			charData->val.patch[i].output[KcsRGB_R] =

(Luminance_float_out[0][i])/0.75;

			charData->val.patch[i].output[KcsRGB_G] =

(Luminance_float_out[1][i])/0.75;

			charData->val.patch[i].output[KcsRGB_B] =

(Luminance_float_out[2][i])/0.75; 			charData->val.patch[i].output[3] =

0.0;     }     charData->val.patch[0].sampleType = KcsBlack;    

charData->val.patch[255].sampleType = KcsWhite;      sizemeas = (int)

(sizeof(KcsMeasurementBase) + sizeof(long) + 		   	

sizeof(KcsMeasurementSample) * levels);     calData = (KcsCalibrationData *)

malloc(sizemeas);      calData->base.countSupplied = levels;    

calData->base.numInComp = 3;     calData->base.numOutComp = 3;    

calData->base.inputSpace = KcsRGB;     calData->base.outputSpace =

KcsRGB;     for (i=0; i< levels; i++) { 			calData->val.patch[i].weight

= 1.0; 			calData->val.patch[i].standardDeviation = 0.0;

			calData->val.patch[i].sampleType = KcsChromatic; 

			calData->val.patch[i].input[KcsRGB_R] = (float)i/255;

			calData->val.patch[i].input[KcsRGB_G] = (float)i/255;

			calData->val.patch[i].input[KcsRGB_B] = (float)i/255;

			calData->val.patch[i].input[3] = 0.0; 

			calData->val.patch[i].output[KcsRGB_R] = Luminance_float_out[0][i];

			calData->val.patch[i].output[KcsRGB_G] = Luminance_float_out[1][i];

			calData->val.patch[i].output[KcsRGB_B] = Luminance_float_out[2][i];

			calData->val.patch[i].output[3] = 0.0;     }     

calData->val.patch[0].sampleType = KcsBlack;    

calData->val.patch[255].sampleType = KcsWhite;      printf("Update a

profile with characterization and calibration data.\n");     rc =

KcsUpdateProfile(profileid, charData, calData, NULL);     if(rc !=

KCS_SUCCESS) {         KcsGetLastError(&errDesc);         fprintf(stderr, 

               "unable to update profile: %s\n", errDesc.desc);   

     KcsFreeProfile(profileid);         return(-1);     }

Saving a Profile to the Same Description

Saving a profile to the same description is the same as loading in reverse. Each object pointed to or contained within the KcsProfile object is instructed, with its own save mechanisms, to write the data needed to reconstruct itself out to static store. In this case, the description is identical to that used to load the profile, so the current KcsIO associated with the profile is used. Example 3-10 is a KCMS "C" API code excerpt that saves a profile to the same description.


Example 3-10 Saving a Profile to the Same Description

status

= KcsSaveProfile(profileid, NULL); if(status != KCS_SUCCESS) { 	status =

KcsGetLastError(&errDesc); 	printf("SaveProfile error: %s\n",

errDesc.desc); }


Saving a Profile to a Different Description

To save a profile to a different description, load a new KcsIO so that the KcsProfile object can save itself. This is accomplished with the same mechanism as that described in steps 2 to 5 of "Creating a KcsIO Object" . Example 3-11 is a KCMS "C" API code excerpt that saves a profile to a different description.


Example 3-11 Saving a Profile to a Different Description

/* Application opens the

file */ if ((sfd = open(argv[2], O_RDWR|O_CREAT, 0666)) == -1) { 	perror

("save open failed"); 	exit (1); }  desc.type = KcsFileProfile;

desc.desc.file.openFileId = sfd; desc.desc.file.offset = 0; status =

KcsSaveProfile(profileid, &desc); if(status != KCS_SUCCESS) { 	status =

KcsGetLastError(&errDesc); 	printf("SaveProfile error: %s\n",

errDesc.desc); }