Using RAYZ Library Operations

A Simple Example

RAYZ allows the plugin programmer access to its own internal image manipulation routines (IMs), such as scaling, brightness, over, etc. The purpose of this is to allow the programmer to concentrate on the parts of the plugin which are unique, and avoid reinventing base functions.

The full list of internal operations, and the parameters which they take, can be found in the file CPI_InternalOps.h. This is reproduced in Appendix C - Internal Ops Parameter Reference.

For example, say you want to build a node which does camera shake. A simple way to do this is to generate the XY offsets for the image, and then use the RAYZ Transform utility to do the actual work. The code for that is given in example/Nodes/EarthQuake.C, and the relevant parts of that plugin are shown below, with the library operations shown in bold.

static CPI_ImageOp
QuakeExec( CPI_PrivateData   /*handle*/,
           CPI_Float32       time,
           CPI_Uint8         quality,
           CPI_Uint32        output,
           CPI_Bool          viewerOutput,
           CPI_Float32       scaleX,
           CPI_Float32       scaleY )
{
    CPI_ImageOp         retval = NULL;
    CPI_ImageOp         myInput;
    CPI_Float32         maxZrot, maxX, maxY, speed;
    CPI_Float32         xoff, yoff, roll, px, py;
    CPI_Int32           sizeX, sizeY;
    CPI_Int32           seed;

    // get the input image
    myInput = cpiGetInputOp( 0, time, quality, scaleX, scaleY );

    // get the parameters from the user
    if ( cpiGetFloat( &maxZrot, "roll", time ) &&
         cpiGetFloat( &maxX, "shift.x", time ) &&
         cpiGetFloat( &maxY, "shift.y", time ) &&
         cpiGetFloat( &speed, "speed", time ) &&
         cpiGetInteger( &seed, "seed", time ) )
    {
        CPI_Uint32  realSeed  = (CPI_Uint32)seed;

        // compute the transform values
        roll = maxZrot * newrand( realSeed ) * speed;
        xoff = maxX * newrand( realSeed ) * speed;
        yoff = maxY * newrand( realSeed ) * speed;

        // get the input size - pivot and translates need
        // to be given in pixel values
        if ( cpiGetInputFullSize( &sizeX, &sizeY, 0, time ) )
        {
            // set up the parameters for the internal Image Op.
            // Parameter names for internal ops are defined in
            // Appendix C
            CPI_PrivateData  opData;
            transformParms  *parms  = NULL;

            opData  = cpiCreatePrivateData( "transform" );
            parms   = (transformParms *)opData;

            px = CPI_Float32(sizeX) / 2.0F;
            py = CPI_Float32(sizeY) / 2.0F;

            if ( NULL != parms )
            {
                parms->order            = RTS;
                parms->translation[0]   = xoff;
                parms->translation[1]   = yoff;
                parms->rotateZ          = roll;
                parms->pivot[0]         = px;
                parms->pivot[1]         = py;
                parms->filterOp         = BEST_NATURAL;

    
                // add the 'transform' op to the list. This is an internal
                // Rayz image op
                if ( NULL != myInput )
                    retval = cpiAddImageOp( "transform",
                                            parms,
                                            &myInput,
                                            1 );
                else
                {
                    cpiError( "myInput is NULL" );
                    cpiDestroyPrivateData( opData );
                }

            }
            else
                cpiError( "Couldn't get transform private data" );
        }
        else
            cpiError( "Couldn't get full size from input A" );
    }
    else
        cpiError( "Couldn't get EarthQuake parameter(s)" );

    return retval;

}
In this case, there is no internal operation to define, because we are simply taking advantage of the RAYZ Image Op called "transform".

It is possible to use more than one Image Op in the same plugin, and / or to mix your own Image Ops with RAYZ internal ones. For example, your plugin may convert to log format first (using the RAYZ internal lintolog IM), then perform some custom manipulation, and finally convert back to linear (using the internal logtolin IM).

Handling Kernel-Based Operations

Also see the BlurXY.C source example. The BlurXY example shows something else, which is that in order to use internal IMs which are kernel-based operations, namely:
blur
dilate
edge
erode
you need to specify the Active Area that is being operated on. To do this, you define the size and offset of the result. Typically, this involves getting the input context and copying the size and offsets across, but you might do this different ways. In BlurXY, this is accomplished with the following lines:
    blurData->maskType  = (CPI_MaskType)action;
    blurData->size[0]   = inputContext.sizeX;
    blurData->size[1]   = inputContext.sizeY;
    blurData->offset[0] = inputContext.offsetX;
    blurData->offset[1] = inputContext.offsetY;

    if ( NULL != myInputs[0] )
        retval = cpiAddImageOp( "blur", blurData, myInputs, 2 );

