General Node Description

The Structure of a Node Plugin

In RAYZ, nodes are made of up to three parts:
  1. The image operation - defines the actual work. This is optional, as RAYZ internal IMs can be used instead or as well.
  2. The node interface - this defines the parameters to the user.
  3. The overlay - this is graphics, which may be interactive, drawn on the image itself. Typically these are used to provide visual parameter settings. The Crop node is an example of this. Overlays are optional.
The distinction between these is that the image operation (IM) is the actual arithmetic that creates or manipulates images, while the node is the interface presented to the user. These are separate because some aspects, such as scripting, do not require the node - they just use the operation. This means that when you build a RAYZ plugin, you are also creating a script command that can be used outside of RAYZ.

The Image Operation Structure

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.

The Node Info Structure

The node is built around a structure that must be present. Here is an example from the CircleRamp node:
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.


How a Plugin Executes

User-supplied routines are called in the following order, or in response to the given events:
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
    

How an Image is Defined

In RAYZ, an image is defined as a context and some data. The format for each of these is given in CPI_Types.h:
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;

Passing Parameters with Metadata

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.h

Metadata 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.

[Previous Page] [Next Page]
[Table of Contents] [Index]

Copyright © 2002 Silicon Grail Inc.
736 Seward Street, Hollywood, CA 90038