The image operation is built around a structure that must be present. Here is an example from the CircleRamp node. This node is shipped with RAYZ as a plugin, not as a native node:
static RPI_ImageOpInfo opinfo = { { "circleramp", // command name "", // not used "Silicon Grail", // author "1.0" // version "", // not used "", // not used }; CircleRampProcess, // image process NULL, // input region CircleRampOutputRegion, // output region NULL, // control processing NULL, // setup processing NULL, // cleanup processing 0, // minimum number of inputs CPI_FALSE, // does it multi-process? CPI_FALSE // disjoint regions? };This structure is defined in CPI/CPI_ImageOpProvider.h
Let's look at each of the fields in this structure:
Command Name -
This is the name of the image operation. This is the string which
would be used in scripting operations involving this function. When
a .rayz file is saved, this is the string which defines the operation
there. In the given example, the plugin would appear in the .rayz
file (or be accessed by the script writer) as
given_name = circleramp( ) { }Author - Any text string
Version -
This is not used by RAYZ, but is available for the programmer
Image Process - (required) This is the name of the subroutine that is called when a CircleRamp is to be created. The declaration for this routine is always
CPI_Bool ProcessName( CPI_Image *result, CPI_Image inputs[], CPI_Uint32 numInputs, CPI_Metadata myParms)In most cases, this routine first gets the relevant parameters via the metadata, and then performs its operation on the inputs, putting the result into (of course) *result.
Input region - (optional)
This routine describes the input needed to
complete the image operation.
If this function is not provided then the input region will be
assumed to be the same as the output region.
Output region - (optional)
This routine describes the region which will be the
result of this image operation. If the output size (or bit depth) is
not the same as the input, you should provide this function.
If this function is not provided, then the output region is
assumed to be the same as the input region.
Control processing - (optional) This routine is called if the 'disjoint regions' flag is set to CPI_TRUE. In that case, the control_processing() routine is passed the full sizes of all inputs, and it is the programmer's responsiblity to handle them correctly. See Regions.C as an example, as well as the section on region handling.
Setup processing - (optional)This would be used in conjunction with control_processing() above.
Cleanup processing - (optional)This would be used in conjunction with control_processing() above.
Number of inputs - This is the minimum number of inputs which are valid for this operation. RAYZ performs a quick sanity check with this number before proceeding.
Multi-processes - This is a boolean value which is CPI_TRUE if the plugin can multiprocess. In general, you can set this to CPI_TRUE if the calculations for any given pixel do not depend on the results of any other pixels - that is, if each output pixel can be calculated in parallel with every other.
However, thread safety is a complex issue, and is discussed more fully elsewhere.
Disjoint regions - This is a boolean value which is CPI_TRUE if the plugin can handle disjoint regions. That means that the plugin can cope with images which are not the same size, or which are offset from each other. If this is CPI_FALSE, then only the intersection, or common, regions are passed to the Process function. See Regions.C as an example, and also the section on region handling.
static RPI_NodeInfo ninfo = { { "CircleRamp", // node name "Circleramp", // menu name "Silicon Grail", // author "v0.1" // version "", // help URL "" // icon name }, CircleRampInit, // initialize node CircleRampShutDown, // shut down node NULL, // parameter changed NULL, // input changed CircleRampGetRegion, // return result region CircleRampGetFullSize, // return full size CircleRampGetRange, // return frame range NULL, // return errors CircleRampExec, // node execute NULL, // input label NULL, // output label NULL, // viewer label NULL, // viewer outputs 0, // min inputs 0, // max inputs 1, // min outputs 1, // max outputs CPI_FALSE // can add more inputs };This structure is defined in rayz/support/src/plugIn/CPI_NodeProvider.h Any fields which are optional may be given a simple NULL value; otherwise, they are the names of routines that the programmer provides. Let's look at each of the fields in this structure:
Node Name - A text string which labels the operation. This is the name that will be registered/unregisterd with the Exec (see later). This is also the name that will be used to reference the node in the nodemenu.tcl file. This is also the name of the nodes that will be created on the Worksheet, eg node_name1, node_name2, etc
Menu Name - This is the name which the user will see on the RAYZ menu. For file formats, this is the string which will appear in the popup menu on the Image In/Out nodes.
Initialize - Called to initialize the node. Typically, this means setting up the node parameter UI.
ShutDown - (optional) Called to exit the node.
ParameterChanged - (optional) Called with the name of the parameter whose value has just changed. This is used to make adjustments based on the user changing a parameter; for example, here is where you would enable/disable parameters based on menu choices.
InputChanged - (optional) Called with the number of the input (counting from 0) which has changed (ie, been replaced or deleted). If the plugin depends on info from the input(s), this is where you get that info.
GetRegion - (optional) Describes the size and bit depth of the result image from this node operation.
GetFullSize - (optional) Describes the full size of the result image. If this function is not provided, the output is assumed to be the same size as the input.
GetRange - (optional) Delivers the start and end frame numbers of the output of this node.
GetErrors - (optional) This is called by RAYZ to enable you to check for user errors. This allows the programmer to trap and handle errors before RAYZ begins to process the image operation.
NodeExecute - (required) This does the actual work. This is called with the previously defined metadata values, and the input(s) and a pointer to the output image.
InputLabel - (optional) Returns the label of the given input number.
OutputLabel - (optional) Returns the label of the given output number.
ViewerLabel - (optional)
A node can have more than one output, and each output can have more
than one viewer. (They are called viewers because you can't pass them
on for further processing, you can only view them).
For example, take the Ultimatte GK node. This has one output,
(that is, one output connector),
but that output has three view choices: the input image, the output image
and the filter area.
This routine returns the label for the given viewer output. Note that
you cannot rename the two default viewer outputs (see below), only the
extra ones which you
have added. You set this value with the ViewerOutputs function below.
See also Chapter 4 - Multiple Inputs and Outputs.
ViewerOutputs - (optional)
This returns the number of viewers available for each output of
the node. Most nodes only have a single output, but that output can have
many viewers (for example, the Ultimatte GK node). By default, there
are always 2 - the input and the output of the node. When you add more,
you add to these two.
See also Chapter 4 - Multiple Inputs and Outputs.
Growable flag - If this is set to CPI_TRUE, the node will grow to allow more inputs to be added. An example of this in RAYZ is the MultiComp node.
Init Called when RAYZ is started (so once per node type) Callbacks: Supplied by programmer to plugin ParamChanged When a parameter to the node changes value InputChanged When an input image to the node changes or is deleted Information: Supplied by programmer to RAYZ GetRegion GetFullSize GetRange GetErrors Exec When an image or computation is requested by user ShutDown Called when the user quits RAYZ For Image Ops, or script commands, a similar but much shorter list is used: InputRegion Tell rayz how big the input region should be OutputRegion Tell rayz how big the result will be ControlProcessing Oversee the image processing, if required Process Do the actual operation The minimum required is: Node Init Exec Shutdown Image Op Process
typedef struct { CPI_Int32 sizeX; CPI_Int32 sizeY; CPI_Int32 offsetX; CPI_Int32 offsetY; CPI_Int32 channels; CPI_Int32 skipPixels; // see note below CPI_Int32 bitsPerPel; // passed in as 8, 16, or 32 CPI_Float32 pixelRatio; CPI_Int32 dataType: // 0 = linear, 1 = log, 2 = video CPI_Float32 referenceBlack; CPI_Float32 referenceWhite; CPI_Float32 linearWhite; CPI_Float32 displayGamma; CPI_Float32 filmGamma; CPI_Float32 videoGamma; CPI_Bool isPremultiplied; CPI_Float32 backingRed; // backing color CPI_Float32 backingGreen; CPI_Float32 backingBlue; CPI_Float32 backingAlpha; CPI_Float32 backingOther; } CPI_ImageContext; typedef struct { CPI_ImageContext myContext; void *data; } CPI_Image;
Because in RAYZ the Node and the Image Operation are separate things, there needs to be a mechanism to get parameters from the one to the other. That mechanism is called metadata. To use this, you allocate a metadata object, and then fill it with name/value pairs. Names are always strings, values are dependent on the call being used. Typically, this looks like:Exec () { // get input(s) and parameter values from GUI ... // allocate metadata and fill it with parameters CPI_Metadata nodeparms = cpiCreateMetadata(); cpiSetMetaFloat32( nodeparms, "centerx", centerX ); cpiSetMetaFloat32( nodeparms, "centery", centerY ); cpiSetMetaFloat32( nodeparms, "radius", radius ); cpiSetMetaInt32( nodeparms, "oval", oval ); // add image operation to list // note that even if a node takes no params, it still must include // a metadata structure if ( myInputs != NULL ) retval = cpiAddImageOp( "cropcircle", nodeparms, &myInputs, 1 ); else retval = NULL; cpiDeleteMetadata( nodeparms ); return retval; }In the Exec function of the node, the input image(s) and parameter values are obtained. Then a metadata object is created, and filled with whatever information needs to be passed to the image operation. This is done via the cpiAddImageOp() function call.If the image operation takes no arguments, then you can pass NULL as the metadata structure in the call to cpiAddImageOp().
Then in the Process function of the image op, you typically do this:
#define MAX8 (CPI_Uint8)255 #define MAX16 (CPI_Uint16)65535 #define MAXF (CPI_Float32)1.0F Process () { // unload metadata to get image operation parameters cpiGetMetaFloat32( myParms, "centerx", ¢erx ); cpiGetMetaFloat32( myParms, "centery", ¢ery ); cpiGetMetaFloat32( myParms, "radius", &radius ); cpiGetMetaInt32( myParms, "oval", &oval ); ... // perform the operation. Often this is done via a templated function // which is called like this if ( result->myContext.bitsPerPel == 8 ) doThing( inputs, result, center, radius, oval, MAX8 ); else if ( result->myContext.bitsPerPel == 16 ) doThing( inputs, result, center, radius, oval, MAX16 ); else doThing( inputs, result, center, radius, oval, MAXF ); }RAYZ has lots of routines for handling metadata. Some common ones are given here. The full list is given in Appendix C. You can also find the the function definitions in rayz1.0/support/plugIns/include/CPI_Metadata.hMetadata Tools:
CPI_Metadata cpiCreateMetadata( void ); CPI_Metadata cpiCloneMetadata( const CPI_Metadata meta ); CPI_Bool cpiMergeMetadata( CPI_Metadata result, const CPI_Metadata meta ); void cpiClearMetadata( CPI_Metadata meta ); void cpiDeleteMetadata( CPI_Metadata meta );Add Metadata Items:
Integer
CPI_Bool cpiSetMetaInt32( CPI_Metadata meta, const char *name, int value );Float
CPI_Bool cpiSetMetaFloat32( CPI_Metadata meta, const char *name, float value );String
CPI_Bool cpiSetMetaString( CPI_Metadata meta, const char *name, char *value );Retrieve Metadata Items:
Integer
CPI_Bool cpiGetMetaInt32( const CPI_Metadata meta, const char *name, int *result );Float
CPI_Bool cpiGetMetaFloat32( const CPI_Metadata meta, const char *name, float *result );String
CPI_Bool cpiGetMetaString( const CPI_Metadata meta, const char *name, char *result, int buffersize );There are many others - most of those are concerned with supporting new file formats. See the header file or Appendix C.