|
| 以 PDF 格式下载本书
KcsXform Derivative
7
- This chapter discusses the following topics to help you create a KcsXform class derivative that is dynamically loadable at runtime:
-
- External entry points with an example
- Member function override rules
- Helpful information and hints on using many of the KcsXform member functions
-
KcsXformSeq derivative
External Entry Points
- The KCMS framework uses external entry points to load your derivative as an executable. The mandatory and optional entry points are described.
Mandatory
- When you derive from a KcsXform class, the mandatory external entry points are:
-
extern long KcsDLOpenXfrmCount;
KcsXform * KcsCreateXfrm(KcsStatus *aStat,
KcsChunkSet *aChunkSet, KcsChunkId aChunkId,
KcsAttributeSet *aAttrSet);
|
-
KcsCreateXfrm() creates an instance of a KcsXform derivative.
Optional
- When you derive from a KcsXform class, the optional entry points are:
-
KcsStatus KcsInitXfrm();
KcsStatus KcsCleanupXfrm();
|
Example
- The following example shows you how to use the entry points when creating a KcsXform derivative.
-
Code Example 7-1 KcsXform Class Entry Points Example
-
//External entry points for runtime derivation
extern "C"{
extern long KcsDLOpenXfrmCount;
KcsXform* KcsCreateXfrm(KcsStatus *aStat,
KcsChunkSet *aChunkSet,
KcsChunkId aChunkId,
KcsAttributeSet *aAttrSet);
KcsStatus KcsInitXfrm();
KcsStatus KcsCleanupXfrm();
}
extern long KcsDLOpenXfrmCount = 0;
/* Global initialization */
KcsStatus
KcsInitXfrm(long libMajor, long libMinor, long *myMajor, long *myMinor)
{
// Set up the return values
*myMajor = KCS_MAJOR_VERSION;
*myMinor = KCS_MINOR_VERSION;
//Check the major version
if (libMajor != KCS_MAJOR_VERSION)
return (KCS_CMM_MAJOR_VERSION_MISMATCH);
//Currently, if minor version of library is less than the KCMS
// minor version, return an error.
if (libMinor < KCS_MINOR_VERSION)
return (KCS_CMM_MINOR_VERSION_MISMATCH);
|
-
Code Example 7-1 KcsXform Class Entry Points Example (Continued)
-
//Library guarantees if your minor version number is greater than
//KCMS minor version number, it will handle it. No more init.
return(KCS_SUCCESS);
}
KcsXform *
KcsCreateXfrm(KcsStatus *aStat, KcsChunkSet *aCS,
KcsChunkId aChunkId, KcsAttributeSet *aAttrSet)
{
//Create the new derivative
return(new KcsTechUCP(aStat, KcsLoadAllow, aCS, aChunkId,
aAttrSet));
}
/* Global clean up */
KcsStatus
KcsCleanupXfrm()
{
KcsStatus sStat;
return(KCS_SUCCESS);
}
|
Member Function Override Rules
- The following table tells you which KcsXform member functions you must override, can override, and should not override when deriving from this class. The member functions indicated with an "X" in the Must column are required to successfully derive from this base class. All of these member functions are defined in the kcsxform.h header file and the KCMS CMM Reference Manual.
-
Table 7-1 KcsXform
| Member Function | Must | Override Rules
Can Do Not
|
| compose() |
| X |
|
| connect() |
| X |
|
| connectSink() |
| X |
|
| connectSource() |
| X |
|
| convertXform() |
| X |
|
-
Table 7-1 KcsXform(Continued)
| Member Function | Must | Override Rules
Can Do Not
|
| eval() | X |
|
|
| getAttrSet() |
| X |
|
| getComponentDepth() |
|
| X |
| getLoadOrder() |
| X |
|
| getNumComponents() |
|
| X |
| getSaveOrder() |
| X |
|
| KcsXform() | X |
|
|
| ~KcsXform() |
| X |
|
| kindOfTransform() |
|
| X |
| loadU() |
| X |
|
| numberOfCallbacks() |
| X |
|
| optimize() |
| X |
|
| saveU() |
| X |
|
| setAttrSet() |
| X |
|
| setCallbackInterval() |
| X |
|
| setComponentDepth() |
| X |
|
| setDefaultAttributes() |
| X |
|
| setNumComponents() |
| X |
|
| validateLayouts() |
| X |
|
Technology
- In the KCMS framework environment, the term technology means algorithms, code, and data used to implement a specific method of color transformations. All supported technologies must supply a certain uniform set of functionality. You can do this in C++ by having a transform base class with pure virtual methods. Each technology is implemented in a derived class that must implement the required virtual methods.
- With transform conversion, a technology or base class can default to a specific derivative with the functionality that best meets that technology. For example, the base class is aware of only one type of xform derivative that can save universally; therefore, the default saveU() method converts whatever it is into a KcsTechUCP. Then it asks the converted xform to do the saveU().
Xform Attributes
-
Xforms contain their own KcsAttributeSets. They are passed in through all constructors and default to NULL. The KcsAttributeSets are copied and not shared by default; they are set by their constructor callers. All derivative constructors are updated.
- The KcsProfile base class copies some standard attributes to the appropriate xform. Access is through methods that set and get the attribute set; therefore, all access to these attributes are equal to the interface to KcsAttributeSets.
Optimization
- Transform optimization includes one or more compositions, but this is not always the case. That is why optimize() is separate from composition. Generally stated, optimizing an object makes it smaller, faster, more precise, or some combination of the above. It is up to the derivative to figure out what is best for its situation. For example, if your derivative contains extra tables for quality purposes and is requested to optimize for space and speed, it may very well throw away those extra tables.
- During a save, this same derivative may not want to get rid of these extra tables. Instead, it either can use the hierarchical method described in save or reread the tables back into memory and save them again. It is up to the derivative. The choice might depend on the size of the table or some error constraint on the transform. See "KcsXformSeq Derivatives" on page 88 for information on how a derivative always composes and keeps that xform for evaluation. Also note that it keeps the original xforms in the list unless it is also told to optimize for size, after which it will get rid of them.
- If you optimize something away and then subsequently save it, that pruned data may not be replacable. Ultimately it is up to the derivative to decide when and how to make that determination. It may (and probably will) change between releases of that derivation as well.
- Optimization must be defined by the derivative if that functionality is needed. Only the derivative instances understand how best to optimize. The derivation can refuse any optimization request. It also can prioritize the types of optimization if more than one bit is set. For example, if the instance is told to optimize for space and speed and speed means to add space, then if you feel it is appropriate, add the space to support the speed increase.
Loading
- Defer some loading functionality to other objects in the KCMS framework, because the objects can minimize and load more efficiently. With the KcsXform class, the object does not need to implement the load in all derivatives for the first time. This means that the profile instance has the objects (in this case the xform) load and unload themselves, but it still has to load and minimize objects, through construction and destruction, to make up for those KcsXform derivatives that do not load and minimize.
- The KcsXform base class provides the default load virtual methods that return the KCS_NOT_RUNTIME_LOADABLE error. This error allows the KcsProfile class or any other xform container to check for this error condition and to use another approach if necessary.
-
Note - Currently, not all technologies provide their own loading mechanism; use the base class functionality.
Save Types
- Since there is more than one way to save, derivatives can specify the order in which its pieces get saved. The save types consist of bitsets and are:
-
- Xform saves and loads data in a chunk, but uses the universal format. This type allows the KcsProfileFormat derivative to map a chunk set's chunk Id to the data in Universal format so that the KcsXform class can use the getChunk() method. This type sets its own bit and the Universal bit since it saves universally.
- Three choices are available with an extensible protocol in which:
-
Universal
- The base class supports saving in the universal format. The save() method converts the object to ICC 3.0 to icLutX form. You need to provide allocation of the *aLut argument. When complete, the converted date is copied to the *aLut variable. This method is used by other objects during save(). The ICC 3.0 profile format derivative calls KcsXform during its save to convert the xform into the appropriate ICC transform tag. If not overridden, the KcsXform base class converts the xform into a KcsXform derivative that supports the save() method and returns its conversion. If a derivative needs more control over this type of save, then it must override this method.
Private
-
Private saving is when the chunk set and chunk Id associated with the instance are used to save. The derivative only needs to package all of it's data into a contiguous piece of memory and pass the address and its chunkId to the object's chunk set. If this is too limiting, you can split the derivative's pieces into different chunks, each with its own chunkId. The only caveat is that the instance must then place all of those chunkIds into one chunk which is ultimately saved as the top of the object.
- This approach is appropriate when the object has many data structures that it does not want to store into one contiguous memory block. It also helps with loading if all of the pieces are not needed all of the time. This is the overall approach taken by the KCMS framework where the KcsProfile class has a table of chunkIds, one of which is the attribute chunkId for this profile. When loading attributes only, it is faster to use getchunk() and load just the attribute object than it is to use getchunk() and load the entire set of objects that represent a profile.
Example
- ICC has both universal and private places for xform data. The InterColorProfileFormat asks for the load order and gives a list of universal plus private. The UCP derivative responds with universalAsPrivate. Since the derivative knows that UCPs can do this, it asks anything that does not do universal to convert themselves into a UCP. This follows the second way to break an obligation since the InterColorProfileFormat actually converts the xform to another kind and saves the converted one. It never saves the original.
- The typdefs are as follows:
-
typedef long KcsLoadSaveSet;
#define KcsNoParts ((KcsLoadSaveSet)0x00000000)
#define KcsPrivatePart ((KcsLoadSaveSet)0x00000001)
#define KcsUniversalPart ((KcsLoadSaveSet)0x00000002)
#define KcsUniversalisPrivate
((KcsLoadSaveSet)((0x80000000)|KcsUniversalPart|KcsPrivatePart))
|
Composition
- Some technologies convert from another technology (Xform *). For example, CS1.0 logTech can generate one of itself from any other (KcsXform *) derivative. This is done by calling the compose() method that takes a (KcsXform *) and returns a (KcsXform **). Supply a callback function because this can be a slow operation.
- The KCMS framework uses this protocol to implement a sequence xform derivative that can take many xforms and treat them as one by sequentially evaluating the chain. Since the KcsXformSeq class is a KcsXform derivative, one LogTech can be generated that represents the complete connection. This has tremendous speed and quality advantages.
- The base class performs composition using the default CMM.
Evaluation
- When a KcsXform is instantiated, it is ready to transform n->m component data (unless it is in the process of being built). Since it can handle many different data formats, the KCMS framework has encapsulated the description of the data to be transformed into a data structure called KcsPixelLayout. This structure is an array of component descriptions. See the KCMS Application Developer's Guide for more information on KcsPixelLayout.
- The KcsPixelLayout structure is used by the eval() methods to describe the information to be transformed. When using an eval()method, supply a source PixelLayout, a destination PixelLayout, and a callback function. The eval()method takes the data described by the source layout, transforms it, and puts it into the buffer described by the destination layout. If the evaluation is going to take a long time, the callback function is repeatedly called until evaluation is complete.
- The layouts can describe the same buffer (called in-place transformations). In this case, the eval() method detects it is the same buffer and optimizes for performance. These layouts can also specify different buffers, in which case the data is moved as well as transformed. You can even supply two layouts which differ in composition (for example, planar RGB and chunky CMYK), and the data will be moved and transformed from RGB to CMYK as well as have its composition transformed from planar to chunky. The evaluation methods will do so with minimal steps. The evaluation is most efficient when given large buffers of data to transform.
- If the transform is not compatible with the layout(s), it returns an error. For example, if an RGB->CMYK KcsXform * was given two three-component descriptions, it would return an error since the destination was expecting four-component data.
- If your data can be represented in different formats, get the value of the attribute KcsAttrPixelLayoutSupported to see what the most efficient pixel layout is for that xform derivative.
Evaluation Helper Methods
- There is only one eval() method that is pure virtual. It is the one with two pixel layouts and a callback. Other eval() methods are overloaded in the base class to allow other data types to fit a long on each side of the xform. For example, (long *) -> (long *) and aRGB->aR'G'B'. The base class takes all the overloaded methods and creates a pixel layout for each method and then calls the pure virtual method; therefore, derivatives only need to implement one eval() method.
- When starting an evaluation, the derivative can use any of the helper functions provided for pixel layout usage. The convertLayouts() method takes any layout and transforms it into any other. If a derivative can only handle chunky, then the derivative may want to convert a non-chunky derivative to chunky before the evaluation is started.
KcsXformSeq Derivatives
- The KcsXformSeq class is a KcsXform derivative that allows a list or concatenation of KcsXforms to act as one KcsXform. It is an alias to an ordered transform collection that allows all normal list management in addition to all of the required KcsXform protocols. It also allows a hierarchy of KcsXform instances by providing the ability to sequence the list. Evaluating through a sequence of KcsXforms is like serially running each transform, with successive transforms taking input from the output of its predecessor and ending with the last one putting its output into the destination location.
Constructs and Destructors
- You can construct a KcsXformSeq with:
-
- This constructor also provides the required load hints.
-
Saving
- Saving trickles down throughout the whole connected hierarchy. Any change to any xform in the sequence is saved when the sequence is saved. This happens because the sequence shares the xforms passed to it. The instance also gets the chunkIds from each transform in the list. It then packs these and other state information into a memory block and does a setChunk() to allow lookup of this transform list upon a load request to the sequence.
- When requested to save in universal format, the sequence does a composition that generates one xform that is saved in universal format.
Loading and Constructing the List
- A KcsXformSeq instance saves its xforms as a list of chunkIds to later instantiate when needed. For every chunkId in its own chunk, getChunkXform() takes the current chunk set and chunkIds and, through the chunk set protocol of createXform(), allocates the transform represented by that unique combination.
Connections
- Connection is the only reason the KcsXformSeq class exists. No other support in the KCMS framework for connections exist except the KcsXformSeq class. The base KcsXform class uses a sequence derivative in its connect() method.
- To make a connection, call the constructor that takes a list of transform pointers, or create a sequence of 0s and edit the transforms list with the list() methods (or use a combination of the two). See "Validation" on page 91 for a description of the connection method.
Optimization
- When a sequence is told to optimize itself, first it optimizes each transform in the chain individually. Then it composes all the transforms into one KcsTechUCP xform. Finally it uses that composed KcsTechUCP to do future evaluations. Overall optimization is provided with optimization and composition of the individual transforms in the list.
- The KcsXformSeq class performs composition by asking each transform in the list to compose. If none comply, it uses the base class method to compose. It attempts to compose from the rightmost to leftmost. By doing so, the harder-to-model devices (typically printers, which are on the right) get composed first.
- If you request to optimize for size, KcsXformSeq detaches all of the original list. After optimizing for size, the only way to regenerate the original list is to build it again.
Composition
- The KcsXformSeq class uses the compose() method to implement optimization. Since the KcsXformSeq class is a KcsXform derivative, one KcsTechUCP can be generated that represents the complete connection. This offers performance and quality advantages.
Evaluation
- Evaluation of a KcsXformSeq instance is done with either the optimized or non-optimized technique.
- Optimized evaluation uses the composed transform it constructed when told to optimize. It keeps a pointer to that optimized transform in its private section. When asked to evaluate, it passes the information down to the optimized transform.
- Unoptimized evaluation is used when the sequence is not optimized. This implementation evaluates the data through the list of xforms sequentially. Between transforms a buffer is used to hold the temporary calculations. The first step evaluates from the source buffer, while the last step evaluates into the destination buffer.
- Up to two different extra buffers are used between non-endpoint transforms, depending on the layout of the data. They are swapped between eval()s. If the composition of the transforms is different (for example, chunky and planar), two buffers are needed. If the implementation did not use this technique, the data from one complete pixel (or component set) overrides a different (set of) pixel. The eval() method always alternates between two buffer pointers. Both buffer pointers point to the same buffer if a transform's output buffer is compatible with the next transform's input buffer. This can be optimized further if all buffer layouts describe a buffer that is compatible with the destination buffer supplied by the caller. If this is the case, the buffer pointers point to the destination buffer described. And if the caller has its source and destination buffer the same, everything ultimately uses one buffer. Such buffers are KcsMemoryBlocks that can be resized.
Validation
- Each time a connection is made, it is validated against a set of rules defined in this class. The rules use the current set of attributes as well as the current state of all of the transforms in the connection.
- If the sequence rules pass, the sequence passes itself down to all of the validation methods of each xform in the list. In this way, all xforms are allowed to determine if a connection can be made. If an error occurs in any xform, the connection is refused.
The List
- The list of xforms is represented by a memory block of pointers to KcsXforms. The size of the block is incremented by a constant each time the current block fills with pointers. A few methods access and edit the list.
- Note that a NULL parent starts the list based on this sequence and you must pass the last parent found into the next call of this function and use the same object for invocations of this method. It returns KCS_END_OF_XFORMS when it reaches the end of the xforms in the sequence. All getNextXform() calls are sequential. Any sharing of an object must take this into account; otherwise, two different results may occur if the gets are not synchronized. It works correctly when called on a sequence that is a part of another sequence; it runs through that subsequence only.
- For example, given sequence A (a->B->e) and sequence B (c->d) where a, c, d, and e are primitive xform types: A->GetNext(). If called (starting with a NULL parent **) until it returns KCS_END_OF_SEQUENCE, it returns xforms in the following order: a, c, d, e, B->GetNext(). If called (starting with a NULL parent **) until it returns KCS_END_OF_SEQUENCE, it returns xforms in the following order: c, d. It also skips over all sequences of 0 xforms as if they are not even there.
|
|