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