1 /*
2 Software License :
3 
4 Copyright (c) 2003, The Open Effects Association Ltd. All rights reserved.
5 
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8 
9     * Redistributions of source code must retain the above copyright notice,
10       this list of conditions and the following disclaimer.
11     * Redistributions in binary form must reproduce the above copyright notice,
12       this list of conditions and the following disclaimer in the documentation
13       and/or other materials provided with the distribution.
14     * Neither the name The Open Effects Association Ltd, nor the names of its
15       contributors may be used to endorse or promote products derived from this
16       software without specific prior written permission.
17 
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29 
30 /*
31   Ofx Example plugin that shows how an overlay is drawn and interacted with.
32 
33   It is meant to illustrate certain features of the API, as opposed to being a perfectly
34   crafted piece of image processing software.
35 
36   Note, that the default bitdepths and components are specified for the source (RGBA + A, 8 + 16 + 32) and that
37   this plugin does absolutely no image processing, it assumes the isIdentity action will be called before a render
38   and so do nothing.
39  */
40 
41 #ifdef WIN32
42 #include <windows.h>
43 #endif
44 
45 #include <cstring>
46 #ifdef __APPLE__
47 #include <OpenGL/gl.h>
48 #else
49 #include <GL/gl.h>
50 #endif
51 #include <cmath>
52 #include <stdexcept>
53 #include <new>
54 #include "ofxImageEffect.h"
55 #include "ofxMemory.h"
56 #include "ofxMultiThread.h"
57 
58 #include "../include/ofxUtilities.H" // example support utils
59 
60 #if defined __APPLE__ || defined linux || defined __DragonFly__
61 #  define EXPORT __attribute__((visibility("default")))
62 #elif defined _WIN32
63 #  define EXPORT OfxExport
64 #else
65 #  error Not building on your operating system quite yet
66 #endif
67 
68 #define kPointParam "point"
69 
70 // pointers to various bits of the host
71 OfxHost               *gHost;
72 OfxImageEffectSuiteV1 *gEffectHost = 0;
73 OfxPropertySuiteV1    *gPropHost = 0;
74 OfxParameterSuiteV1   *gParamHost = 0;
75 OfxMemorySuiteV1      *gMemoryHost = 0;
76 OfxMultiThreadSuiteV1 *gThreadHost = 0;
77 OfxMessageSuiteV1     *gMessageSuite = 0;
78 OfxInteractSuiteV1    *gInteractHost = 0;
79 
80 
81 // we are always identity as we are just a hack example plugin
82 static OfxStatus
isIdentity(OfxImageEffectHandle,OfxPropertySetHandle,OfxPropertySetHandle outArgs)83 isIdentity(OfxImageEffectHandle  /*pluginInstance*/,
84 	   OfxPropertySetHandle /*inArgs*/,
85 	   OfxPropertySetHandle outArgs)
86 {
87   // set the property in the out args indicating which is the identity clip
88   gPropHost->propSetString(outArgs, kOfxPropName, 0, kOfxImageEffectSimpleSourceClipName);
89   return kOfxStatOK;
90 }
91 
92 // the process code  that the host sees
render(OfxImageEffectHandle,OfxPropertySetHandle,OfxPropertySetHandle)93 static OfxStatus render(OfxImageEffectHandle  /*instance*/,
94 			OfxPropertySetHandle /*inArgs*/,
95 			OfxPropertySetHandle /*outArgs*/)
96 {
97   // do nothing as this should never be called as isIdentity should always be trapped
98   return kOfxStatOK;
99 }
100 
101 ////////////////////////////////////////////////////////////////////////////////
102 // the interaction routines
103 struct MyInteractData {
104   bool selected;
105   OfxParamHandle pointParam;
106 
MyInteractDataMyInteractData107   explicit MyInteractData(OfxParamHandle pParam)
108     : selected(false)
109     , pointParam(pParam)
110   {
111   }
112 };
113 
114 // get the interact data from an interact instance
115 static MyInteractData *
getInteractData(OfxInteractHandle interactInstance)116 getInteractData(OfxInteractHandle interactInstance)
117 {
118   void *dataV = ofxuGetInteractInstanceData(interactInstance);
119   return (MyInteractData *) dataV;
120 }
121 
122 // creation of an interact instance
123 static OfxStatus
interactDescribe(OfxInteractHandle)124 interactDescribe(OfxInteractHandle /*interactDescriptor*/)
125 {
126 
127   // and we are good
128   return kOfxStatOK;
129 }
130 
131 // creation of an interact instance
132 static OfxStatus
interactCreateInstance(OfxImageEffectHandle pluginInstance,OfxInteractHandle interactInstance)133 interactCreateInstance(OfxImageEffectHandle pluginInstance,
134 		       OfxInteractHandle interactInstance)
135 {
136   // get the parameter set for this effect
137   OfxParamSetHandle paramSet;
138   gEffectHost->getParamSet(pluginInstance, &paramSet);
139 
140   // fetch a handle to the point param from the parameter set
141   OfxParamHandle pointParam;
142   gParamHost->paramGetHandle(paramSet, kPointParam, &pointParam, 0);
143 
144   // make my interact's instance data
145   MyInteractData *data = new MyInteractData(pointParam);
146 
147   // and set the interact's data pointer
148   ofxuSetInteractInstanceData(interactInstance, (void *) data);
149 
150   OfxPropertySetHandle interactProps;
151   gInteractHost->interactGetPropertySet(interactInstance, &interactProps);
152 
153   // slave this interact to the point param so redraws are triggered cleanly
154   gPropHost->propSetString(interactProps, kOfxInteractPropSlaveToParam, 0, kPointParam);
155 
156   return kOfxStatOK;
157 }
158 
159 // destruction of an interact instance
160 static OfxStatus
interactDestroyInstance(OfxImageEffectHandle,OfxInteractHandle interactInstance)161 interactDestroyInstance(OfxImageEffectHandle  /*pluginInstance*/,
162 			OfxInteractHandle interactInstance)
163 {
164   MyInteractData *data = getInteractData(interactInstance);
165   delete data;
166   return kOfxStatOK;
167 }
168 
169 // size of the cross hair in screen pixels
170 #define kXHairSize 10
171 
172 // draw an interact instance
173 static OfxStatus
interactDraw(OfxImageEffectHandle pluginInstance,OfxInteractHandle interactInstance,OfxPropertySetHandle drawArgs)174 interactDraw(OfxImageEffectHandle  pluginInstance,
175 	     OfxInteractHandle interactInstance,
176 	     OfxPropertySetHandle drawArgs)
177 {
178   // get my private interact data
179   MyInteractData *data = getInteractData(interactInstance);
180 
181   // get the size of a pixel in the current projection
182   double pixelScale[2];
183   ofxuGetInteractPixelScale(drawArgs, pixelScale);
184 
185   // get my param's value
186   double x, y;
187   gParamHost->paramGetValue(data->pointParam, &x, &y);
188 
189   // make the xhair a constant size on screen by scaling by the pixel scale
190   float dx = kXHairSize * pixelScale[0];
191   float dy = kXHairSize * pixelScale[1];
192 
193   // if the we have selected the Xhair, draw it highlit
194   if(data->selected)
195     glColor3f(1, 1, 1);
196   else
197     glColor3f(1, 0, 0);
198 
199   // Draw a cross hair, the current coordinate system aligns with the image plane.
200   glPushMatrix();
201 
202   glTranslated(x, y, 0);
203 
204   glBegin(GL_LINES);
205 
206   glVertex2f(-dx, 0);
207   glVertex2f(dx, 0);
208 
209   glVertex2f(0, -dy);
210   glVertex2f(0, dy);
211 
212   glEnd();
213 
214 
215   glPopMatrix();
216 
217   return kOfxStatOK;
218 }
219 
220 // function reacting to pen motion
221 static OfxStatus
interactPenMotion(OfxImageEffectHandle pluginInstance,OfxInteractHandle interactInstance,OfxPropertySetHandle inArgs)222 interactPenMotion(OfxImageEffectHandle  pluginInstance,
223 		  OfxInteractHandle interactInstance,
224 		  OfxPropertySetHandle inArgs)
225 {
226   // get my data handle
227   MyInteractData *data = getInteractData(interactInstance);
228 
229   // Have we grabbed on a pen down already?
230   if(data->selected) {
231     // get the pen position
232     OfxPointD penPos = {0., 0.};
233     gPropHost->propGetDoubleN(inArgs, kOfxInteractPropPenPosition, 2, &penPos.x);
234 
235     // set the value of the 'point' param
236     gParamHost->paramSetValue(data->pointParam, penPos.x, penPos.y);
237     return kOfxStatOK;
238   }
239   return kOfxStatReplyDefault;
240 }
241 
242 static OfxStatus
interactPenDown(OfxImageEffectHandle pluginInstance,OfxInteractHandle interactInstance,OfxPropertySetHandle inArgs)243 interactPenDown(OfxImageEffectHandle  pluginInstance,
244 		OfxInteractHandle interactInstance,
245 		OfxPropertySetHandle inArgs)
246 {
247   // get my data handle
248   MyInteractData *data = getInteractData(interactInstance);
249 
250   // get the point param's value
251   double x, y;
252   gParamHost->paramGetValue(data->pointParam, &x, &y);
253 
254   // get the size of a pixel on screen
255   double pixelScale[2];
256   ofxuGetInteractPixelScale(inArgs, pixelScale);
257 
258   // see if the pen is within 5 screen pixels of the point, in which case, select it
259   double penPos[2];
260   gPropHost->propGetDoubleN(inArgs, kOfxInteractPropPenPosition, 2, penPos);
261   if(fabs(x - penPos[0]) < 5 * pixelScale[0] && fabs(y - penPos[1]) < 5 * pixelScale[1]) {
262     data->selected = true;
263     return kOfxStatOK;
264   }
265   return kOfxStatReplyDefault;
266 }
267 
268 static OfxStatus
interactPenUp(OfxImageEffectHandle,OfxInteractHandle interactInstance,OfxPropertySetHandle)269 interactPenUp(OfxImageEffectHandle  /*pluginInstance*/,
270 		OfxInteractHandle interactInstance,
271 		OfxPropertySetHandle /*inArgs*/)
272 {
273   // get my data handle
274   MyInteractData *data = getInteractData(interactInstance);
275 
276   if(data->selected) {
277     // pen's gone up, deselect
278     data->selected = false;
279     return kOfxStatOK;
280   }
281   return kOfxStatReplyDefault;
282 }
283 
284 ////////////////////////////////////////////////////////////////////////////////
285 // the entry point for the overlay
286 static OfxStatus
overlayMain(const char * action,const void * handle,OfxPropertySetHandle inArgs,OfxPropertySetHandle)287 overlayMain(const char *action,  const void *handle, OfxPropertySetHandle inArgs, OfxPropertySetHandle /*outArgs*/)
288 {
289   OfxInteractHandle interact = (OfxInteractHandle ) handle;
290 
291   OfxPropertySetHandle props;
292   gInteractHost->interactGetPropertySet(interact, &props);
293 
294   if(strcmp(action, kOfxActionDescribe) == 0) {
295     return interactDescribe(interact);
296   }
297   else {
298     // fetch the effect instance from the interact
299     OfxImageEffectHandle pluginInstance;
300     gPropHost->propGetPointer(props, kOfxPropEffectInstance, 0, (void **) &pluginInstance);
301 
302     if(strcmp(action, kOfxActionCreateInstance) == 0) {
303       return interactCreateInstance(pluginInstance, interact);
304     }
305     else if(strcmp(action, kOfxActionDestroyInstance) == 0) {
306       return interactDestroyInstance(pluginInstance, interact);
307     }
308     else if(strcmp(action, kOfxInteractActionDraw) == 0) {
309       return interactDraw(pluginInstance, interact, inArgs);
310     }
311     else if(strcmp(action, kOfxInteractActionPenMotion) == 0) {
312       return interactPenMotion(pluginInstance, interact, inArgs);
313     }
314     else if(strcmp(action, kOfxInteractActionPenDown) == 0) {
315       return interactPenDown(pluginInstance, interact, inArgs);
316     }
317     else if(strcmp(action, kOfxInteractActionPenUp) == 0) {
318       return interactPenUp(pluginInstance, interact, inArgs);
319     }
320     return kOfxStatReplyDefault;
321   }
322   return kOfxStatReplyDefault;
323 }
324 
325 ////////////////////////////////////////////////////////////////////////////////
326 // the plugin's description routine
327 static OfxStatus
describe(OfxImageEffectHandle effect)328 describe(OfxImageEffectHandle effect)
329 {
330   // fetch the host APIs
331   OfxStatus stat;
332   if((stat = ofxuFetchHostSuites()) != kOfxStatOK)
333     return stat;
334 
335   // see if the host supports overlays
336   int supportsOverlays;
337   gPropHost->propGetInt(gHost->host, kOfxImageEffectPropSupportsOverlays, 0, &supportsOverlays);
338   if(supportsOverlays == 0)
339     return kOfxStatErrMissingHostFeature;
340 
341   // get the property handle for the plugin
342   OfxPropertySetHandle effectProps;
343   gEffectHost->getPropertySet(effect, &effectProps);
344 
345   // define the plugin to the host
346   gPropHost->propSetString(effectProps, kOfxImageEffectPropSupportedPixelDepths, 0, kOfxBitDepthByte);
347   gPropHost->propSetString(effectProps, kOfxImageEffectPropSupportedPixelDepths, 1, kOfxBitDepthShort);
348   gPropHost->propSetString(effectProps, kOfxImageEffectPropSupportedPixelDepths, 2, kOfxBitDepthFloat);
349 
350   // set the bit depths the plugin can handle
351   gPropHost->propSetString(effectProps, kOfxPropLabel, 0, "OFX Overlay Example");
352   gPropHost->propSetString(effectProps, kOfxImageEffectPluginPropGrouping, 0, "OFX Example");
353 
354   // define the contexts we can be used in
355   gPropHost->propSetString(effectProps, kOfxImageEffectPropSupportedContexts, 0, kOfxImageEffectContextFilter);
356 
357   // set the property that is the overlay's main entry point for the plugin
358   gPropHost->propSetPointer(effectProps, kOfxImageEffectPluginPropOverlayInteractV1, 0,  (void *) overlayMain);
359 
360   return kOfxStatOK;
361 }
362 
363 static OfxStatus
describeInContext(OfxImageEffectHandle effect,OfxPropertySetHandle)364 describeInContext(OfxImageEffectHandle  effect, OfxPropertySetHandle /*inArgs*/)
365 {
366   // define the single source clip
367   OfxPropertySetHandle props;
368   gEffectHost->clipDefine(effect, kOfxImageEffectSimpleSourceClipName, &props);
369   gPropHost->propSetString(props, kOfxImageEffectPropSupportedComponents, 0, kOfxImageComponentRGBA);
370   gPropHost->propSetString(props, kOfxImageEffectPropSupportedComponents, 1, kOfxImageComponentAlpha);
371 
372   // define the output clip
373   gEffectHost->clipDefine(effect, kOfxImageEffectOutputClipName, &props);
374   gPropHost->propSetString(props, kOfxImageEffectPropSupportedComponents, 0, kOfxImageComponentRGBA);
375   gPropHost->propSetString(props, kOfxImageEffectPropSupportedComponents, 1, kOfxImageComponentAlpha);
376 
377   // fetch the parameter set from the effect
378   OfxParamSetHandle paramSet;
379   gEffectHost->getParamSet(effect, &paramSet);
380 
381   // define the 2D point we are going to draw an overlay for
382   gParamHost->paramDefine(paramSet, kOfxParamTypeDouble2D, kPointParam, &props);
383   gPropHost->propSetString(props, kOfxParamPropDoubleType, 0, kOfxParamDoubleTypeXYAbsolute);
384   gPropHost->propSetString(props, kOfxParamPropDefaultCoordinateSystem, 0, kOfxParamCoordinatesNormalised);
385   gPropHost->propSetDouble(props, kOfxParamPropDefault, 0, 0.5);
386   gPropHost->propSetDouble(props, kOfxParamPropDefault, 1, 0.5);
387   gPropHost->propSetString(props, kOfxParamPropHint, 0, "Point attached to overlay crosshair");
388   gPropHost->propSetString(props, kOfxParamPropScriptName, 0, "point");
389   gPropHost->propSetString(props, kOfxPropLabel, 0, "Point");
390 
391   return kOfxStatOK;
392 }
393 
394 ////////////////////////////////////////////////////////////////////////////////
395 // The main function
396 static OfxStatus
pluginMain(const char * action,const void * handle,OfxPropertySetHandle inArgs,OfxPropertySetHandle outArgs)397 pluginMain(const char *action, const void *handle, OfxPropertySetHandle inArgs, OfxPropertySetHandle outArgs)
398 {
399   try {
400   // cast to appropriate type
401   OfxImageEffectHandle effect = (OfxImageEffectHandle ) handle;
402 
403   if(strcmp(action, kOfxActionDescribe) == 0) {
404     return describe(effect);
405   }
406   else if(strcmp(action, kOfxImageEffectActionDescribeInContext) == 0) {
407     return describeInContext(effect, inArgs);
408   }
409   else if(strcmp(action, kOfxImageEffectActionIsIdentity) == 0) {
410     return isIdentity(effect, inArgs, outArgs);
411   }
412   else if(strcmp(action, kOfxImageEffectActionRender) == 0) {
413     return render(effect, inArgs, outArgs);
414   }
415   } catch (std::bad_alloc) {
416     // catch memory
417     //std::cout << "OFX Plugin Memory error." << std::endl;
418     return kOfxStatErrMemory;
419   } catch ( const std::exception& e ) {
420     // standard exceptions
421     //std::cout << "OFX Plugin error: " << e.what() << std::endl;
422     return kOfxStatErrUnknown;
423   } catch (int err) {
424     // ho hum, gone wrong somehow
425     return err;
426   } catch ( ... ) {
427     // everything else
428     //std::cout << "OFX Plugin error" << std::endl;
429     return kOfxStatErrUnknown;
430   }
431 
432   // other actions to take the default value
433   return kOfxStatReplyDefault;
434 }
435 
436 // function to set the host structure
437 static void
setHostFunc(OfxHost * hostStruct)438 setHostFunc(OfxHost *hostStruct)
439 {
440   gHost         = hostStruct;
441 }
442 
443 ////////////////////////////////////////////////////////////////////////////////
444 // the plugin struct
445 static OfxPlugin basicPlugin =
446 {
447   kOfxImageEffectPluginApi,
448   1,
449   "uk.co.thefoundry.BasicOverlayPlugin",
450   1,
451   0,
452   setHostFunc,
453   pluginMain
454 };
455 
456 // the two mandated functions
457 EXPORT OfxPlugin *
OfxGetPlugin(int nth)458 OfxGetPlugin(int nth)
459 {
460   if(nth == 0)
461     return &basicPlugin;
462   return 0;
463 }
464 
465 EXPORT int
OfxGetNumberOfPlugins(void)466 OfxGetNumberOfPlugins(void)
467 {
468   return 1;
469 }
470 
471 
472