Contained WithinFind More DocumentationFeatured Support Resources | Scarica il manuale in formato PDF (257 KB)
Chapter 3 KCMS Framework OperationsIn This ChapterThe 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 ArchitectureFigure 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
KcsProfileKcsProfile 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
KcsProfileFormatEach 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. KcsAttributeSetEach 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. KcsXformThe 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 ExamplesThe 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 ProfileThis example explains what the KCMS framework does when an application makes a KCMS "C" API call to load a profile.
Getting AttributesOnce 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 ):
A similar flow of control is true for the other KCMS "C" API calls. KCMS Framework Primary OperationsThe 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 SystemThe 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 SystemKcsProfileId
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
Creating a KcsIO ObjectAs 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:
Creating a KcsProfile ObjectOnce 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
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 ObjectOnce a KcsProfile object has been created, you (???your CMM)can ask it to load itself using the generated KcsIO. Figure 3-5 illustrates the process. Figure 3-5 Creating a KcsProfileFormat Object
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 ObjectThe 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
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 ProfileIn 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 Profileif ((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 ProfilesExample 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 ProfilesprofileSequence[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:
Evaluating Data Without OptimizationThe evaluation path of data is different for unoptimized and optimized sequences. Figure 3-7 shows both paths. Figure 3-7 Optimized And Unoptimized Evaluation
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 OptimizationWhen 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 Speedstatus =
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 ProfileFreeing 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. AttributesThe examples below show how to get and set attributes. Setting an AttributeWhen 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 AttributeWhen 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 CalibrationCharacterization 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 CalibrationKcsCalibrationData
*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 DescriptionSaving 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
Saving a Profile to a Different DescriptionTo 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); }
|
|||||||||||||||||