1= OFX Programming Guide : Parameters
2Author:Bruno Nicoletti
32014-10-17
4:toc:
5:data-uri:
6:source-highlighter: coderay
7
8This guide will take you through the basics of creating and using parameters in OFX.  An example plugin will be used to illustrate
9how it all works and its source can be found in the pass:[C++]
10file `Guide/Code/Example3/gain.cpp`.
11This plugin takes an image and multiplies the pixel by the value held in a user visible parameter.
12Ideally you should have read the guide to the link:ofxExample2_invert.adoc[basic image processing] before you read this guide.
13
14== Parameters
15Host applications need parameters to make their own effects work. Such as the size of a blur effect, colours to apply to some text,
16a point for the centre of a lens flare and so on. The host app will use
17some widget set to present a user interface for the parameter, have ways to do undo/redo, saving/loading, manage animation
18etc...
19
20The OFX parameters suite is the bridge from a plugin to the host's native parameter set. So plugin devs don't have to do all
21that work for themselves and also get the other advantages a host's parameters system may give (e.g. scripting).
22
23The way it works is fairly simple in concept, we get a plugin to tell the host what parameters it wants during description.
24When an instance is created, the host will make whatever
25native data structures it needs to manage those params. The plugin can then grab values from the various parameters to
26do what it needs to do during various actions, it can even write back to parameters under certain conditions.
27
28Our simple gain example will make two parameters, a parameter that is of type `**double**` which is the gain amount and a `**bool**` param
29which controls whether to apply the gain amount to the Alpha of an RGBA image. There are more parameter types, and quite
30a few properties you can set on a param to control how it should behave in a host application. The current parameter types available are...
31
32  - double - 1, 2 or 3D double precision floating point parameters, footnote:[the API manages all floating point params as doubles, the host could be
33using 32 bit floats, or fixed precision for that matter, so long as the values are passed back and forth over the API as doubles, all will be fine]
34  - integers - 1, 2 or 3D,
35  - colour parameters,
36  - boolean parameters,
37  - 'choice' parameters (where the user gets to choise 1-of-N options),
38  - string parameters,
39  - parametric parameters (which can be used for lookup tables and so on),
40  - custom parameters (where you manage all the parameter state and UI yourself).
41
42There are several UI elements that are described with pseudo parameters, these are...
43
44  - push buttons - to have the plugin do something when the user tells it (eg: analyse and image and set some params),
45  - group params - to organise params in a scrolling type UI,
46  - page params - for hosts that have a paged parameter layout as opposed to a scrolling UI.
47
48NOTE: A key concept in OFX is that the state of a plugin instance is totally and uniquely defined by the value of its parameters and input clips.
49You are asking for trouble if you try and store important data in separate files and so on. The host won't be able to properly know when things have
50changed, how to manage that extra data and so on. Attempting to manage data outside of the parameter set will almost certainly cause hosts
51to lose track of the correct render and you will get angry users.
52This is a fundamental aspect of the API. If it isn't in a parameter, it is going to cause problems with the host if you rely on it.
53
54== Actions
55This example doesn't trap any actions you haven't already seen in the other examples, it just does a little bit more in them.
56Seeing as you should be familiar with how the main entry point works, I won't bother with the code listing from now on. The actions
57our plugin traps are now ...
58
59  - kOfxActionLoad - to grab suites from the host,
60  - kOfxActionDescribe and kOfxImageEffectActionDescribeInContext - to describe the plugin to the host, __including parameters__,
61  - kOfxActionCreateInstance and kOfxActionDestroyInstance - to create and destroy instance data, where we cache handles to clips and parameters,
62  - kOfxImageEffectActionIsIdentity - to check if the parameter values are at their defaults and so the plugin can be ignore by the host,
63  - kOfxImageEffectActionRender - to actually process pixels.
64
65Now seeing as we are going to be playing with parameters, our plugin will need a new suite, the parameters suite, and our load action now looks like...
66
67[source, c++]
68.gain.cpp
69----
70  OfxPropertySuiteV1    *gPropertySuite    = 0;
71  OfxImageEffectSuiteV1 *gImageEffectSuite = 0;
72  OfxParameterSuiteV1   *gParameterSuite   = 0;
73
74  ////////////////////////////////////////////////////////////////////////////////
75  // get the named suite and put it in the given pointer, with error checking
76  template <class SUITE>
77  void FetchSuite(SUITE *& suite, const char *suiteName, int suiteVersion)
78  {
79    suite = (SUITE *) gHost->fetchSuite(gHost->host, suiteName, suiteVersion);
80    if(!suite) {
81      ERROR_ABORT_IF(suite == NULL,
82                     "Failed to fetch %s verison %d from the host.",
83                     suiteName,
84                     suiteVersion);
85    }
86  }
87
88  ////////////////////////////////////////////////////////////////////////////////
89  // The first _action_ called after the binary is loaded
90  OfxStatus LoadAction(void)
91  {
92    // fetch our three suites
93    FetchSuite(gPropertySuite,    kOfxPropertySuite,    1);
94    FetchSuite(gImageEffectSuite, kOfxImageEffectSuite, 1);
95    FetchSuite(gParameterSuite,   kOfxParameterSuite,   1);
96
97    return kOfxStatOK;
98  }
99----
100
101You can see I've written a `**FetchSuite**` function, as I got bored of writing the same code over and over. We are now fetching the a
102suite of type `**OfxParameterSuiteV1**` which is defined in the header file **ofxParam.h**. footnote:[The suite is completely independent of the image effect suite and could happily
103be used to describe parameters to other types of plugins].
104
105== Describing Our Plugin
106
107We have the standard two step description process for this plugin. The Describe action is almost exactly the same as in our previous
108examples, some names and labels have been changed is all, so I won't list it. However, the describe in context action has a few more things going on.
109
110In the listings below I've chopped out the code to describe clips, as it is exactly the same as in the last example. What's new is the bit
111where we describe parameters. I'll show the describe in context action in several small chunks to take you through it.
112
113[source, c++]
114.gain.cpp
115----
116  OfxStatus
117  DescribeInContextAction(OfxImageEffectHandle descriptor,
118                          OfxPropertySetHandle inArgs)
119  {
120    ...
121    BIG SNIP OF EXACTLY THE SAME CODE IN THE LAST EXAMPLE
122    ...
123
124    // first get the handle to the parameter set
125    OfxParamSetHandle paramSet;
126    gImageEffectSuite->getParamSet(descriptor, &paramSet);
127
128    // properties on our parameter
129    OfxPropertySetHandle paramProps;
130
131    // now define a 'gain' parameter and set its properties
132    gParameterSuite->paramDefine(paramSet,
133                                 kOfxParamTypeDouble,
134                                 GAIN_PARAM_NAME,
135                                 &paramProps);
136----
137
138The first thing we do is to grab a `**OfxParamSetHandle**` from the effect descriptor. This object represents all the
139parameters attached to a plugin and is independent and orthogonal to an image effect.
140
141The parameter suite is then used to define a parameter on that parameter set. In this case its type is double,
142and its name is "gain". These are the two most important things for a parameter.
143
144NOTE: The name uniquely identifies that parameter within the API, so no two parameters can have the same name.
145
146The last argument to `**paramDefine**` is an optional pointer to the new parameter's property set
147handle. Each parameter has a set of properties we use to refine its behaviour, most of which have
148sensible defaults.
149
150[source, c++]
151.gain.cpp
152----
153    gPropertySuite->propSetString(paramProps,
154                                  kOfxParamPropDoubleType,
155                                  0,
156                                  kOfxParamDoubleTypeScale);
157----
158
159The first property on our 'gain' param we set is the kind of double parameter it is. Many host
160applications have different kind of double parameters and user interfaces that make working with
161them easier. For example a parameter used to control a rotation might have a little dial in the UI
162to spin the angle, a 2D position parameter might get cross hairs over the image and so on. In this
163case we are saying that our double parameter represents a scaling value. OFX has more kinds of
164double parameter which you can use to best for your effect.
165
166[source, c++]
167.gain.cpp
168----
169    gPropertySuite->propSetDouble(paramProps,
170                                  kOfxParamPropDefault,
171                                  0,
172                                  1.0);
173    gPropertySuite->propSetDouble(paramProps,
174                                  kOfxParamPropMin,
175                                  0,
176                                  0.0);
177----
178
179This section sets a default value for our parameter and a logical a minimum value below which it cannot go. Note
180it does not set a maximum value, so the parameter should not be clamped to any upper value ever.
181
182[source, c++]
183.gain.cpp
184----
185    gPropertySuite->propSetDouble(paramProps,
186                                  kOfxParamPropDisplayMin,
187                                  0,
188                                  0.0);
189    gPropertySuite->propSetDouble(paramProps,
190                                  kOfxParamPropDisplayMax,
191                                  0,
192                                  10.0);
193----
194
195Numbers are often manipulated with sliders widgets in user interfaces, and it is useful to set a range on those sliders. Which
196is exactly what we are doing here. This is distinct to the logical mimimum and maximum values, so you can set a 'useful' range
197for the UI, but still allow the values to be outside that range. So here a slider would only allow values between 0.0 and 10.0
198for our gain param, but the parameter could be set to a million via other means, eg: typing in a UI number box, animation, scripting
199whatever.
200
201[source, c++]
202.gain.cpp
203----
204    gPropertySuite->propSetString(paramProps,
205                                  kOfxPropLabel,
206                                  0,
207                                  "Gain");
208    gPropertySuite->propSetString(paramProps,
209                                  kOfxParamPropHint,
210                                  0,
211                                  "How much to multiply the image by.");
212----
213Here we are setting two text field on the param. The first is a label for the parameter. This is to be used in any UI the host
214has to label the parameter. It defaults to the name of the param, but it can be entirely different. Finally we set a hint string
215to be used for the parameter.
216
217[source, c++]
218.gain.cpp
219----
220    // and define the 'applyToAlpha' parameters and set its properties
221    gParameterSuite->paramDefine(paramSet,
222                                 kOfxParamTypeBoolean,
223                                 APPLY_TO_ALPHA_PARAM_NAME,
224                                 &paramProps);
225    gPropertySuite->propSetInt(paramProps,
226                               kOfxParamPropDefault,
227                               0,
228                               0);
229    gPropertySuite->propSetString(paramProps,
230                                  kOfxParamPropHint,
231                                  0,
232                                  "Whether to apply the gain value to alpha as well.");
233    gPropertySuite->propSetString(paramProps,
234                                  kOfxPropLabel,
235                                  0,
236                                  "Apply To Alpha");
237
238    return kOfxStatOK;
239  }
240----
241
242In this last section we define a second parameter, named "applyToAlpha", which is of type boolean. We then
243set some obvious state on it and we are done. Notice the label we set, it is much clearer to read than the name.
244
245And that's it, we've defined two parameters for our plugin. There are many more properties you can set
246on your plugin to control how they behave and to give hints as to what you are going to do to them.
247
248image::Pics/GainControlPanelNuke.jpg[ role = "thumb", align=center, title=Control Panel For Our Example In Nuke]
249
250Finally, the image above shows the control panel for an instance of our example inside Nuke.
251
252== Instances and Parameters
253
254When the host creates an instance of the plugin, it will first create all the native data structures it
255needs to represent the plugin, fully populate them with the required values, and only then call the create
256instance action.
257
258So what happens in the create instance action then? Possibly nothing, you can always grab parameters from
259an instance by name at any time. But to make our code a bit cleaner and to show an example of instance data
260being used, we are going to trap create instance.
261
262[source, c++]
263.gain.cpp
264----
265  ////////////////////////////////////////////////////////////////////////////////
266  // our instance data, where we are caching away clip and param handles
267  struct MyInstanceData {
268    // handles to the clips we deal with
269    OfxImageClipHandle sourceClip;
270    OfxImageClipHandle outputClip;
271
272    // handles to a our parameters
273    OfxParamHandle gainParam;
274    OfxParamHandle applyToAlphaParam;
275  };
276----
277
278To stop duplicating code all over, and to minimise fetches to various handles, we are going to cache
279away handles to our clips and parameters in a simple struct. Note that these handles are valid for the duration
280of the instance.
281
282[source, c++]
283.gain.cpp
284----
285  ////////////////////////////////////////////////////////////////////////////////
286  /// instance construction
287  OfxStatus CreateInstanceAction( OfxImageEffectHandle instance)
288  {
289    OfxPropertySetHandle effectProps;
290    gImageEffectSuite->getPropertySet(instance, &effectProps);
291
292    // To avoid continual lookup, put our handles into our instance
293    // data, those handles are guaranteed to be valid for the duration
294    // of the instance.
295    MyInstanceData *myData = new MyInstanceData;
296
297    // Set my private instance data
298    gPropertySuite->propSetPointer(effectProps, kOfxPropInstanceData, 0, (void *) myData);
299
300    // Cache the source and output clip handles
301    gImageEffectSuite->clipGetHandle(instance, "Source", &myData->sourceClip, 0);
302    gImageEffectSuite->clipGetHandle(instance, "Output", &myData->outputClip, 0);
303
304    // Cache away the param handles
305    OfxParamSetHandle paramSet;
306    gImageEffectSuite->getParamSet(instance, &paramSet);
307    gParameterSuite->paramGetHandle(paramSet,
308                                    GAIN_PARAM_NAME,
309                                    &myData->gainParam,
310                                    0);
311    gParameterSuite->paramGetHandle(paramSet,
312                                    APPLY_TO_ALPHA_PARAM_NAME,
313                                    &myData->applyToAlphaParam,
314                                    0);
315
316    return kOfxStatOK;
317  }
318----
319
320So here is the function called when we trap a create instance action. You can see that it allocates a
321MyInstanceData struct and caches it away in the instance's property set.
322
323It then fetches handles to the two clips and two parameters by name and caches those into the
324newly created struct.
325
326[source, c++]
327.gain.cpp
328----
329  ////////////////////////////////////////////////////////////////////////////////
330  // get my instance data from a property set handle
331  MyInstanceData *FetchInstanceData(OfxPropertySetHandle effectProps)
332  {
333    MyInstanceData *myData = 0;
334    gPropertySuite->propGetPointer(effectProps,
335                                   kOfxPropInstanceData,
336                                   0,
337                                   (void **) &myData);
338    return myData;
339  }
340----
341
342And here is a simple function to fetch instance data. It is actually overloaded and there is another version
343that take an `**OfxImageEffectHandle**`.
344
345Of course we now need to trap the destroy instance action to delete our instance data, otherwise we
346will get memory leaks.
347
348[source, c++]
349.gain.cpp
350----
351  ////////////////////////////////////////////////////////////////////////////////
352  // instance destruction
353  OfxStatus DestroyInstanceAction( OfxImageEffectHandle instance)
354  {
355    // get my instance data
356    MyInstanceData *myData = FetchInstanceData(instance);
357    delete myData;
358
359    return kOfxStatOK;
360  }
361----
362
363== Getting Values From Instances
364
365So we've define our parameters, we've got handles to the instance of them, but we will want to grab
366the value of the parameters to actually use them at render time.
367
368[source, c++]
369.gain.cpp
370----
371  ////////////////////////////////////////////////////////////////////////////////
372  // Render an output image
373  OfxStatus RenderAction( OfxImageEffectHandle instance,
374                          OfxPropertySetHandle inArgs,
375                          OfxPropertySetHandle outArgs)
376  {
377    // get the render window and the time from the inArgs
378    OfxTime time;
379    OfxRectI renderWindow;
380    OfxStatus status = kOfxStatOK;
381
382    gPropertySuite->propGetDouble(inArgs, kOfxPropTime, 0, &time);
383    gPropertySuite->propGetIntN(inArgs, kOfxImageEffectPropRenderWindow, 4, &renderWindow.x1);
384
385    // get our instance data which has out clip and param handles
386    MyInstanceData *myData = FetchInstanceData(instance);
387
388    // get our param values
389    double gain = 1.0;
390    int applyToAlpha = 0;
391    gParameterSuite->paramGetValueAtTime(myData->gainParam, time, &gain);
392    gParameterSuite->paramGetValueAtTime(myData->applyToAlphaParam, time, &applyToAlpha);
393
394....
395
396----
397
398We are using the `**paramGetValueAtTime**` suite function to get the value of our parameters for the
399given time we are rendering at. Nearly all actions passed to an instance will have a time to perform
400the instance at, you should use this when fetching values out of a param.
401
402The param get value functions use var-args to return values to plugins, similar to a C scanf function.
403
404And finally here is a snippet of the templated pixel pushing code where we do the actuall processing using
405our parameter values;
406
407[source, c++]
408.gain.cpp
409----
410    // and do some processing
411    for(int y = renderWindow.y1; y < renderWindow.y2; y++) {
412      if(y % 20 == 0 && gImageEffectSuite->abort(instance)) break;
413
414      // get the row start for the output image
415      T *dstPix = pixelAddress<T>(renderWindow.x1, y,
416                                  dstPtr,
417                                  dstBounds,
418                                  dstRowBytes,
419                                  nComps);
420
421      for(int x = renderWindow.x1; x < renderWindow.x2; x++) {
422
423        // get the source pixel
424        T *srcPix = pixelAddress<T>(x, y,
425                                    srcPtr,
426                                    srcBounds,
427                                    srcRowBytes,
428                                    nComps);
429
430        if(srcPix) {
431          // we have one, iterate each component in the pixels
432          for(int i = 0; i < nComps; ++i) {
433            if(i != 3 || applyToAlpha) {
434              // multiply our source component by our gain value
435              double value = *srcPix * gain;
436
437              // if it has gone out of legal bounds, clamp it
438              if(MAX != 1) {  // we let floating point pixels over and underflow
439                value = value < 0 ? 0 : (value > MAX ? MAX : value);
440              }
441              *dstPix = T(value);
442            }
443            else {
444              *dstPix = *srcPix;
445            }
446            // increment to next component
447            ++dstPix; ++srcPix;
448          }
449        }
450        else {
451          // we don't have a pixel in the source image, set output to zero
452          for(int i = 0; i < nComps; ++i) {
453            *dstPix = 0;
454            ++dstPix;
455          }
456        }
457      }
458    }
459----
460Notice that we are checking to see if `MAX != 1`, which means our pixels are not floating point. If
461that is the case, we are clamping the pixel's value so we don't get integer overflow.
462
463== Summary
464This plugin has shown you the basics of working with OFX parameters, the main things it illustrated were...
465
466   - defining parameters in the define in context action,
467   - setting properties to control the behaviour of parameters,
468   - using the instance data pointer to cache away handles to instances of parameters and clips,
469   - fetching values of a parameter from parameter instance handles and using them to process pixels.
470