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).
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 );
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.