#ifdef WIN32
#include <windows.h>
#endif
#include <cstring>
#include <cstdio>
#ifdef __APPLE__
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
#include <cmath>
#include <stdexcept>
#include <new>
#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
#define kPointParam "point"
OfxHost *gHost;
OfxImageEffectSuiteV1 *gEffectHost = 0;
OfxPropertySuiteV1 *gPropHost = 0;
OfxParameterSuiteV1 *gParamHost = 0;
OfxMemorySuiteV1 *gMemoryHost = 0;
OfxMultiThreadSuiteV1 *gThreadHost = 0;
OfxMessageSuiteV1 *gMessageSuite = 0;
OfxInteractSuiteV1 *gInteractHost = 0;
static OfxStatus
parseCustomParam(char *str, double &x, double &y)
{
if(str && *str) {
int nRead = sscanf(str, "%lg %lg", &x, &y);
if(nRead != 2)
return kOfxStatErrFormat; }
else
return kOfxStatErrFormat; return kOfxStatOK;
}
void
writeCustomParam(char *str, int strlen, double x, double y)
{
#ifdef WIN32
_snprintf(str, strlen, "%lg %lg", x, y);
#else
snprintf(str, strlen, "%lg %lg", x, y);
#endif
}
static OfxStatus
getCustomParam(OfxImageEffectHandle pluginInstance, double &x, double &y)
{
OfxParamSetHandle paramSet;
gEffectHost->getParamSet(pluginInstance, ¶mSet);
OfxParamHandle param;
gParamHost->paramGetHandle(paramSet, kPointParam, ¶m, NULL);
char *str = 0;
gParamHost->paramGetValue(param, &str);
return parseCustomParam(str, x, y);
}
static OfxStatus
setCustomParam(OfxImageEffectHandle pluginInstance, double x, double y)
{
char str[1024];
writeCustomParam(str, 1024, x, y);
OfxParamSetHandle paramSet;
gEffectHost->getParamSet(pluginInstance, ¶mSet);
OfxParamHandle param;
gParamHost->paramGetHandle(paramSet, kPointParam, ¶m, NULL);
return gParamHost->paramSetValue(param, str);
}
static OfxStatus
customParamInterpFunction(OfxImageEffectHandle ,
OfxPropertySetHandle inArgs,
OfxPropertySetHandle outArgs)
{
double x0, y0, x1, y1;
OfxStatus err;
char *fromValue, *toValue;
gPropHost->propGetString(inArgs, kOfxParamPropCustomValue, 0, &fromValue);
if((err = parseCustomParam(fromValue, x0, y0)) != kOfxStatOK)
return err;
gPropHost->propGetString(inArgs, kOfxParamPropCustomValue, 1, &toValue);
if((err = parseCustomParam(toValue, x1, y1)) != kOfxStatOK)
return err;
double interp;
gPropHost->propGetDouble(inArgs, kOfxParamPropInterpolationAmount, 0, &interp);
double x = x0 + (x1 - x0) * interp;
double y = y0 + (y1 - y0) * interp;
char str[1024];
writeCustomParam(str, 1024, x, y);
gPropHost->propSetString(outArgs, kOfxParamPropCustomValue, 0, str);
return kOfxStatOK;
}
static OfxStatus
createInstance(OfxImageEffectHandle )
{
return kOfxStatOK;
}
static OfxStatus
destroyInstance(OfxImageEffectHandle )
{
return kOfxStatOK;
}
static OfxStatus
isIdentity(OfxImageEffectHandle ,
OfxPropertySetHandle ,
OfxPropertySetHandle outArgs)
{
gPropHost->propSetString(outArgs, kOfxPropName, 0, kOfxImageEffectSimpleSourceClipName);
return kOfxStatOK;
}
static OfxStatus render(OfxImageEffectHandle ,
OfxPropertySetHandle ,
OfxPropertySetHandle )
{
return kOfxStatOK;
}
struct MyInteractData {
bool selected;
OfxParamHandle pointParam;
explicit MyInteractData(OfxParamHandle pParam)
: selected(false)
, pointParam(pParam)
{
}
};
static MyInteractData *
getInteractData(OfxInteractHandle interactInstance)
{
void *dataV = ofxuGetInteractInstanceData(interactInstance);
return (MyInteractData *) dataV;
}
static OfxStatus
interactDescribe(OfxInteractHandle )
{
return kOfxStatOK;
}
static OfxStatus
interactCreateInstance(OfxImageEffectHandle pluginInstance,
OfxInteractHandle interactInstance)
{
OfxParamSetHandle paramSet;
gEffectHost->getParamSet(pluginInstance, ¶mSet);
OfxParamHandle pointParam;
gParamHost->paramGetHandle(paramSet, kPointParam, &pointParam, 0);
MyInteractData *data = new MyInteractData(pointParam);
ofxuSetInteractInstanceData(interactInstance, (void *) data);
OfxPropertySetHandle interactProps;
gInteractHost->interactGetPropertySet(interactInstance, &interactProps);
gPropHost->propSetString(interactProps, kOfxInteractPropSlaveToParam, 0, kPointParam);
return kOfxStatOK;
}
static OfxStatus
interactDestroyInstance(OfxImageEffectHandle ,
OfxInteractHandle interactInstance)
{
MyInteractData *data = getInteractData(interactInstance);
delete data;
return kOfxStatOK;
}
#define kXHairSize 10
static OfxStatus
interactDraw(OfxImageEffectHandle pluginInstance,
OfxInteractHandle interactInstance,
OfxPropertySetHandle drawArgs)
{
OfxStatus err;
MyInteractData *data = getInteractData(interactInstance);
OfxPointD projSize, projOffset;
ofxuGetProjectSetup(pluginInstance, projSize, projOffset);
double pixelScale[2];
ofxuGetInteractPixelScale(drawArgs, pixelScale);
double x, y;
if((err = getCustomParam(pluginInstance, x, y)) != kOfxStatOK)
return err;
x = projOffset.x + x * projSize.x;
y = projOffset.y + y * projSize.y;
float dx = kXHairSize * pixelScale[0];
float dy = kXHairSize * pixelScale[1];
if(data->selected)
glColor3f(1, 1, 1);
else
glColor3f(1, 0, 0);
glPushMatrix();
glTranslated(x, y, 0);
glBegin(GL_LINES);
glVertex2f(-dx, 0);
glVertex2f(dx, 0);
glVertex2f(0, -dy);
glVertex2f(0, dy);
glEnd();
glPopMatrix();
return kOfxStatOK;
}
static OfxStatus
interactPenMotion(OfxImageEffectHandle pluginInstance,
OfxInteractHandle interactInstance,
OfxPropertySetHandle inArgs)
{
MyInteractData *data = getInteractData(interactInstance);
if(data->selected) {
OfxPointD projSize, projOffset;
ofxuGetProjectSetup(pluginInstance, projSize, projOffset);
OfxPointD penPos;
gPropHost->propGetDoubleN(inArgs, kOfxInteractPropPenPosition, 2, &penPos.x);
penPos.x = (penPos.x - projOffset.x)/projSize.x;
penPos.y = (penPos.y - projOffset.y)/projSize.y;
setCustomParam(pluginInstance, penPos.x, penPos.y);
return kOfxStatOK;
}
return kOfxStatReplyDefault;
}
static OfxStatus
interactPenDown(OfxImageEffectHandle pluginInstance,
OfxInteractHandle interactInstance,
OfxPropertySetHandle inArgs)
{
MyInteractData *data = getInteractData(interactInstance);
OfxPointD projSize, projOffset;
ofxuGetProjectSetup(pluginInstance, projSize, projOffset);
double x, y;
OfxStatus stat = getCustomParam(pluginInstance, x, y);
if (stat != kOfxStatOK) {
return stat;
}
x = projOffset.x + x * projSize.x;
y = projOffset.y + y * projSize.y;
double pixelScale[2];
ofxuGetInteractPixelScale(inArgs, pixelScale);
double penPos[2];
gPropHost->propGetDoubleN(inArgs, kOfxInteractPropPenPosition, 2, penPos);
if(fabs(x - penPos[0]) < 5 * pixelScale[0] && fabs(y - penPos[1]) < 5 * pixelScale[1]) {
data->selected = true;
return kOfxStatOK;
}
return kOfxStatReplyDefault;
}
static OfxStatus
interactPenUp(OfxImageEffectHandle ,
OfxInteractHandle interactInstance,
OfxPropertySetHandle )
{
MyInteractData *data = getInteractData(interactInstance);
if(data->selected) {
data->selected = false;
return kOfxStatOK;
}
return kOfxStatReplyDefault;
}
static OfxStatus
overlayMain(const char *action, const void *handle, OfxPropertySetHandle inArgs, OfxPropertySetHandle )
{
OfxInteractHandle interact = (OfxInteractHandle ) handle;
OfxPropertySetHandle props;
gInteractHost->interactGetPropertySet(interact, &props);
if(strcmp(action, kOfxActionDescribe) == 0) {
return interactDescribe(interact);
}
else {
OfxImageEffectHandle pluginInstance;
gPropHost->propGetPointer(props, kOfxPropEffectInstance, 0, (void **) &pluginInstance);
if(strcmp(action, kOfxActionCreateInstance) == 0) {
return interactCreateInstance(pluginInstance, interact);
}
else if(strcmp(action, kOfxActionDestroyInstance) == 0) {
return interactDestroyInstance(pluginInstance, interact);
}
else if(strcmp(action, kOfxInteractActionDraw) == 0) {
return interactDraw(pluginInstance, interact, inArgs);
}
else if(strcmp(action, kOfxInteractActionPenMotion) == 0) {
return interactPenMotion(pluginInstance, interact, inArgs);
}
else if(strcmp(action, kOfxInteractActionPenDown) == 0) {
return interactPenDown(pluginInstance, interact, inArgs);
}
else if(strcmp(action, kOfxInteractActionPenUp) == 0) {
return interactPenUp(pluginInstance, interact, inArgs);
}
return kOfxStatReplyDefault;
}
}
static OfxStatus
describe(OfxImageEffectHandle effect)
{
OfxStatus stat;
if((stat = ofxuFetchHostSuites()) != kOfxStatOK)
return stat;
int supportsOverlays;
gPropHost->propGetInt(gHost->host, kOfxImageEffectPropSupportsOverlays, 0, &supportsOverlays);
if(supportsOverlays == 0)
return kOfxStatErrMissingHostFeature;
OfxPropertySetHandle effectProps;
gEffectHost->getPropertySet(effect, &effectProps);
gPropHost->propSetString(effectProps, kOfxImageEffectPropSupportedPixelDepths, 0, kOfxBitDepthByte);
gPropHost->propSetString(effectProps, kOfxImageEffectPropSupportedPixelDepths, 1, kOfxBitDepthShort);
gPropHost->propSetString(effectProps, kOfxImageEffectPropSupportedPixelDepths, 2, kOfxBitDepthFloat);
gPropHost->propSetString(effectProps, kOfxPropLabel, 0, "OFX Custom Param Example");
gPropHost->propSetString(effectProps, kOfxImageEffectPluginPropGrouping, 0, "OFX Example");
gPropHost->propSetString(effectProps, kOfxImageEffectPropSupportedContexts, 0, kOfxImageEffectContextFilter);
gPropHost->propSetPointer(effectProps, kOfxImageEffectPluginPropOverlayInteractV1, 0, (void *) overlayMain);
return kOfxStatOK;
}
static OfxStatus
describeInContext(OfxImageEffectHandle effect, OfxPropertySetHandle )
{
OfxPropertySetHandle props;
gEffectHost->clipDefine(effect, kOfxImageEffectSimpleSourceClipName, &props);
gPropHost->propSetString(props, kOfxImageEffectPropSupportedComponents, 0, kOfxImageComponentRGBA);
gPropHost->propSetString(props, kOfxImageEffectPropSupportedComponents, 1, kOfxImageComponentAlpha);
gEffectHost->clipDefine(effect, kOfxImageEffectOutputClipName, &props);
gPropHost->propSetString(props, kOfxImageEffectPropSupportedComponents, 0, kOfxImageComponentRGBA);
gPropHost->propSetString(props, kOfxImageEffectPropSupportedComponents, 1, kOfxImageComponentAlpha);
OfxParamSetHandle paramSet;
gEffectHost->getParamSet(effect, ¶mSet);
gParamHost->paramDefine(paramSet, kOfxParamTypeCustom, kPointParam, &props);
gPropHost->propSetString(props, kOfxParamPropDefault, 0, "0.5 0.5");
gPropHost->propSetString(props, kOfxParamPropScriptName, 0, "point");
gPropHost->propSetString(props, kOfxParamPropHint, 0, "Custom point attached to overlay crosshair");
gPropHost->propSetString(props, kOfxPropLabel, 0, "Point");
int supportsCustomAnim;
gPropHost->propGetInt(gHost->host, kOfxParamHostPropSupportsCustomAnimation, 0, &supportsCustomAnim);
if(supportsCustomAnim) {
gPropHost->propSetInt(props, kOfxParamPropAnimates, 0, true);
gPropHost->propSetPointer(props, kOfxParamPropCustomInterpCallbackV1, 0, (void *) customParamInterpFunction);
}
else {
gPropHost->propSetInt(props, kOfxParamPropAnimates, 0, false);
}
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, 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);
}
} 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.CustomParamPlugin",
1,
0,
setHostFunc,
pluginMain
};
EXPORT OfxPlugin *
OfxGetPlugin(int nth)
{
if(nth == 0)
return &basicPlugin;
return 0;
}
EXPORT int
OfxGetNumberOfPlugins(void)
{
return 1;
}