#include <stdexcept>
#include <new>
#include <cstring>
#include "ofxImageEffect.h"
#include "ofxMemory.h"
#include "ofxMultiThread.h"
#include "../include/ofxUtilities.H"
#if defined __APPLE__ || defined linux || defined __FreeBSD__
# define EXPORT __attribute__((visibility("default")))
#elif defined _WIN32
# define EXPORT OfxExport
#else
# error Not building on your operating system quite yet
#endif
template <class T> inline T Maximum(T a, T b) {return a > b ? a : b;}
template <class T> inline T Minimum(T a, T b) {return a < b ? a : b;}
OfxHost *gHost;
OfxImageEffectSuiteV1 *gEffectHost = 0;
OfxPropertySuiteV1 *gPropHost = 0;
OfxParameterSuiteV1 *gParamHost = 0;
OfxMemorySuiteV1 *gMemoryHost = 0;
OfxMultiThreadSuiteV1 *gThreadHost = 0;
OfxMessageSuiteV1 *gMessageSuite = 0;
OfxInteractSuiteV1 *gInteractHost = 0;
int gHostSupportsMultipleBitDepths = false;
struct MyInstanceData {
bool isGeneralEffect;
OfxImageClipHandle sourceClip;
OfxImageClipHandle maskClip;
OfxImageClipHandle outputClip;
OfxParamHandle scaleParam;
OfxParamHandle perComponentScaleParam;
OfxParamHandle scaleRParam;
OfxParamHandle scaleGParam;
OfxParamHandle scaleBParam;
OfxParamHandle scaleAParam;
};
static MyInstanceData *
getMyInstanceData( OfxImageEffectHandle effect)
{
OfxPropertySetHandle effectProps;
gEffectHost->getPropertySet(effect, &effectProps);
MyInstanceData *myData = 0;
gPropHost->propGetPointer(effectProps, kOfxPropInstanceData, 0,
(void **) &myData);
return myData;
}
static inline void
setParamEnabledness( OfxImageEffectHandle effect,
const char *paramName,
int enabledState)
{
OfxParamSetHandle paramSet;
gEffectHost->getParamSet(effect, ¶mSet);
OfxParamHandle param; OfxPropertySetHandle paramProps;
gParamHost->paramGetHandle(paramSet, paramName, ¶m, ¶mProps);
gPropHost->propSetInt(paramProps, kOfxParamPropEnabled, 0, enabledState);
}
static void
setPerComponentScaleEnabledness( OfxImageEffectHandle effect)
{
MyInstanceData *myData = getMyInstanceData(effect);
int perComponentScale;
gParamHost->paramGetValue(myData->perComponentScaleParam, &perComponentScale);
if(ofxuIsClipConnected(effect, kOfxImageEffectSimpleSourceClipName)) {
OfxPropertySetHandle props; gEffectHost->clipGetPropertySet(myData->sourceClip, &props);
char *pixelType;
gPropHost->propGetString(props, kOfxImageEffectPropComponents, 0, &pixelType);
perComponentScale = perComponentScale && !(strcmp(pixelType, kOfxImageComponentAlpha) == 0);
}
setParamEnabledness(effect, "scaleR", perComponentScale);
setParamEnabledness(effect, "scaleG", perComponentScale);
setParamEnabledness(effect, "scaleB", perComponentScale);
setParamEnabledness(effect, "scaleA", perComponentScale);
}
static OfxStatus
onLoad(void)
{
return kOfxStatOK;
}
static OfxStatus
onUnLoad(void)
{
return kOfxStatOK;
}
static OfxStatus
createInstance( OfxImageEffectHandle effect)
{
OfxPropertySetHandle effectProps;
gEffectHost->getPropertySet(effect, &effectProps);
OfxParamSetHandle paramSet;
gEffectHost->getParamSet(effect, ¶mSet);
MyInstanceData *myData = new MyInstanceData;
char *context = 0;
gPropHost->propGetString(effectProps, kOfxImageEffectPropContext, 0, &context);
myData->isGeneralEffect = context && (strcmp(context, kOfxImageEffectContextGeneral) == 0);
gParamHost->paramGetHandle(paramSet, "scaleComponents", &myData->perComponentScaleParam, 0);
gParamHost->paramGetHandle(paramSet, "scale", &myData->scaleParam, 0);
gParamHost->paramGetHandle(paramSet, "scaleR", &myData->scaleRParam, 0);
gParamHost->paramGetHandle(paramSet, "scaleG", &myData->scaleGParam, 0);
gParamHost->paramGetHandle(paramSet, "scaleB", &myData->scaleBParam, 0);
gParamHost->paramGetHandle(paramSet, "scaleA", &myData->scaleAParam, 0);
gEffectHost->clipGetHandle(effect, kOfxImageEffectSimpleSourceClipName, &myData->sourceClip, 0);
gEffectHost->clipGetHandle(effect, kOfxImageEffectOutputClipName, &myData->outputClip, 0);
if(myData->isGeneralEffect) {
gEffectHost->clipGetHandle(effect, "Mask", &myData->maskClip, 0);
}
else
myData->maskClip = 0;
gPropHost->propSetPointer(effectProps, kOfxPropInstanceData, 0, (void *) myData);
setPerComponentScaleEnabledness(effect);
return kOfxStatOK;
}
static OfxStatus
destroyInstance( OfxImageEffectHandle effect)
{
MyInstanceData *myData = getMyInstanceData(effect);
if(myData)
delete myData;
return kOfxStatOK;
}
OfxStatus
getSpatialRoD( OfxImageEffectHandle effect, OfxPropertySetHandle inArgs, OfxPropertySetHandle outArgs)
{
MyInstanceData *myData = getMyInstanceData(effect);
OfxTime time;
gPropHost->propGetDouble(inArgs, kOfxPropTime, 0, &time);
OfxRectD rod;
gEffectHost->clipGetRegionOfDefinition(myData->sourceClip, time, &rod);
gPropHost->propSetDoubleN(outArgs, kOfxImageEffectPropRegionOfDefinition, 4, &rod.x1);
return kOfxStatOK;
}
OfxStatus
getSpatialRoI( OfxImageEffectHandle effect, OfxPropertySetHandle inArgs, OfxPropertySetHandle outArgs)
{
OfxRectD roi;
gPropHost->propGetDoubleN(inArgs, kOfxImageEffectPropRegionOfInterest, 4, &roi.x1);
gPropHost->propSetDoubleN(outArgs, "OfxImageClipPropRoI_Source", 4, &roi.x1);
MyInstanceData *myData = getMyInstanceData(effect);
if(myData->isGeneralEffect && ofxuIsClipConnected(effect, "Mask")) {
gPropHost->propSetDoubleN(outArgs, "OfxImageClipPropRoI_Mask", 4, &roi.x1);
}
return kOfxStatOK;
}
OfxStatus
getTemporalDomain( OfxImageEffectHandle effect, OfxPropertySetHandle , OfxPropertySetHandle outArgs)
{
MyInstanceData *myData = getMyInstanceData(effect);
double sourceRange[2];
OfxPropertySetHandle props; gEffectHost->clipGetPropertySet(myData->sourceClip, &props);
gPropHost->propGetDoubleN(props, kOfxImageEffectPropFrameRange, 2, sourceRange);
gPropHost->propSetDoubleN(outArgs, kOfxImageEffectPropFrameRange, 2, sourceRange);
return kOfxStatOK;
}
static OfxStatus
getClipPreferences( OfxImageEffectHandle effect, OfxPropertySetHandle , OfxPropertySetHandle outArgs)
{
MyInstanceData *myData = getMyInstanceData(effect);
int bitDepth;
bool isRGBA;
ofxuClipGetFormat(myData->sourceClip, bitDepth, isRGBA, true);
const char *bitDepthStr = bitDepth == 8 ? kOfxBitDepthByte : (bitDepth == 16 ? kOfxBitDepthShort : kOfxBitDepthFloat);
const char *componentStr = isRGBA ? kOfxImageComponentRGBA : kOfxImageComponentAlpha;
gPropHost->propSetString(outArgs, "OfxImageClipPropComponents_Output", 0, componentStr);
if(gHostSupportsMultipleBitDepths)
gPropHost->propSetString(outArgs, "OfxImageClipPropDepth_Output", 0, bitDepthStr);
if(myData->isGeneralEffect) {
if(ofxuIsClipConnected(effect, "Mask")) {
gPropHost->propSetString(outArgs, "OfxImageClipPropComponents_Mask", 0, kOfxImageComponentAlpha);
if(gHostSupportsMultipleBitDepths)
gPropHost->propSetString(outArgs, "OfxImageClipPropDepth_Mask", 0, bitDepthStr);
}
}
return kOfxStatOK;
}
static OfxStatus
isIdentity( OfxImageEffectHandle effect,
OfxPropertySetHandle inArgs,
OfxPropertySetHandle outArgs)
{
OfxTime time;
OfxRectI renderWindow;
gPropHost->propGetDouble(inArgs, kOfxPropTime, 0, &time);
gPropHost->propGetIntN(inArgs, kOfxImageEffectPropRenderWindow, 4, &renderWindow.x1);
MyInstanceData *myData = getMyInstanceData(effect);
double scaleValue, sR = 1, sG = 1, sB = 1, sA = 1;
gParamHost->paramGetValueAtTime(myData->scaleParam, time, &scaleValue);
if(ofxuGetClipPixelsAreRGBA(myData->sourceClip)) {
gParamHost->paramGetValueAtTime(myData->scaleRParam, time, &sR);
gParamHost->paramGetValueAtTime(myData->scaleGParam, time, &sG);
gParamHost->paramGetValueAtTime(myData->scaleBParam, time, &sB);
gParamHost->paramGetValueAtTime(myData->scaleAParam, time, &sA);
}
if(scaleValue == 1.0 && sR==1 && sG == 1 && sB == 1 && sA == 1) {
gPropHost->propSetString(outArgs, kOfxPropName, 0, kOfxImageEffectSimpleSourceClipName);
return kOfxStatOK;
}
return kOfxStatReplyDefault;
}
static OfxStatus
instanceChanged( OfxImageEffectHandle effect,
OfxPropertySetHandle inArgs,
OfxPropertySetHandle )
{
char *changeReason;
gPropHost->propGetString(inArgs, kOfxPropChangeReason, 0, &changeReason);
if(strcmp(changeReason, kOfxChangeUserEdited) != 0) return kOfxStatReplyDefault;
char *typeChanged;
gPropHost->propGetString(inArgs, kOfxPropType, 0, &typeChanged);
bool isClip = strcmp(typeChanged, kOfxTypeClip) == 0;
bool isParam = strcmp(typeChanged, kOfxTypeParameter) == 0;
char *objChanged;
gPropHost->propGetString(inArgs, kOfxPropName, 0, &objChanged);
if((isClip && strcmp(objChanged, kOfxImageEffectSimpleSourceClipName) == 0) ||
(isParam && strcmp(objChanged, "scaleComponents") == 0)) {
setPerComponentScaleEnabledness(effect);
return kOfxStatOK;
}
return kOfxStatReplyDefault;
}
template <class T> inline T
Clamp(T v, int min, int max)
{
if(v < T(min)) return T(min);
if(v > T(max)) return T(max);
return v;
}
template <class PIX> inline PIX *
pixelAddress(PIX *img, OfxRectI rect, int x, int y, int bytesPerLine)
{
if(x < rect.x1 || x >= rect.x2 || y < rect.y1 || y >= rect.y2 || !img)
return 0;
PIX *pix = (PIX *) (((char *) img) + (y - rect.y1) * bytesPerLine);
pix += x - rect.x1;
return pix;
}
class Processor {
protected :
OfxImageEffectHandle instance;
float rScale, gScale, bScale, aScale;
void *srcV, *dstV, *maskV;
OfxRectI srcRect, dstRect, maskRect;
int srcBytesPerLine, dstBytesPerLine, maskBytesPerLine;
OfxRectI window;
public :
Processor(OfxImageEffectHandle inst,
float rScal, float gScal, float bScal, float aScal,
void *src, OfxRectI sRect, int sBytesPerLine,
void *dst, OfxRectI dRect, int dBytesPerLine,
void *mask, OfxRectI mRect, int mBytesPerLine,
OfxRectI win)
: instance(inst)
, rScale(rScal)
, gScale(gScal)
, bScale(bScal)
, aScale(aScal)
, srcV(src)
, dstV(dst)
, maskV(mask)
, srcRect(sRect)
, dstRect(dRect)
, maskRect(mRect)
, srcBytesPerLine(sBytesPerLine)
, dstBytesPerLine(dBytesPerLine)
, maskBytesPerLine(mBytesPerLine)
, window(win)
{}
static void multiThreadProcessing(unsigned int threadId, unsigned int nThreads, void *arg);
virtual void doProcessing(OfxRectI window) = 0;
void process(void);
};
void
Processor::multiThreadProcessing(unsigned int threadId, unsigned int nThreads, void *arg)
{
Processor *proc = (Processor *) arg;
unsigned int dy = proc->window.y2 - proc->window.y1;
unsigned int y1 = proc->window.y1 + threadId * dy/nThreads;
unsigned int y2 = proc->window.y1 + Minimum((threadId + 1) * dy/nThreads, dy);
OfxRectI win = proc->window;
win.y1 = y1; win.y2 = y2;
proc->doProcessing(win);
}
void
Processor::process(void)
{
unsigned int nThreads;
gThreadHost->multiThreadNumCPUs(&nThreads);
gThreadHost->multiThread(multiThreadProcessing, nThreads, (void *) this);
}
template <class PIX, class MASK, int max, int isFloat>
class ProcessRGBA : public Processor{
public :
ProcessRGBA(OfxImageEffectHandle instance,
float rScale, float gScale, float bScale, float aScale,
void *srcV, OfxRectI srcRect, int srcBytesPerLine,
void *dstV, OfxRectI dstRect, int dstBytesPerLine,
void *maskV, OfxRectI maskRect, int maskBytesPerLine,
OfxRectI window)
: Processor(instance,
rScale, gScale, bScale, aScale,
srcV, srcRect, srcBytesPerLine,
dstV, dstRect, dstBytesPerLine,
maskV, maskRect, maskBytesPerLine,
window)
{
}
void doProcessing(OfxRectI procWindow)
{
PIX *src = (PIX *) srcV;
PIX *dst = (PIX *) dstV;
MASK *mask = (MASK *) maskV;
for(int y = procWindow.y1; y < procWindow.y2; y++) {
if(gEffectHost->abort(instance)) break;
PIX *dstPix = pixelAddress(dst, dstRect, procWindow.x1, y, dstBytesPerLine);
for(int x = procWindow.x1; x < procWindow.x2; x++) {
PIX *srcPix = pixelAddress(src, srcRect, x, y, srcBytesPerLine);
float maskV = 1.0f;
if(mask) {
MASK *maskPix = pixelAddress(mask, maskRect, x, y, maskBytesPerLine);
if(maskPix) {
maskV = float(*maskPix)/float(max);
}
else
maskV = 0.0f;
maskPix++;
}
float sR = 1.0 + (rScale - 1.0) * maskV;
float sG = 1.0 + (gScale - 1.0) * maskV;
float sB = 1.0 + (bScale - 1.0) * maskV;
float sA = 1.0 + (aScale - 1.0) * maskV;
if(srcPix) {
if(isFloat) {
dstPix->r = srcPix->r * sR;
dstPix->g = srcPix->g * sG;
dstPix->b = srcPix->b * sB;
dstPix->a = srcPix->a * sA;
}
else {
dstPix->r = Clamp(int(srcPix->r * sR), 0, max);
dstPix->g = Clamp(int(srcPix->g * sG), 0, max);
dstPix->b = Clamp(int(srcPix->b * sB), 0, max);
dstPix->a = Clamp(int(srcPix->a * sA), 0, max);
}
srcPix++;
}
else {
dstPix->r = dstPix->g = dstPix->b = dstPix->a= 0;
}
dstPix++;
}
}
}
};
template <class PIX, class MASK, int max, int isFloat>
class ProcessAlpha : public Processor {
public :
ProcessAlpha( OfxImageEffectHandle instance,
float scale,
void *srcV, OfxRectI srcRect, int srcBytesPerLine,
void *dstV, OfxRectI dstRect, int dstBytesPerLine,
void *maskV, OfxRectI maskRect, int maskBytesPerLine,
OfxRectI window)
: Processor(instance,
scale, scale, scale, scale,
srcV, srcRect, srcBytesPerLine,
dstV, dstRect, dstBytesPerLine,
maskV, maskRect, maskBytesPerLine,
window)
{
}
void doProcessing(OfxRectI procWindow)
{
PIX *src = (PIX *) srcV;
PIX *dst = (PIX *) dstV;
MASK *mask = (MASK *) maskV;
for(int y = procWindow.y1; y < procWindow.y2; y++) {
if(gEffectHost->abort(instance)) break;
PIX *dstPix = pixelAddress(dst, dstRect, procWindow.x1, y, dstBytesPerLine);
for(int x = procWindow.x1; x < procWindow.x2; x++) {
PIX *srcPix = pixelAddress(src, srcRect, x, y, srcBytesPerLine);
float maskV = 1.0f;
if(mask) {
MASK *maskPix = pixelAddress(mask, maskRect, x, y, maskBytesPerLine);
if(maskPix) {
maskV = float(*maskPix)/float(max);
}
}
float theScale = 1.0 + (rScale - 1.0) * maskV;
if(srcPix) {
if(isFloat) {
*dstPix = *srcPix * theScale;
}
else {
*dstPix = Clamp(int(*srcPix * theScale), 0, max);
}
srcPix++;
}
else {
*dstPix = 0;
}
dstPix++;
}
}
}
};
static OfxStatus render( OfxImageEffectHandle instance,
OfxPropertySetHandle inArgs,
OfxPropertySetHandle )
{
OfxTime time;
OfxRectI renderWindow;
OfxStatus status = kOfxStatOK;
gPropHost->propGetDouble(inArgs, kOfxPropTime, 0, &time);
gPropHost->propGetIntN(inArgs, kOfxImageEffectPropRenderWindow, 4, &renderWindow.x1);
MyInstanceData *myData = getMyInstanceData(instance);
OfxPropertySetHandle sourceImg = NULL, outputImg = NULL, maskImg = NULL;
int srcRowBytes, srcBitDepth, dstRowBytes, dstBitDepth, maskRowBytes = 0, maskBitDepth;
bool srcIsAlpha, dstIsAlpha, maskIsAlpha = false;
OfxRectI dstRect, srcRect, maskRect = {0};
void *src, *dst, *mask = NULL;
try {
sourceImg = ofxuGetImage(myData->sourceClip, time, srcRowBytes, srcBitDepth, srcIsAlpha, srcRect, src);
if(sourceImg == NULL) throw OfxuNoImageException();
outputImg = ofxuGetImage(myData->outputClip, time, dstRowBytes, dstBitDepth, dstIsAlpha, dstRect, dst);
if(outputImg == NULL) throw OfxuNoImageException();
if(myData->isGeneralEffect) {
if(ofxuIsClipConnected(instance, "Mask")) {
maskImg = ofxuGetImage(myData->maskClip, time, maskRowBytes, maskBitDepth, maskIsAlpha, maskRect, mask);
if(maskImg != NULL) {
if(!maskIsAlpha || maskBitDepth != srcBitDepth) {
throw OfxuStatusException(kOfxStatErrImageFormat);
}
}
}
}
if(srcBitDepth != dstBitDepth || srcIsAlpha != dstIsAlpha) {
throw OfxuStatusException(kOfxStatErrImageFormat);
}
int scaleComponents;
gParamHost->paramGetValueAtTime(myData->perComponentScaleParam, time, &scaleComponents);
double scale, rScale = 1, gScale = 1, bScale = 1, aScale = 1;
gParamHost->paramGetValueAtTime(myData->scaleParam, time, &scale);
if(scaleComponents) {
gParamHost->paramGetValueAtTime(myData->scaleRParam, time, &rScale);
gParamHost->paramGetValueAtTime(myData->scaleGParam, time, &gScale);
gParamHost->paramGetValueAtTime(myData->scaleBParam, time, &bScale);
gParamHost->paramGetValueAtTime(myData->scaleAParam, time, &aScale);
}
rScale *= scale; gScale *= scale; bScale *= scale; aScale *= scale;
if(!dstIsAlpha) {
switch(dstBitDepth) {
case 8 : {
ProcessRGBA<OfxRGBAColourB, unsigned char, 255, 0> fred(instance, rScale, gScale, bScale, aScale,
src, srcRect, srcRowBytes,
dst, dstRect, dstRowBytes,
mask, maskRect, maskRowBytes,
renderWindow);
fred.process();
}
break;
case 16 : {
ProcessRGBA<OfxRGBAColourS, unsigned short, 65535, 0> fred(instance, rScale, gScale, bScale, aScale,
src, srcRect, srcRowBytes,
dst, dstRect, dstRowBytes,
mask, maskRect, maskRowBytes,
renderWindow);
fred.process();
}
break;
case 32 : {
ProcessRGBA<OfxRGBAColourF, float, 1, 1> fred(instance, rScale, gScale, bScale, aScale,
src, srcRect, srcRowBytes,
dst, dstRect, dstRowBytes,
mask, maskRect, maskRowBytes,
renderWindow);
fred.process();
break;
}
}
}
else {
switch(dstBitDepth) {
case 8 : {
ProcessAlpha<unsigned char, unsigned char, 255, 0> fred(instance, scale,
src, srcRect, srcRowBytes,
dst, dstRect, dstRowBytes,
mask, maskRect, maskRowBytes,
renderWindow);
fred.process();
}
break;
case 16 : {
ProcessAlpha<unsigned short, unsigned short, 65535, 0> fred(instance, scale,
src, srcRect, srcRowBytes,
dst, dstRect, dstRowBytes,
mask, maskRect, maskRowBytes,
renderWindow);
fred.process();
}
break;
case 32 : {
ProcessAlpha<float, float, 1, 1> fred(instance, scale,
src, srcRect, srcRowBytes,
dst, dstRect, dstRowBytes,
mask, maskRect, maskRowBytes,
renderWindow);
fred.process();
}
break;
}
}
}
catch(OfxuNoImageException &ex) {
if(!gEffectHost->abort(instance)) {
status = kOfxStatFailed;
}
}
catch(OfxuStatusException &ex) {
status = ex.status();
}
if(maskImg)
gEffectHost->clipReleaseImage(maskImg);
if(sourceImg)
gEffectHost->clipReleaseImage(sourceImg);
if(outputImg)
gEffectHost->clipReleaseImage(outputImg);
return status;
}
static void
defineScaleParam( OfxParamSetHandle effectParams,
const char *name,
const char *label,
const char *scriptName,
const char *hint,
const char *parent)
{
OfxPropertySetHandle props;
OfxStatus stat;
stat = gParamHost->paramDefine(effectParams, kOfxParamTypeDouble, name, &props);
if (stat != kOfxStatOK) {
throw OfxuStatusException(stat);
}
gPropHost->propSetString(props, kOfxParamPropDoubleType, 0, kOfxParamDoubleTypeScale);
gPropHost->propSetDouble(props, kOfxParamPropDefault, 0, 1.0);
gPropHost->propSetDouble(props, kOfxParamPropMin, 0, 0.0);
gPropHost->propSetDouble(props, kOfxParamPropDisplayMin, 0, 0.0);
gPropHost->propSetDouble(props, kOfxParamPropDisplayMax, 0, 100.0);
gPropHost->propSetString(props, kOfxParamPropHint, 0, hint);
gPropHost->propSetString(props, kOfxParamPropScriptName, 0, scriptName);
gPropHost->propSetString(props, kOfxPropLabel, 0, label);
if(parent)
gPropHost->propSetString(props, kOfxParamPropParent, 0, parent);
}
static OfxStatus
describeInContext( OfxImageEffectHandle effect, OfxPropertySetHandle inArgs)
{
char *context;
gPropHost->propGetString(inArgs, kOfxImageEffectPropContext, 0, &context);
bool isGeneralContext = strcmp(context, kOfxImageEffectContextGeneral) == 0;
OfxPropertySetHandle props;
gEffectHost->clipDefine(effect, kOfxImageEffectOutputClipName, &props);
gPropHost->propSetString(props, kOfxImageEffectPropSupportedComponents, 0, kOfxImageComponentRGBA);
gPropHost->propSetString(props, kOfxImageEffectPropSupportedComponents, 1, kOfxImageComponentAlpha);
gEffectHost->clipDefine(effect, kOfxImageEffectSimpleSourceClipName, &props);
gPropHost->propSetString(props, kOfxImageEffectPropSupportedComponents, 0, kOfxImageComponentRGBA);
gPropHost->propSetString(props, kOfxImageEffectPropSupportedComponents, 1, kOfxImageComponentAlpha);
if(isGeneralContext) {
gEffectHost->clipDefine(effect, "Mask", &props);
gPropHost->propSetString(props, kOfxImageEffectPropSupportedComponents, 0, kOfxImageComponentAlpha);
gPropHost->propSetInt(props, kOfxImageClipPropOptional, 0, 1);
}
OfxParamSetHandle paramSet;
gEffectHost->getParamSet(effect, ¶mSet);
defineScaleParam(paramSet, "scale", "scale", "scale", "Scales all component in the image", 0);
gParamHost->paramDefine(paramSet, kOfxParamTypeBoolean, "scaleComponents", &props);
gPropHost->propSetInt(props, kOfxParamPropDefault, 0, 0);
gPropHost->propSetString(props, kOfxParamPropHint, 0, "Enables scales on individual components");
gPropHost->propSetString(props, kOfxParamPropScriptName, 0, "scaleComponents");
gPropHost->propSetString(props, kOfxPropLabel, 0, "Scale Individual Components");
gParamHost->paramDefine(paramSet, kOfxParamTypeGroup, "componentScales", &props);
gPropHost->propSetString(props, kOfxParamPropHint, 0, "Scales on the individual component");
gPropHost->propSetString(props, kOfxPropLabel, 0, "Components");
defineScaleParam(paramSet, "scaleR", "red", "scaleR",
"Scales the red component of the image", "componentScales");
defineScaleParam(paramSet, "scaleG", "green", "scaleG",
"Scales the green component of the image", "componentScales");
defineScaleParam(paramSet, "scaleB", "blue", "scaleB",
"Scales the blue component of the image", "componentScales");
defineScaleParam(paramSet, "scaleA", "alpha", "scaleA",
"Scales the alpha component of the image", "componentScales");
gParamHost->paramDefine(paramSet, kOfxParamTypePage, "Main", &props);
gPropHost->propSetString(props, kOfxParamPropPageChild, 0, "scale");
gPropHost->propSetString(props, kOfxParamPropPageChild, 1, "scaleComponents");
gPropHost->propSetString(props, kOfxParamPropPageChild, 2, "scaleR");
gPropHost->propSetString(props, kOfxParamPropPageChild, 3, "scaleG");
gPropHost->propSetString(props, kOfxParamPropPageChild, 4, "scaleB");
gPropHost->propSetString(props, kOfxParamPropPageChild, 5, "scaleA");
return kOfxStatOK;
}
static OfxStatus
describe(OfxImageEffectHandle effect)
{
OfxStatus stat;
if((stat = ofxuFetchHostSuites()) != kOfxStatOK)
return stat;
gPropHost->propGetInt(gHost->host, kOfxImageEffectPropSupportsMultipleClipDepths, 0, &gHostSupportsMultipleBitDepths);
OfxPropertySetHandle effectProps;
gEffectHost->getPropertySet(effect, &effectProps);
gPropHost->propSetInt(effectProps, kOfxImageEffectPluginPropFieldRenderTwiceAlways, 0, 0);
gPropHost->propSetInt(effectProps, kOfxImageEffectPropSupportsMultipleClipDepths, 0, 1);
gPropHost->propSetString(effectProps, kOfxImageEffectPropSupportedPixelDepths, 0, kOfxBitDepthByte);
gPropHost->propSetString(effectProps, kOfxImageEffectPropSupportedPixelDepths, 1, kOfxBitDepthShort);
gPropHost->propSetString(effectProps, kOfxImageEffectPropSupportedPixelDepths, 2, kOfxBitDepthFloat);
gPropHost->propSetString(effectProps, kOfxPropLabel, 0, "OFX Gain Example");
gPropHost->propSetString(effectProps, kOfxImageEffectPluginPropGrouping, 0, "OFX Example");
gPropHost->propSetString(effectProps, kOfxImageEffectPropSupportedContexts, 0, kOfxImageEffectContextFilter);
gPropHost->propSetString(effectProps, kOfxImageEffectPropSupportedContexts, 1, kOfxImageEffectContextGeneral);
return kOfxStatOK;
}
static OfxStatus
pluginMain(const char *action, const void *handle, OfxPropertySetHandle inArgs, OfxPropertySetHandle outArgs)
{
try {
OfxImageEffectHandle effect = (OfxImageEffectHandle) handle;
if(strcmp(action, kOfxActionDescribe) == 0) {
return describe(effect);
}
else if(strcmp(action, kOfxImageEffectActionDescribeInContext) == 0) {
return describeInContext(effect, inArgs);
}
else if(strcmp(action, kOfxActionLoad) == 0) {
return onLoad();
}
else if(strcmp(action, kOfxActionUnload) == 0) {
return onUnLoad();
}
else if(strcmp(action, kOfxActionCreateInstance) == 0) {
return createInstance(effect);
}
else if(strcmp(action, kOfxActionDestroyInstance) == 0) {
return destroyInstance(effect);
}
else if(strcmp(action, kOfxImageEffectActionIsIdentity) == 0) {
return isIdentity(effect, inArgs, outArgs);
}
else if(strcmp(action, kOfxImageEffectActionRender) == 0) {
return render(effect, inArgs, outArgs);
}
else if(strcmp(action, kOfxImageEffectActionGetRegionOfDefinition) == 0) {
return getSpatialRoD(effect, inArgs, outArgs);
}
else if(strcmp(action, kOfxImageEffectActionGetRegionsOfInterest) == 0) {
return getSpatialRoI(effect, inArgs, outArgs);
}
else if(strcmp(action, kOfxImageEffectActionGetClipPreferences) == 0) {
return getClipPreferences(effect, inArgs, outArgs);
}
else if(strcmp(action, kOfxActionInstanceChanged) == 0) {
return instanceChanged(effect, inArgs, outArgs);
}
else if(strcmp(action, kOfxImageEffectActionGetTimeDomain) == 0) {
return getTemporalDomain(effect, inArgs, outArgs);
}
} catch (std::bad_alloc) {
return kOfxStatErrMemory;
} catch ( const std::exception& e ) {
return kOfxStatErrUnknown;
} catch (int err) {
return err;
} catch ( ... ) {
return kOfxStatErrUnknown;
}
return kOfxStatReplyDefault;
}
static void
setHostFunc(OfxHost *hostStruct)
{
gHost = hostStruct;
}
static OfxPlugin basicPlugin =
{
kOfxImageEffectPluginApi,
1,
"uk.co.thefoundry.BasicGainPlugin",
1,
0,
setHostFunc,
pluginMain
};
EXPORT OfxPlugin *
OfxGetPlugin(int nth)
{
if(nth == 0)
return &basicPlugin;
return 0;
}
EXPORT int
OfxGetNumberOfPlugins(void)
{
return 1;
}