Handling Inputs and Outputs

Referencing Time

Nodes which take inputs get access to those input images with the call
CPI_ImageOp    cpiGetInputOp( CPI_Uint32   input,
                              CPI_Float32  theTime,
                              CPI_Uint8    quality,
                              CPI_Float32  scaleX,
                              CPI_Float32  scaleY )
The parameter 'theTime' is a value given in frames. The current time is passed into the Exec function, and normally this value is simply handed on to cpiGetInputOp(). However, you can request any time you like; for example, to do time filtering around the current frame, you could do this:
static CPI_ImageOp
Exec( CPI_Float32   time,
      CPI_Uint8     quality,
      CPI_Uint32    output,
      CPI_Bool      viewerOutput,
      CPI_Float32   scaleX,
      CPI_Float32   scaleY )
{
    CPI_ImageOp    inputs[3], retval;

    inputs[0] = cpiGetImageOp( 0, time - 1.0F, ... );
    inputs[1] = cpiGetImageOp( 0, time, ... );
    inputs[2] = cpiGetImageOp( 0, time + 1.0F, ... );

	... // set up metadata into myParms

    retval = cpiAddImageOp( "mytimeaverage", myParms, inputs, 3 );

	... // remove metadata and other processing
}
Currently, although the time is a floating point (fractional) frame number, only integer (whole) frames are returned. So time values are rounded to the nearest whole frame and that is the frame which is returned. For example, requesting frame 1.4 will return the image at frame 1; a request for frame 1.5 will return the image at frame 2.

Multiple Inputs

The number of inputs a node has is specified by the RPI_NodeInfo structure. The relevant entries are shown in bold:
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
}

Required Inputs

The example shows a node with no inputs, which would be a source node such as ColorBars or (in this case) CircleRamp. To specify that the node is to have 3 inputs, you would replace the 0 entries above with 3s, as in
static RPI_NodeInfo ninfo = {
    ...
    3,
    3,
    1,
    1,
    CPI_FALSE
}

Optional Inputs

When the maximum number of inputs is greater than the minimum, the inputs in the range are considered optional. For example, a node which took an optional garbage matte, and also an optional holdout matte, would declare its inputs like this:
static RPI_NodeInfo ninfo = {
    ...
    1,
    3,
    1,
    1,
    CPI_FALSE
}
Which means 1 required input and up to 3 total inputs.

Variable Numbers of Inputs

A node may have a variable number of inputs - the RAYZ MultiComp node is an example of this. With a variable number of inputs, each time the user connects up an input, the node will "grow" another. To set this, simply set the "growable" flag to CPI_TRUE, as in:
static RPI_NodeInfo ninfo = {
    ...
    1,
    1,
    1,
    1,
    CPI_TRUE
}
When the "growable" flag is set to true, the value for the maximum number of inputs is ignored by RAYZ. However, the minimum value may still be used to enforce a minumum number of inputs. The node will be drawn with 'minimum' connectors, plus one more, which is the "growable" one. When that extra one is connected, another will grow. RAYZ will generate a user error for nodes which do not have their minimum input count all connected.

For example, a node which is specified like this:

static RPI_NodeInfo ninfo = {
    ...
    3,
    3,
    1,
    1,
    CPI_TRUE
}
will be drawn with 4 inputs, and until the first 3 are connected, RAYZ will show a user error with a message that says "Input image is not connected."

Labeling inputs

To label the input connectors for the user, the programmer should define the InputLabel routine. The routine takes as input the numbe of the input to label (starting at 0) and returns a string which is the label.

For example, this routine defines labels for a 2-input node. The labels are printed in the status line, when the user mouses over the connector. The connectors themselves are labeled with the first letter of the label string.

const char *
SimpleInputLabel( CPI_Uint32    input)
{
    static const char   input1[] = {"foreground image"};
    static const char   input2[] = {"background image"};

    if ( input == 0 )
        return input1;
    else
        return input2;
}
The resulting node would look like this:      

Outputs

To talk about the output(s) of a node, we have to define some terms. When we say output, we mean the physical output connector that the user drags a line from/to.

Then each output usually has several output viewers, or views. These are called views because they may only be (in many cases) viewed in an Image Viewer. They cannot be piped downstream to another node.

By default, a node has 1 output. Also by default, it has 1 viewer for each input, plus one for the output. Those viewers allow the user to see each of the node's inputs, plus (of course) the result of the node's operations.

To see this, drop a simple filter node, such as Blur, onto the RAYZ worksheet. Then go to the top line of the Image Viewer, and look at the menu labeled "Source". This allows the user to choose the view to be displayed in the viewer. By default, this is set to "Output", which is the result of the node's processing. But notice that it can be set to "Input Image". For a multi-input node, such as Ultimatte PFG, each connected input results in another entry in the Source menu.

Adding more outputs

To add more output connectors to your node, which allow separate output images to emerge from the node, you only need to modify the values in the RPI_NodeInfo structure. For example, to specify a node with 2 outputs, the structure would change as follows (changes shown in bold):
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
    2,                            // min outputs
    2,                            // max outputs
    CPI_FALSE                     // can add more inputs
}
(In fact, in RAYZ 1.0, the 'max outputs' value is ignored, but is there for future expansion)

To label these outputs for the user, you define a node output label function, which is analogous to the one for input labels (see above). This routine takes an output number (starting from 0) and returns a string which labels it. This label is printed in the status line when the user places the mouse over the output connector.

The plugin programmer knows which output is being requested from the value being passed into the GetRegion and Exec routines. The plugin ChannelSplit.C shows how this works, and should be used as a reference for doing this.

Adding more output viewers

It is possible to add more viewers to a node - these would correspond to different ways of seeing what the node is doing. For example, consider a node which filters out dust and sparkle. You might like to allow the user to see a matte of where the node thinks the dust/sparkle pixels are, before she goes further with the processing. To do this, you would need to implement the NodeViewerOutputs and ViewerLabel functions, as shown:

// the number returned is the number of extra viewer outputs your
// are making available to the user. There are at least 2 by default
// already (see above).

CPI_Uint32
NewNodeViewerOut( CPI_Uint32 nodeOutput )
{
	return 1;
}


// return a label for each of the extra viewers you have added.
// You cannot relabel the default viewers, just the one(s) you have
// added.

const char *
NewNodeViewerLabel( CPI_Uint32   nodeOutput,
                    CPI_Uint32   whichViewer )
{
    static const char   view1[] = {"sparkle matte"};

    // assuming only one output, 1 extra viewer (which is number 0)
	if ( whichViewer == 0 )
		return view1;
}


// note these routines are declared in the structure

static RPI_NodeInfo ninfo = {
    {
        "newnode",             // node name
        "New Node",            // menu name
        "Silicon Grail",       // author
        "v0.1"                 // version
        "",                    // help URL
        ""                     // icon name
    },
    NewNodeInit,               // initialize node
    NewNodeShutDown,           // shut down node
    NULL,                      // parameter changed
    NULL,                      // input changed
    NewNodeGetRegion,          // return result region
    NewNodeGetFullSize,        // return full size
    NewNodeGetRange,           // return frame range
    NULL,                      // return errors
    NewNodeExec,               // node execute
    NULL,                      // input label
    NULL,                      // output label
    NewNodeViewerLabel,        // viewer label
    NewNodeViewerOut,          // viewer outputs
    0,                         // min inputs
    0,                         // max inputs
    2,                         // min outputs
    2,                         // max outputs
    CPI_FALSE                  // can add more inputs
}

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

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