#include <cstring>
#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 kMessageNotEnoughBits "MessageIDNotEnoughBits"
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;}
static bool gSupportsBytes = false;
static bool gSupportsShorts = false;
static bool gSupportsFloats = false;
static int gDepthParamToBytes[3];
OfxHost *gHost;
OfxImageEffectSuiteV1 *gEffectHost = 0;
OfxPropertySuiteV1 *gPropHost = 0;
OfxParameterSuiteV1 *gParamHost = 0;
OfxMemorySuiteV1 *gMemoryHost = 0;
OfxMultiThreadSuiteV1 *gThreadHost = 0;
OfxMessageSuiteV1 *gMessageSuite = 0;
OfxInteractSuiteV1 *gInteractHost = 0;
struct MyInstanceData {
OfxImageClipHandle sourceClip;
OfxImageClipHandle outputClip;
OfxParamHandle depthParam;
};
static MyInstanceData *
getMyInstanceData(OfxImageEffectHandle effect)
{
MyInstanceData *myData = (MyInstanceData *) ofxuGetEffectInstanceData(effect);
return myData;
}
static OfxStatus
createInstance(OfxImageEffectHandle effect)
{
OfxPropertySetHandle effectProps;
gEffectHost->getPropertySet(effect, &effectProps);
OfxParamSetHandle paramSet;
gEffectHost->getParamSet(effect, ¶mSet);
MyInstanceData *myData = new MyInstanceData;
gParamHost->paramGetHandle(paramSet, "depth", &myData->depthParam, 0);
gEffectHost->clipGetHandle(effect, "Source", &myData->sourceClip, 0);
gEffectHost->clipGetHandle(effect, "Output", &myData->outputClip, 0);
ofxuSetEffectInstanceData(effect, (void *) myData);
return kOfxStatOK;
}
static OfxStatus
destroyInstance(OfxImageEffectHandle effect)
{
MyInstanceData *myData = getMyInstanceData(effect);
if(myData)
delete myData;
return kOfxStatOK;
}
static OfxStatus
isIdentity(OfxImageEffectHandle effect,
OfxPropertySetHandle ,
OfxPropertySetHandle outArgs)
{
MyInstanceData *myData = getMyInstanceData(effect);
int srcDepth = ofxuGetClipPixelDepth(myData->sourceClip);
int dstDepth = ofxuGetClipPixelDepth(myData->outputClip);
if(srcDepth == dstDepth) {
gPropHost->propSetString(outArgs, kOfxPropName, 0, "Source");
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)
return 0;
PIX *pix = (PIX *) (((char *) img) + (y - rect.y1) * bytesPerLine);
pix += x - rect.x1;
return pix;
}
class Processor {
protected :
OfxImageEffectHandle instance;
int nComponents;
void *srcV, *dstV;
OfxRectI srcRect, dstRect;
int srcBytesPerLine, dstBytesPerLine;
OfxRectI window;
public :
Processor(const Processor &p)
: instance(p.instance)
, nComponents(p.nComponents)
, srcV(p.srcV)
, dstV(p.dstV)
, srcRect(p.srcRect)
, dstRect(p.dstRect)
, srcBytesPerLine(p.srcBytesPerLine)
, dstBytesPerLine(p.dstBytesPerLine)
, window(p.window)
{}
Processor(OfxImageEffectHandle inst, int nComps,
void *src, OfxRectI sRect, int sBytesPerLine,
void *dst, OfxRectI dRect, int dBytesPerLine,
OfxRectI win)
: instance(inst)
, nComponents(nComps)
, srcV(src)
, dstV(dst)
, srcRect(sRect)
, dstRect(dRect)
, srcBytesPerLine(sBytesPerLine)
, dstBytesPerLine(dBytesPerLine)
, window(win)
{}
static void multiThreadProcessing(unsigned int threadId, unsigned int nThreads, void *arg);
virtual void doProcessing(OfxRectI window);
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);
}
void
Processor::doProcessing(OfxRectI )
{
}
template <class SRCPIX, int kSrcMax, int srcIsFloat,
class DSTPIX, int kDstMax, int dstIsFloat>
class ProcessPix : public Processor {
public :
ProcessPix(const Processor &p)
: Processor(p)
{
process();
}
void doProcessing(OfxRectI procWindow)
{
SRCPIX *src = (SRCPIX *) srcV;
DSTPIX *dst = (DSTPIX *) dstV;
float scaleF;
scaleF = float(kDstMax)/float(kSrcMax);
for(int y = procWindow.y1; y < procWindow.y2; y++) {
if(gEffectHost->abort(instance)) break;
DSTPIX *dstPix = pixelAddress(dst, dstRect, procWindow.x1, y, dstBytesPerLine);
for(int x = procWindow.x1; x < procWindow.x2; x++) {
SRCPIX *srcPix = pixelAddress(src, srcRect, x, y, srcBytesPerLine);
if(srcPix) {
for(int c = 0; c < nComponents; c++) {
if(dstIsFloat)
dstPix[c] = srcPix[c] * scaleF;
else if (srcIsFloat)
dstPix[c] = Clamp(srcPix[c] * scaleF, 0, kDstMax);
else
dstPix[c] = Clamp(srcPix[c] * kDstMax/kSrcMax, 0, kDstMax);
}
srcPix += nComponents;
}
else {
for(int c = 0; c < nComponents; c++)
dstPix[c] = 0;
}
dstPix += nComponents;
}
}
}
};
static OfxStatus render(OfxImageEffectHandle effect,
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(effect);
OfxPropertySetHandle sourceImg = NULL, outputImg = NULL;
int srcRowBytes, srcBitDepth, dstRowBytes, dstBitDepth;
bool srcIsAlpha, dstIsAlpha;
OfxRectI dstRect, srcRect;
void *src, *dst;
try {
outputImg = ofxuGetImage(myData->outputClip, time, dstRowBytes, dstBitDepth, dstIsAlpha, dstRect, dst);
if(outputImg == NULL) throw OfxuNoImageException();
sourceImg = ofxuGetImage(myData->sourceClip, time, srcRowBytes, srcBitDepth, srcIsAlpha, srcRect, src);
if(sourceImg == NULL) throw OfxuNoImageException();
int nComponents = dstIsAlpha ? 1 : 4;
Processor proc(effect, nComponents,
src, srcRect, srcRowBytes,
dst, dstRect, dstRowBytes,
renderWindow);
switch(dstBitDepth) {
case 8 : {
switch(srcBitDepth) {
case 8 : {ProcessPix<unsigned char, 255, 0, unsigned char, 255, 0> pixProc(proc); break;}
case 16 : {ProcessPix<unsigned short, 65535, 0, unsigned char, 255, 0> pixProc(proc); break;}
case 32 : {ProcessPix<float, 1, 1, unsigned char, 255, 0> pixProc(proc); break;}
}
}
break;
case 16 : {
switch(srcBitDepth) {
case 8 : {ProcessPix<unsigned char, 255, 0, unsigned short, 65535, 0> pixProc(proc); break;}
case 16 : {ProcessPix<unsigned short, 65535, 0, unsigned short, 65535, 0> pixProc(proc); break;}
case 32 : {ProcessPix<float, 1, 1, unsigned short, 65535, 0> pixProc(proc); break;}
}
}
break;
case 32 : {
switch(srcBitDepth) {
case 8 : {ProcessPix<unsigned char, 255, 0, float, 1, 1> pixProc(proc); break;}
case 16 : {ProcessPix<unsigned short, 65535, 0, float, 1, 1> pixProc(proc); break;}
case 32 : {ProcessPix<float, 1, 1, float, 1, 1> pixProc(proc); break;}
}
}
break;
}
}
catch(OfxuNoImageException &ex) {
if(!gEffectHost->abort(effect)) {
status = kOfxStatFailed;
}
}
if(sourceImg)
gEffectHost->clipReleaseImage(sourceImg);
if(outputImg)
gEffectHost->clipReleaseImage(outputImg);
return status;
}
static OfxStatus
getClipPreferences(OfxImageEffectHandle effect,
OfxPropertySetHandle ,
OfxPropertySetHandle outArgs)
{
MyInstanceData *myData = getMyInstanceData(effect);
int depthVal;
gParamHost->paramGetValue(myData->depthParam, &depthVal);
switch(gDepthParamToBytes[depthVal]) {
case 8 : gPropHost->propSetString(outArgs, "OfxImageClipPropDepth_Output", 0, kOfxBitDepthByte); break;
case 16 : gPropHost->propSetString(outArgs, "OfxImageClipPropDepth_Output", 0, kOfxBitDepthShort); break;
case 32 : gPropHost->propSetString(outArgs, "OfxImageClipPropDepth_Output", 0, kOfxBitDepthFloat); break;
}
return kOfxStatOK;
}
static OfxStatus
describeInContext(OfxImageEffectHandle effect, OfxPropertySetHandle inArgs)
{
char *context;
gPropHost->propGetString(inArgs, kOfxImageEffectPropContext, 0, &context);
OfxPropertySetHandle clipProps;
gEffectHost->clipDefine(effect, "Output", &clipProps);
gPropHost->propSetString(clipProps, kOfxImageEffectPropSupportedComponents, 0, kOfxImageComponentRGBA);
gPropHost->propSetString(clipProps, kOfxImageEffectPropSupportedComponents, 1, kOfxImageComponentAlpha);
gPropHost->propSetString(clipProps, kOfxImageClipPropFieldExtraction, 0, kOfxImageFieldSingle);
gEffectHost->clipDefine(effect, "Source", &clipProps);
gPropHost->propSetString(clipProps, kOfxImageEffectPropSupportedComponents, 0, kOfxImageComponentRGBA);
gPropHost->propSetString(clipProps, kOfxImageEffectPropSupportedComponents, 1, kOfxImageComponentAlpha);
gPropHost->propSetString(clipProps, kOfxImageClipPropFieldExtraction, 0, kOfxImageFieldSingle);
OfxParamSetHandle paramSet;
gEffectHost->getParamSet(effect, ¶mSet);
OfxPropertySetHandle paramProps;
gParamHost->paramDefine(paramSet, kOfxParamTypeChoice, "depth", ¶mProps);
gPropHost->propSetString(paramProps, kOfxParamPropHint, 0, "What pixel depth to convert the image to");
gPropHost->propSetString(paramProps, kOfxPropLabel, 0, "Depth");
int i = 0;
if(gSupportsBytes) gPropHost->propSetString(paramProps, kOfxParamPropChoiceOption, i++, "Byte");
if(gSupportsShorts) gPropHost->propSetString(paramProps, kOfxParamPropChoiceOption, i++, "Short");
if(gSupportsFloats) gPropHost->propSetString(paramProps, kOfxParamPropChoiceOption, i++, "Float");
gPropHost->propSetInt(paramProps, kOfxParamPropDefault, 0, 0);
OfxPropertySetHandle effectProps;
gEffectHost->getPropertySet(effect, &effectProps);
gPropHost->propSetString(effectProps, kOfxImageEffectPropClipPreferencesSlaveParam, 0, "depth");
return kOfxStatOK;
}
static OfxStatus
describe(OfxImageEffectHandle effect)
{
OfxStatus stat;
if((stat = ofxuFetchHostSuites()) != kOfxStatOK)
return stat;
int hostSupportsMultipleDepths;
gPropHost->propGetInt(gHost->host, kOfxImageEffectPropSupportsMultipleClipDepths, 0, &hostSupportsMultipleDepths);
int nHostDepths;
gPropHost->propGetDimension(gHost->host, kOfxImageEffectPropSupportedPixelDepths, &nHostDepths);
if(!hostSupportsMultipleDepths || nHostDepths == 1) {
}
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);
int i;
for(i = 0; i < nHostDepths; i++) {
char *depthStr;
gPropHost->propGetString(gHost->host, kOfxImageEffectPropSupportedPixelDepths, i, &depthStr);
int nBits = ofxuMapPixelDepth(depthStr);
switch(nBits) {
case 8 : gSupportsBytes = true; break;
case 16 : gSupportsShorts = true; break;
case 32 : gSupportsFloats = true; break;
}
}
i = 0;
if(gSupportsBytes) gDepthParamToBytes[i++] = 8;
if(gSupportsShorts) gDepthParamToBytes[i++] = 16;
if(gSupportsFloats) gDepthParamToBytes[i++] = 32;
gPropHost->propSetString(effectProps, kOfxPropLabel, 0, "OFX Depth Converter");
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, 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, kOfxImageEffectActionGetClipPreferences) == 0) {
return getClipPreferences(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.DepthConverterExample",
1,
0,
setHostFunc,
pluginMain
};
EXPORT OfxPlugin *
OfxGetPlugin(int nth)
{
if(nth == 0)
return &basicPlugin;
return 0;
}
EXPORT int
OfxGetNumberOfPlugins(void)
{
return 1;
}