The Exec() function has a scaleX and scaleY parameter, and these values are used to tell the programmer what fraction of the input image size the result is to be. Recall that the Exec function signature looks like this:
static CPI_ImageOp SimpleExec( CPI_PrivateData handle, CPI_Float32 myTime, CPI_Uint8 quality, CPI_Uint32 nodeOutput, CPI_Bool viewerOutput, CPI_Float32 scaleX, CPI_Float32 scaleY ) {Assuming that the users set the "computation size" in the menu to Medium, then the Exec function will be called with scaleX = 0.5 and scaleY = 0.5 (Note that these are the default values for Medium and that the user can change them, via the Preferences, to be any other set of values). For "Fit", the scale factors will be whatever they need to be to keep the image fully in view.
The programmer may or may not need to take this into account. For source nodes, such as Grid or CircleRamp, this information is very important, since you want to generate the correct image for any given scale. For example, the Grid example uses these values in this way:
gridData->lineWidth[0] = cpiRound( linewid * scaleX ); gridData->lineWidth[1] = cpiRound( linewid * scaleY ); if ( gridData->lineWidth[0] < 1 ) gridData->lineWidth[0] = 1; if ( gridData->lineWidth[1] < 1 ) gridData->lineWidth[1] = 1; gridData->lineGap[0] = cpiRound( linegap * scaleX ); gridData->lineGap[1] = cpiRound( linegap * scaleY ); if ( gridData->lineGap[0] < 1 ) gridData->lineGap[0] = 1; if ( gridData->lineGap[1] < 1 ) gridData->lineGap[1] = 1;where gridData is a pointer to the private data structure which holds, among other things, information about the grid parameters. If this scaling were not done, the grid parameters would be too large for the image being created. In general, image parameters should be handled as relative to the size of the image, not as absolute values.
Note that this is not the same as the zoom in/out values. When you zoom in or out, the scaling of the image is handled after the given size is computed. So zooming out to 1/2 does not save any computation - the image is still computed at Full Size, then scaled down by RAYZ. However, asking for a Medium Size image does save computation, since now only half the image (in each dimension) is calculated.
However, even for nodes which take inputs, you should not assume that your plugin will only be handed images of a certain size. Nodes are given requests to execute at various sizes for various reasons. One reason would be if your plugin is executing on a multi-processor machine - in that case, each thread will call the Exec() function with a sub-image region of the whole incoming image.
Another reason for execution requests at odd sizes would be if the user wants to see a thumbnail on the plugin node. In that case, RAYZ does not simply scale down the full size output - it makes a specific request to the node to generate a thumbnail image (usually, but not always, 60 x 45 pixels). So your plugin needs to be generalized to handle any size requests that come in. That also means not assuming that the first line is line 0, or that the first pixel is pixel 0.
The best way to do this is to use the myContext.sizeX & Y and myContext.offsetX & Y values, and to use the function cpiGetLine() to access the appropriate pixel for processing. For example, this processing routine (taken from Simple.C), assumes nothing about the size or offset of the incoming image. Loops do not begin or end at magic numbers like 0, and the offset is not assumed to be 0. It also checks for a user Abort signal from the interface before processing each scanline.
templatestatic void simple( CPI_Image &input, CPI_Image *result, T max) { CPI_Int32 x, y, endX, endY, i, j = 0; CPI_Int32 chans = result->myContext.channels; T *pin, *pout; endX = result->myContext.offsetX + result->myContext.sizeX; endY = result->myContext.offsetY + result->myContext.sizeY; for ( y = result->myContext.offsetY; y < endY; y++ ) { // this detects the user requesting an early exit if ( cpiIsProcessingAborted() ) break; // Notice that we use cpiGetLine to avoid any sub-image issues // as rayz may ask us to process a small part of a larger image pin = (T *)cpiGetLine( &input, y ); pout = (T *)cpiGetLine( result, y ); j = 0; for ( x = result->myContext.offsetX; x < endX; x++ ) { // copy each channel for ( i = 0; i < chans; i++, j++ ) pout[j] = pin[j]; } } }