Building Internal Exec Lists

The previous examples are ways of building a node using RAYZ building blocks, and they assume that the node is going to be executed in the conventional way, where one or more images come in, and then the resulting image(s) pop out the other side. However, there are other models available to the programmer.

Another technique is to build up a list of image operations, which can be RAYZ internal ops or custom written ones, or a combination of both, and then cause that list to be executed on a command from the user. In other words, a callback can be set up that results in a separate list of image operations being executed, with the result coming back to the node to be used in some way.

The node Stats.C shows an example of this. It takes an input image and allows the users to click on an overlay button which causes a simple analysis of the image. The results of that analysis are dumped back into the node panel. There are a few interesting functions to look at. The first is computeStats(), which creates a temporary internal exec list, and then executes it. This function is the callback for an overlay button - when that button is pushed, computeStats() is executed (Overlays are discussed in the next chapter, but understanding exactly how they are set up is not necessary to follow this example). The fuction looks like this:

static void
computeStats( const char        *nodename,
              const char        *parmname,
              CPI_Float32        time,
              CPI_PrivateData    data )
{
    CPI_ImageOp lastOp  = NULL;

    lastOp = cpiCreateNewExecList();    // initialize the list

    if ( NULL != lastOp )
    {
        // create a data structure to pass the results around in
        CPI_PrivateData  opData = cpiCreatePrivateData( "stats" );

        // add an image op to the list. This could be more than one
        // op, but in this case it is just a copy of the image op we
        // have previously defined.
        lastOp = cpiAddImageOp( "stats", opData, &lastOp, 1 );

        // kick off the execution of the list, and then call setStats
        // when finished, to use the results
        cpiStartExecution( setStats, NULL, lastOp );
    }
    else
        printf("lastOp is NULL!\n" );
}
And it is set up when the overlay is defined, like this:
static CPI_Bool
StatInitOverlay( void )
{
    cpiStartToolbar();
    cpiAddButton( "compute",
                  "Compute Values",
                  computeStats,
                  CPI_PARM_NO_LABEL,
                  NULL,
                  NULL );
    cpiEndToolbar( "statbar", "Stats", CPI_WINDOW_BOTTOM, true, false );

    return CPI_TRUE;
}
The second function is setStats(), which takes the resulting data and updates the node panel. In general, there is a function call which can be defined which is a function to call after the execution of an execlist has finished. This has the following signature
static void
(*CPI_ExecCallBackFn)( CPI_Image         *result,
                       CPI_PrivateData    callerData,
                       CPI_PrivateData    dataList[],
                       CPI_Uint32         numOfData,
                       CPI_Float32        time )
setStats() is defined as follows:
static void
setStats( CPI_Image         *result,
          CPI_PrivateData    callerData,
          CPI_PrivateData    dataList[],
          CPI_Uint32         numOfData,
          CPI_Float32        time )
{
    if ( numOfData == 1 )
    {
        statState   *state  = (statState *)dataList[0];

        if ( NULL != state )
        {
            cpiSetFloat( state->min[0], "minimum.red", time, CPI_FALSE );
            cpiSetFloat( state->min[1], "minimum.green", time, CPI_FALSE );
            cpiSetFloat( state->min[2], "minimum.blue", time, CPI_FALSE );
            cpiSetFloat( state->min[3], "minimum.alpha", time, CPI_FALSE );
            cpiSetFloat( state->max[0], "maximum.red", time, CPI_FALSE );
            cpiSetFloat( state->max[1], "maximum.green", time, CPI_FALSE );
            cpiSetFloat( state->max[2], "maximum.blue", time, CPI_FALSE );
            cpiSetFloat( state->max[3], "maximum.alpha", time, CPI_FALSE );
            cpiSetFloat( state->avg[0], "average.red", time, CPI_FALSE );
            cpiSetFloat( state->avg[1], "average.green", time, CPI_FALSE );
            cpiSetFloat( state->avg[2], "average.blue", time, CPI_FALSE );
            cpiSetFloat( state->avg[3], "average.alpha", time, CPI_FALSE );
        }
        else
            printf( "Incorrect private data!" );
    }
    else
        printf( "I only expected to get one piece of private data!" );
}
Note that setStats() is defined as part of the overlay code. For more information, see the section on Node Overlays.
[Previous Page] [Next Page]
[Table of Contents] [Index]

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