The Render Action

// look up a pixel in the image, does bounds checking to see if it is in the image rectangle
inline OfxRGBAColourB *
pixelAddress(OfxRGBAColourB *img, OfxRectI rect, int x, int y, int bytesPerLine)
{  
  if(x < rect.x1 || x >= rect.x2 || y < rect.y1 || y > rect.y2)
    return 0;
  OfxRGBAColourB *pix = (OfxRGBAColourB *) (((char *) img) + (y - rect.y1) * bytesPerLine);
  pix += x - rect.x1;  
  return pix;
}

// the process code  that the host sees
static OfxStatus render(OfxImageEffectHandle  instance,
                        OfxPropertySetHandle inArgs,
                        OfxPropertySetHandle outArgs)
{
    // get the render window and the time from the inArgs
    OfxTime time;
    OfxRectI renderWindow;
  
    gPropertySuite->propGetDouble(inArgs, kOfxPropTime, 0, &time);
    gPropertySuite->propGetIntN(inArgs, kOfxImageEffectPropRenderWindow, 4, &renderWindow.x1);

    // fetch output clip
    OfxImageClipHandle outputClip;
    gEffectSuite->clipGetHandle(instance, "Output", &outputClip, 0);

    // fetch image to render into from that clip
    OfxPropertySetHandle outputImg;
    gEffectSuite->clipGetImage(outputClip, time, NULL, &outputImg);

    // fetch output image info from that handle
    int dstRowBytes, dstBitDepth;
    OfxRectI dstRect;
    void *dstPtr;
    gPropertySuite->propGetInt(outputImg, kOfxImagePropRowBytes, 0, &dstRowBytes);
    gPropertySuite->propGetIntN(outputImg, kOfxImagePropBounds, 4, &dstRect.x1);
    gPropertySuite->propGetPointer(outputImg, kOfxImagePropData, 0, &dstPtr);
  
    // fetch main input clip
    OfxImageClipHandle sourceClip;
    gEffectSuite->clipGetHandle(instance, "Source", &sourceClip, 0);

    // fetch image at render time from that clip
    OfxPropertySetHandle sourceImg;
    gEffectSuite->clipGetImage(sourceClip, time, NULL, &sourceImg);

    // fetch image info out of that handle
    int srcRowBytes, srcBitDepth;
    OfxRectI srcRect;
    void *srcPtr;
    gPropertySuite->propGetInt(sourceImg, kOfxImagePropRowBytes, 0, &srcRowBytes);
    gPropertySuite->propGetIntN(sourceImg, kOfxImagePropBounds, 4, &srcRect.x1);
    gPropertySuite->propGetPointer(sourceImg, kOfxImagePropData, 0, &srcPtr);

    // cast data pointers to 8 bit RGBA
    OfxRGBAColourB *src = (OfxRGBAColourB *) srcPtr;
    OfxRGBAColourB *dst = (OfxRGBAColourB *) dstPtr;

    // and do some inverting
    for(int y = renderWindow.y1; y < renderWindow.y2; y++) {
        if(gEffectSuite->abort(instance)) break;

        OfxRGBAColourB *dstPix = pixelAddress(dst, dstRect, renderWindow.x1, y, dstRowBytes);

        for(int x = renderWindow.x1; x < renderWindow.x2; x++) {
        
            OfxRGBAColourB *srcPix = pixelAddress(src, srcRect, x, y, srcRowBytes);

            if(srcPix) {
                dstPix->r = 255 - srcPix->r;
                dstPix->g = 255 - srcPix->g;
                dstPix->b = 255 - srcPix->b;
                dstPix->a = 255 - srcPix->a;
            }
            else {
                dstPix->r = 0;
                dstPix->g = 0;
                dstPix->b = 0;
                dstPix->a = 0;
            }
            dstPix++;
        }
    }

    // we are finished with the source images so release them
    gEffectSuite->clipReleaseImage(sourceImg);
    gEffectSuite->clipReleaseImage(outputImg);
  
    // if we aborted, then we have failed to produce and image, so say so
    if(gEffectSuite->abort(instance)) return kOfxStatFailed;

    // otherwise all was well
    return kOfxStatOK;
}

The render action is where a plug-in turns its input images into output images. The first thing to note is that the effect is no longer a descriptor by an. For our simple example, this makes not much difference, however if the plug-in is maintaining private data, it is very important.

The first thing the render function does is to extract two properties from the inArgs property set. These are the time to render at, and the window to render over. Note that the render window is a 4 dimensional integer property.

Next the function fetches the output clip. A clip is a handle that is accessible by name and represents a sequences of images. The returned handle is valid for the lifetime of the instance, and so does not need releasing or deleting. Next an image is extracted from the clip. An image, unlike a clip, does need to be released when the plug-in is done with it.

Images are encapsulated as a property set, and so the ordinary property mechanism is used to fetch out information from the image. In this case three properties are needed, the rowBytes , or the number of bytes in a scan line (as there may be padding at the end), the image bounds, being the region where there is image data and a pointer to the image data. The source image is fetched in a similar way as the destination image.

Next the void * data pointers are cast to OfxRGBAColourB pointers, and we start iterating over the render window filling in output pixels. The render window must always be equal to or less than the bounds on the destination image. The inline function pixelAddress shows how pixel arithmetic is performed. Note the abort function from the image effect suite being called at every scan line to see if the rendering should be halted.

Finally, once we are done, the images are released and we return.