-
Notifications
You must be signed in to change notification settings - Fork 118
/
basics.cpp
334 lines (274 loc) · 13.4 KB
/
basics.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
// Copyright OpenFX and contributors to the OpenFX project.
// SPDX-License-Identifier: BSD-3-Clause
/*
Author : Bruno Nicoletti (2014)
This plugin will take you through the basics of creating an OFX plugin. It
exercises the basic 'bootstrapping' machinery of the API to allow a host
application to instantiate the plugin.
The plugin itself does no image processing, it is a 'no-op' effect.
The accompanying guide will explain what is happening in more detail.
Finally, the plugin can be used to validate whether a host application
drives the OFX machinery correctly, and will print to stderr any issues
it finds during the whole plugin life cycle.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// the one OFX header we need, it includes the others necessary
#include "ofxImageEffect.h"
#if defined __APPLE__ || defined __linux__
# define EXPORT __attribute__((visibility("default")))
#elif defined _WIN32
# define EXPORT OfxExport
#else
# error Not building on your operating system quite yet
#endif
////////////////////////////////////////////////////////////////////////////////
// macro to write a labelled message to stderr with
#ifdef _WIN32
#define DUMP(LABEL, MSG, ...) \
{ \
fprintf(stderr, "%s%s:%d in %s ", LABEL, __FILE__, __LINE__, __FUNCTION__); \
fprintf(stderr, MSG, ##__VA_ARGS__); \
fprintf(stderr, "\n"); \
}
#else
#define DUMP(LABEL, MSG, ...) \
{ \
fprintf(stderr, "%s%s:%d in %s ", LABEL, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
fprintf(stderr, MSG, ##__VA_ARGS__); \
fprintf(stderr, "\n"); \
}
#endif
// macro to write a simple message, only works if 'VERBOSE' is #defined
#ifdef VERBOSE
# define MESSAGE(MSG, ...) DUMP("", MSG, ##__VA_ARGS__)
#else
# define MESSAGE(MSG, ...)
#endif
// macro to dump errors to stderr if the given condition is true
#define ERROR_IF(CONDITION, MSG, ...) if(CONDITION) { DUMP("ERROR : ", MSG, ##__VA_ARGS__);}
// macro to dump errors to stderr and abort if the given condition is true
#define ERROR_ABORT_IF(CONDITION, MSG, ...) \
{ \
if(CONDITION) { \
DUMP("FATAL ERROR : ", MSG, ##__VA_ARGS__); \
abort(); \
} \
}
// anonymous namespace to hide our symbols in
namespace {
////////////////////////////////////////////////////////////////////////////////
// set of suite pointers provided by the host
OfxHost *gHost;
OfxPropertySuiteV1 *gPropertySuite = 0;
OfxImageEffectSuiteV1 *gImageEffectSuite = 0;
////////////////////////////////////////////////////////////////////////////////
// House keeper to make sure we are loaded and unloaded symmetrically
bool gInLoadedState = false;
bool gDescribeCalled = false;
bool gDescribeInContextCalled = false;
int gNumInstancesLiving = 0;
////////////////////////////////////////////////////////////////////////////////
// The first _action_ called after the binary is loaded (three boot strapper functions will be howeever)
OfxStatus LoadAction(void)
{
ERROR_ABORT_IF(gHost == NULL, "The OfxHost pointer has not been set, it should have been set in 'setHostFunc' before any action is called.");
ERROR_ABORT_IF(gInLoadedState == true, "kOfxActionLoad called more than once without an intervening kOfxUnloadAction.");
/// now fetch a suite out of the host via it's fetch suite function.
gPropertySuite = (OfxPropertySuiteV1 *) gHost->fetchSuite(gHost->host, kOfxPropertySuite, 1);
ERROR_ABORT_IF(gPropertySuite == 0, "Failed to fetch the " kOfxPropertySuite " version 1 from the host.");
gImageEffectSuite = (OfxImageEffectSuiteV1 *) gHost->fetchSuite(gHost->host, kOfxImageEffectSuite, 1);
// increment count, ideally this should not be called more than once. Should that be a condition? FIXME
gInLoadedState = true;
return kOfxStatOK;
}
////////////////////////////////////////////////////////////////////////////////
// last action called before a plugin binary is unloaded.
OfxStatus UnloadAction(void)
{
// make sure no instances have been left alive
ERROR_IF(gNumInstancesLiving != 0, "kOfxActionUnload called while there are still %d instances of the plugin extant.", gNumInstancesLiving);
// check that we had a load called first
ERROR_IF(gInLoadedState != true, "kOfxActionUnload callewd without preceding kOfxAcrtionLoad.");
gInLoadedState = false;
return kOfxStatOK;
}
////////////////////////////////////////////////////////////////////////////////
// the plugin's basic description routine
OfxStatus DescribeAction(OfxImageEffectHandle descriptor)
{
// check stuff
ERROR_ABORT_IF(gInLoadedState != true, "kOfxActionLoad has not been called");
gDescribeCalled = true;
// get the property set handle for the plugin
OfxPropertySetHandle effectProps;
gImageEffectSuite->getPropertySet(descriptor, &effectProps);
// set some labels and the group it belongs to
gPropertySuite->propSetString(effectProps, kOfxPropLabel, 0, "OFX Basics Example");
gPropertySuite->propSetString(effectProps, kOfxImageEffectPluginPropGrouping, 0, "OFX Example");
// define the image effects contexts we can be used in, in this case a simple filter
gPropertySuite->propSetString(effectProps, kOfxImageEffectPropSupportedContexts, 0, kOfxImageEffectContextFilter);
return kOfxStatOK;
}
// describe the plugin in context
OfxStatus
DescribeInContextAction(OfxImageEffectHandle descriptor, OfxPropertySetHandle inArgs)
{
// check state
ERROR_ABORT_IF(gDescribeCalled == false, "DescribeInContextAction called before DescribeAction");
gDescribeInContextCalled = true;
// get the context from the inArgs handle
char *context;
gPropertySuite->propGetString(inArgs, kOfxImageEffectPropContext, 0, &context);
ERROR_IF(strcmp(context, kOfxImageEffectContextFilter) != 0, "DescribeInContextAction called on unsupported contex %s", context);
OfxPropertySetHandle props;
// define the mandated single output clip
gImageEffectSuite->clipDefine(descriptor, "Output", &props);
// set the component types we can handle on out output
gPropertySuite->propSetString(props, kOfxImageEffectPropSupportedComponents, 0, kOfxImageComponentRGBA);
gPropertySuite->propSetString(props, kOfxImageEffectPropSupportedComponents, 1, kOfxImageComponentAlpha);
// define the mandated single source clip
gImageEffectSuite->clipDefine(descriptor, "Source", &props);
// set the component types we can handle on our main input
gPropertySuite->propSetString(props, kOfxImageEffectPropSupportedComponents, 0, kOfxImageComponentRGBA);
gPropertySuite->propSetString(props, kOfxImageEffectPropSupportedComponents, 1, kOfxImageComponentAlpha);
return kOfxStatOK;
}
// instance construction
OfxStatus CreateInstanceAction(OfxImageEffectHandle instance)
{
// check stuff
ERROR_ABORT_IF(gDescribeInContextCalled == false, "CreateInstanceAction called before DescribeInContext.");
++gNumInstancesLiving;
OfxPropertySetHandle effectProps;
gImageEffectSuite->getPropertySet(instance, &effectProps);
// attach some instance data to the effect handle, it can be anything
char *myString = strdup("This is random instance data that could be anything you want.");
// set my private instance data
gPropertySuite->propSetPointer(effectProps, kOfxPropInstanceData, 0, (void *) myString);
return kOfxStatOK;
}
// instance destruction
OfxStatus DestroyInstanceAction(OfxImageEffectHandle instance)
{
--gNumInstancesLiving;
OfxPropertySetHandle effectProps;
gImageEffectSuite->getPropertySet(instance, &effectProps);
// get my private instance data
char *myString = NULL;
gPropertySuite->propGetPointer(effectProps, kOfxPropInstanceData, 0, (void **) &myString);
ERROR_IF(myString == NULL, "Instance should not be null!");
free(myString);
return kOfxStatOK;
}
// are the settings of the effect making it redundant and so not do anything to the image data
OfxStatus IsIdentityAction(OfxImageEffectHandle instance,
OfxPropertySetHandle inArgs,
OfxPropertySetHandle outArgs)
{
// we set the name of the input clip to pull data from
gPropertySuite->propSetString(outArgs, kOfxPropName, 0, "Source");
return kOfxStatOK;
}
////////////////////////////////////////////////////////////////////////////////
// Call back passed to the host in the OfxPlugin struct to set our host pointer
//
// This must be called AFTER both OfxGetNumberOfPlugins and OfxGetPlugin, but
// before the pluginMain entry point is ever touched.
void SetHostFunc(OfxHost *hostStruct)
{
gHost = hostStruct;
}
////////////////////////////////////////////////////////////////////////////////
// The main entry point function, the host calls this to get the plugin to do things.
OfxStatus MainEntryPoint(const char *action, const void *handle, OfxPropertySetHandle inArgs, OfxPropertySetHandle outArgs)
{
MESSAGE(": START action is : %s \n", action );
// cast to appropriate type
OfxImageEffectHandle effect = (OfxImageEffectHandle) handle;
OfxStatus returnStatus = kOfxStatReplyDefault;
if(strcmp(action, kOfxActionLoad) == 0) {
// The very first action called on a plugin.
returnStatus = LoadAction();
}
else if(strcmp(action, kOfxActionUnload) == 0) {
// The very last action called.
returnStatus = UnloadAction();
}
else if(strcmp(action, kOfxActionDescribe) == 0) {
// the first action called to describe what the plugin does
returnStatus = DescribeAction(effect);
}
else if(strcmp(action, kOfxImageEffectActionDescribeInContext) == 0) {
// the second action called to describe what the plugin does
returnStatus = DescribeInContextAction(effect, inArgs);
}
else if(strcmp(action, kOfxActionCreateInstance) == 0) {
// the action called when an instance of a plugin is created
returnStatus = CreateInstanceAction(effect);
}
else if(strcmp(action, kOfxActionDestroyInstance) == 0) {
// the action called when an instance of a plugin is destroyed
returnStatus = DestroyInstanceAction(effect);
}
else if(strcmp(action, kOfxImageEffectActionIsIdentity) == 0) {
// The action called by the host to see if the plugin is currently
// a "no-op". In this example we are always a no-op.
returnStatus = IsIdentityAction(effect, inArgs, outArgs);
}
MESSAGE(": END action is : %s \n", action );
/// other actions to take the default value
return returnStatus;
}
////////////////////////////////////////////////////////////////////////////////
// Class that will be used to check that the plugin is correctly unloaded.
struct CheckNotStillLoaded {
CheckNotStillLoaded()
{
}
~CheckNotStillLoaded()
{
ERROR_IF(gInLoadedState == true, "kOfxActionUnload has not been called before a plugin binary was dynamically unloaded.");
}
};
// The destructor of CheckNumTimesLoadedIsZero will be called automatically when the plugin dso is unloaded.
// we use that to check that kOfxActionUnload has been called.
CheckNotStillLoaded gCheckNotStillLoaded;
} // end of anonymous namespace
////////////////////////////////////////////////////////////////////////////////
// The plugin struct passed back to the host application to initiate bootstrapping\
// of plugin communications
static OfxPlugin effectPluginStruct =
{
kOfxImageEffectPluginApi, // The API this plugin satisfies.
1, // The version of the API it satisifes.
"org.openeffects:BasicsExamplePlugin", // The unique ID of this plugin.
1, // The major version number of this plugin.
0, // The minor version number of this plugin.
SetHostFunc, // Function used to pass back to the plugin the OFXHost struct.
MainEntryPoint // The main entry point to the plugin where all actions are passed to.
};
////////////////////////////////////////////////////////////////////////////////
// The first of the two functions that a host application will look for
// after loading the binary, this function returns the number of plugins within
// this binary.
//
// This will be the first function called by the host.
EXPORT int OfxGetNumberOfPlugins(void)
{
return 1;
}
////////////////////////////////////////////////////////////////////////////////
// The second of the two functions that a host application will look for
// after loading the binary, this function returns the 'nth' plugin declared in
// this binary.
//
// This will be called multiple times by the host, once for each plugin present.
EXPORT OfxPlugin * OfxGetPlugin(int nth)
{
if(nth == 0)
return &effectPluginStruct;
ERROR_IF(nth > 0, "Host tried to get more plugins from binary than were available, called with index %d", nth);
return 0;
}