#include <cstdio>
#include <cstring>
#include <cstdarg>
#ifdef __APPLE__
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
#include <stdexcept>
#include <new>
#include "ofxImageEffect.h"
#include "ofxMemory.h"
#include "ofxMultiThread.h"
#include "ofxOpenGLRender.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
OfxHost *gHost;
OfxImageEffectSuiteV1 *gEffectHost = 0;
OfxPropertySuiteV1 *gPropHost = 0;
OfxParameterSuiteV1 *gParamHost = 0;
OfxMemorySuiteV1 *gMemoryHost = 0;
OfxMultiThreadSuiteV1 *gThreadHost = 0;
OfxMessageSuiteV1 *gMessageSuite = 0;
OfxInteractSuiteV1 *gInteractHost = 0;
OfxImageEffectOpenGLRenderSuiteV1 *gOpenGLSuite = 0;
int gHostSupportsMultipleBitDepths = false;
int gHostSupportsOpenGL = false;
#define DPRINT(args) print_dbg args
void print_dbg(const char *fmt, ...)
{
char msg[1024];
va_list ap;
va_start(ap, fmt);
vsnprintf(msg, 1023, fmt, ap);
fwrite(msg, sizeof(char), strlen(msg), stderr);
fflush(stderr);
#ifdef _WIN32
OutputDebugString(msg);
#endif
va_end(ap);
}
struct MyInstanceData {
bool isGeneralEffect;
OfxImageClipHandle sourceClip;
OfxImageClipHandle outputClip;
OfxParamHandle scaleParam;
OfxParamHandle sourceScaleParam;
};
static MyInstanceData *
getMyInstanceData( OfxImageEffectHandle effect)
{
OfxPropertySetHandle effectProps;
gEffectHost->getPropertySet(effect, &effectProps);
MyInstanceData *myData = 0;
gPropHost->propGetPointer(effectProps, kOfxPropInstanceData, 0,
(void **) &myData);
return myData;
}
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, "scale", &myData->scaleParam, 0);
gParamHost->paramGetHandle(paramSet, "source_scale", &myData->sourceScaleParam, 0);
gEffectHost->clipGetHandle(effect, kOfxImageEffectSimpleSourceClipName, &myData->sourceClip, 0);
gEffectHost->clipGetHandle(effect, kOfxImageEffectOutputClipName, &myData->outputClip, 0);
gPropHost->propSetPointer(effectProps, kOfxPropInstanceData, 0, (void *) myData);
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);
(void)myData;
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);
return kOfxStatOK;
}
static OfxStatus
isIdentity( OfxImageEffectHandle ,
OfxPropertySetHandle ,
OfxPropertySetHandle )
{
return kOfxStatReplyDefault;
}
static OfxStatus
instanceChanged( OfxImageEffectHandle ,
OfxPropertySetHandle ,
OfxPropertySetHandle )
{
return kOfxStatReplyDefault;
}
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;
int gl_enabled = 0;
int source_texture_index = -1, source_texture_target = -1;
int output_texture_index = -1, output_texture_target = -1;
char *tmps;
DPRINT(("render: openGLSuite %s\n", gOpenGLSuite ? "found" : "not found"));
if (gOpenGLSuite) {
gPropHost->propGetInt(inArgs, kOfxImageEffectPropOpenGLEnabled, 0, &gl_enabled);
DPRINT(("render: openGL rendering %s\n", gl_enabled ? "enabled" : "DISABLED"));
}
DPRINT(("Render: window = [%d, %d - %d, %d]\n",
renderWindow.x1, renderWindow.y1,
renderWindow.x2, renderWindow.y2));
if (!gl_enabled) {
return kOfxStatErrImageFormat;
}
status = gOpenGLSuite->clipLoadTexture(myData->outputClip, time, NULL, NULL, &outputImg);
DPRINT(("openGL: clipLoadTexture (output) returns status %d\n", status));
if (status != kOfxStatOK) {
return status;
}
status = gPropHost->propGetInt(outputImg, kOfxImageEffectPropOpenGLTextureIndex,
0, &output_texture_index);
if (status != kOfxStatOK) {
return status;
}
status = gPropHost->propGetInt(outputImg, kOfxImageEffectPropOpenGLTextureTarget,
0, &output_texture_target);
if (status != kOfxStatOK) {
return status;
}
status = gPropHost->propGetString(outputImg, kOfxImageEffectPropPixelDepth, 0, &tmps);
if (status != kOfxStatOK) {
return status;
}
DPRINT(("openGL: output texture index %d, target %d, depth %s\n",
output_texture_index, output_texture_target, tmps));
status = gOpenGLSuite->clipLoadTexture(myData->sourceClip, time, NULL, NULL, &sourceImg);
DPRINT(("openGL: clipLoadTexture (source) returns status %d\n", status));
if (status != kOfxStatOK) {
return status;
}
status = gPropHost->propGetInt(sourceImg, kOfxImageEffectPropOpenGLTextureIndex,
0, &source_texture_index);
if (status != kOfxStatOK) {
return status;
}
status = gPropHost->propGetInt(sourceImg, kOfxImageEffectPropOpenGLTextureTarget,
0, &source_texture_target);
if (status != kOfxStatOK) {
return status;
}
status = gPropHost->propGetString(sourceImg, kOfxImageEffectPropPixelDepth, 0, &tmps);
if (status != kOfxStatOK) {
return status;
}
DPRINT(("openGL: source texture index %d, target %d, depth %s\n",
source_texture_index, source_texture_target, tmps));
double scale = 1;
double source_scale = 1;
gParamHost->paramGetValueAtTime(myData->scaleParam, time, &scale);
gParamHost->paramGetValueAtTime(myData->sourceScaleParam, time, &source_scale);
float w = (renderWindow.x2 - renderWindow.x1);
float h = (renderWindow.y2 - renderWindow.y1);
glPushAttrib(GL_ALL_ATTRIB_BITS);
glDisable(GL_BLEND);
glBegin(GL_QUADS);
glColor4f(0, 0, 0, 1); glVertex2f(0, 0);
glVertex2f(0, h);
glVertex2f(w, h);
glVertex2f(w, 0);
glEnd();
glEnable(source_texture_target);
glBindTexture(source_texture_target, source_texture_index);
glTexParameteri(source_texture_target, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(source_texture_target, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(source_texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(source_texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
float tymin = 0;
float tymax = 1;
glBegin(GL_QUADS);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glBegin (GL_QUADS);
glTexCoord2f (0, tymin);
glVertex2f (0, 0);
glTexCoord2f (1.0, tymin);
glVertex2f (w * source_scale, 0);
glTexCoord2f (1.0, tymax);
glVertex2f (w * source_scale, h * source_scale);
glTexCoord2f (0, tymax);
glVertex2f (0, h * source_scale);
glEnd ();
glDisable(source_texture_target);
#define WIDTH 200
#define HEIGHT 100
glBegin(GL_QUADS);
glColor3f(1.0f, 0, 0); glVertex2f(10, 10);
glVertex2f(10, HEIGHT * scale);
glVertex2f(WIDTH * scale, HEIGHT * scale);
glVertex2f(WIDTH * scale, 10);
glEnd();
glPopAttrib();
if(sourceImg)
gOpenGLSuite->clipFreeTexture(sourceImg);
if(outputImg)
gOpenGLSuite->clipFreeTexture(outputImg);
return status;
}
static void
defineParam( OfxParamSetHandle effectParams,
const char *name,
const char *label,
const char *scriptName,
const char *hint,
const char *parent)
{
OfxPropertySetHandle props;
gParamHost->paramDefine(effectParams, kOfxParamTypeDouble, name, &props);
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, kOfxParamPropIncrement, 0, 0.01);
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);
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);
OfxParamSetHandle paramSet;
gEffectHost->getParamSet(effect, ¶mSet);
defineParam(paramSet, "scale", "scale", "scale",
"Scales the red rect", 0);
defineParam(paramSet, "source_scale", "source_scale", "source_scale",
"Scales the source image", 0);
gParamHost->paramDefine(paramSet, kOfxParamTypePage, "Main", &props);
gPropHost->propSetString(props, kOfxParamPropPageChild, 0, "scale");
gPropHost->propSetString(props, kOfxParamPropPageChild, 1, "source_scale");
return kOfxStatOK;
}
static OfxStatus
describe(OfxImageEffectHandle effect)
{
OfxStatus stat;
if((stat = ofxuFetchHostSuites()) != kOfxStatOK)
return stat;
gOpenGLSuite =
(OfxImageEffectOpenGLRenderSuiteV1 *)gHost->fetchSuite(gHost->host, kOfxOpenGLRenderSuite, 1);
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 OpenGL Example");
gPropHost->propSetString(effectProps, kOfxImageEffectPluginPropGrouping, 0, "OFX Example");
gPropHost->propSetString(effectProps, kOfxImageEffectPropSupportedContexts, 0, kOfxImageEffectContextFilter);
gPropHost->propSetString(effectProps, kOfxImageEffectPropSupportedContexts, 1, kOfxImageEffectContextGeneral);
gPropHost->propSetString(effectProps, kOfxImageEffectPropOpenGLRenderSupported, 0, "true");
{
char *s = NULL;
stat = gPropHost->propGetString(gHost->host, kOfxImageEffectPropOpenGLRenderSupported, 0, &s);
DPRINT(("Host has OpenGL render support: %s (stat=%d)\n", s, stat));
gHostSupportsOpenGL = stat == 0 && !strcmp(s, "true");
}
gPropHost->propSetString(effectProps, kOfxOpenGLPropPixelDepth, 0, kOfxBitDepthByte);
gPropHost->propSetString(effectProps, kOfxOpenGLPropPixelDepth, 1, kOfxBitDepthShort);
gPropHost->propSetString(effectProps, kOfxOpenGLPropPixelDepth, 2, kOfxBitDepthFloat);
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,
"com.genarts:OpenGLSamplePlugin",
1,
0,
setHostFunc,
pluginMain
};
EXPORT OfxPlugin *
OfxGetPlugin(int nth)
{
if(nth == 0)
return &basicPlugin;
return 0;
}
EXPORT int
OfxGetNumberOfPlugins(void)
{
return 1;
}