1 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3  * This file is part of openfx-supportext <https://github.com/devernay/openfx-supportext>,
4  * Copyright (C) 2013-2018 INRIA
5  *
6  * openfx-supportext is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * openfx-supportext is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with openfx-supportext.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>
18  * ***** END LICENSE BLOCK ***** */
19 
20 /*
21  * Helper functions to implement plug-ins that support kFnOfxImageEffectPlaneSuite v2
22  * In order to use these functions the following condition must be met:
23  *#if defined(OFX_EXTENSIONS_NUKE) && defined(OFX_EXTENSIONS_NATRON)
24 
25     if (OFX::fetchSuite(kFnOfxImageEffectPlaneSuite, 2) &&  // for clipGetImagePlane
26         OFX::getImageEffectHostDescription()->supportsDynamicChoices && // for dynamic layer choices
27         OFX::getImageEffectHostDescription()->isMultiPlanar) // for clipGetImagePlane
28          ... this is ok...
29  *#endif
30  */
31 
32 #ifndef openfx_supportext_ofxsMultiPlane_h
33 #define openfx_supportext_ofxsMultiPlane_h
34 
35 #include <cmath>
36 #include <map>
37 #include <string>
38 #include <list>
39 #include <vector>
40 
41 #include "ofxsImageEffect.h"
42 #include "ofxsMacros.h"
43 #ifdef OFX_EXTENSIONS_NATRON
44 #include "ofxNatron.h"
45 #endif
46 
47 
48 #define kOfxMultiplaneColorPlaneID kFnOfxImagePlaneColour
49 #define kOfxMultiplaneColorPlaneLabel "Color"
50 
51 #define kOfxMultiplaneBackwardMotionVectorsPlaneID kFnOfxImagePlaneBackwardMotionVector
52 #define kOfxMultiplaneBackwardMotionVectorsPlaneLabel "Backward"
53 
54 #define kOfxMultiplaneForwardMotionVectorsPlaneID kFnOfxImagePlaneForwardMotionVector
55 #define kOfxMultiplaneForwardMotionVectorsPlaneLabel "Forward"
56 
57 #define kOfxMultiplaneDisparityLeftPlaneID kFnOfxImagePlaneStereoDisparityLeft
58 #define kOfxMultiplaneDisparityLeftPlaneLabel "DisparityLeft"
59 
60 #define kOfxMultiplaneDisparityRightPlaneID kFnOfxImagePlaneStereoDisparityRight
61 #define kOfxMultiplaneDisparityRightPlaneLabel "DisparityRight"
62 
63 #define kOfxMultiplaneDisparityComponentsLabel "Disparity"
64 #define kOfxMultiplaneMotionComponentsLabel "Motion"
65 
66 
67 #define kMultiPlaneChannelParamOption0 "0"
68 #define kMultiPlaneChannelParamOption0Hint "0 constant channel"
69 #define kMultiPlaneChannelParamOption1 "1"
70 #define kMultiPlaneChannelParamOption1Hint "1 constant channel"
71 
72 #define kMultiPlanePlaneParamOptionNone "none"
73 #define kMultiPlanePlaneParamOptionNoneLabel "None"
74 
75 #define kMultiPlaneProcessAllPlanesParam "processAllPlanes"
76 #define kMultiPlaneProcessAllPlanesParamLabel "All Planes"
77 #define kMultiPlaneProcessAllPlanesParamHint "When checked all planes in input will be processed and output to the same plane as in input. It is useful for example to apply a Transform effect on all planes."
78 
79 
80 
81 /*
82  A Multi-planar effect is a plane that can process arbitrary (other than color) image planes.
83  The planes described further can be seen as a layer in the OpenEXR specification.
84 
85  A plane consists of an arbitrary number of channels, ranging from 1 to 4 (included) and is
86  labeled with a unique name and a label
87  For example...
88     The Color.RGBA plane, corresponds to the Color plane with 4 channels: RGBA
89 
90  This extension also allow to alias the channel names in a more meaningful way,
91  For example...
92 
93     The DisparityLeft.Disparity plane corresponds to the DisparityLeft plane with
94     channels of type "Disparity", being 2 channels: XY.
95 
96  The generic way of describing a layer is done as such:
97 
98 
99  kNatronOfxImageComponentsPlaneName +
100  planeName +
101  <optional> kNatronOfxImageComponentsPlaneLabel + planeLabel +
102  <optional> kNatronOfxImageComponentsPlaneChannelsLabel + channelsLabel +
103  kNatronOfxImageComponentsPlaneChannel + channel1Name +
104  kNatronOfxImageComponentsPlaneChannel + channel2Name +
105  kNatronOfxImageComponentsPlaneChannel + channel3Name
106 
107  Examples:
108 
109  kNatronOfxImageComponentsPlaneName + "fr.unique.id.position" + kNatronOfxImageComponentsPlaneLabel + "Position" + kNatronOfxImageComponentsPlaneChannel + "X" + kNatronOfxImageComponentsPlaneChannel + "Y" + kNatronOfxImageComponentsPlaneChannel + "Z"
110 
111 
112  kNatronOfxImageComponentsPlaneName + "DisparityLeft" + kNatronOfxImageComponentsPlaneChannelsLabel + "Disparity" + kNatronOfxImageComponentsPlaneChannel + "X" + kNatronOfxImageComponentsPlaneChannel + "Y"
113 
114 
115  A multi-planar effect is expected to set the kFnOfxImageEffectPropMultiPlanar property to 1 on the image effect descritpor.
116 
117  A multi-planar effect is expected to support images of any number of channels:
118 
119  - The planes fetched from clipGetImagePlane are returned "as is" and will not be remapped by the host to another components type
120 
121  - The exception is for the kFnOfxImagePlaneColour plane: the components of the plane will be remapped to those specified in the getClipPreferences action
122 
123  A multi-planar effect is expected to specify the planes it produces in output and the planes it needs in input via the getClipComponents action.
124 
125  OpenFX only defines kOfxImageComponentRGBA, kOfxImageComponentRGB, kOfxImageComponentAlpha for default components type.
126  However, an effect may still be able to process 2-channel image and be non multi-planar aware.
127  To enable this, we also define kNatronOfxImageComponentXY as a components type that can be used in the getClipPreferences action and can be indicated
128  on a 2-channel image.
129 
130  Support for Nuke:
131  ----------------
132 
133  Nuke only supports the MultiPlane suite V1 and does not uses the getClipComponents action and does not support abitrary planes as defined above.
134 
135  Nuke only supports the following hard-coded planes:
136 
137  kFnOfxImagePlaneColour --> This plane can only have the following components types: kOfxImageComponentRGBA, kOfxImageComponentRGB, kOfxImageComponentAlpha
138 
139  kFnOfxImagePlaneBackwardMotionVector --> This plane has the following components type: kFnOfxImageComponentMotionVectors
140  kFnOfxImagePlaneForwardMotionVector --> This plane has the following components type: kFnOfxImageComponentMotionVectors
141 
142  kFnOfxImagePlaneStereoDisparityLeft --> This plane has the following components type: kFnOfxImageComponentStereoDisparity
143  kFnOfxImagePlaneStereoDisparityRight --> This plane has the following components type: kFnOfxImageComponentStereoDisparity
144 
145  Flagging kFnOfxImageEffectPropMultiPlanar=1 in Nuke only means that besides the regular clipGetImage call to get the color plane,
146  you can fetch the motion vectors (BW/FW) and disparity planes (Left, Right) with the clipGetImagePlane call.
147 
148  To indicate which plane you want on a clip, you do so from the getClipPreferences action by indicating the components TYPE,
149  that is kFnOfxImageComponentMotionVectors, kFnOfxImageComponentStereoDisparity or kOfxImageComponentRGBA, kOfxImageComponentRGB, kOfxImageComponentAlpha
150 
151  If a clip is set to the kFnOfxImageComponentMotionVectors components type, the host expects the plug-in to call clipGetImagePlane on both the
152  kFnOfxImagePlaneBackwardMotionVector plane and the kFnOfxImagePlaneForwardMotionVector plane.
153  These planes are "paired": a plug-in that renders kFnOfxImageComponentMotionVectors is expected to render both planes at once.
154 
155  Similarly, if the clip components are set to kFnOfxImageComponentStereoDisparity, the host expects that the plug-in calls clipGetImagePlane on both
156  the kFnOfxImagePlaneStereoDisparityLeft and kFnOfxImagePlaneStereoDisparityRight planes.
157 
158 
159  Planes vs. components:
160  ----------------------
161 
162  Planes and components are different things. A plane is a unique image label of a certain components type.
163  The components define how many channels are present in an image plane.
164 
165  With the Natron extension, planes encoded directly their components type along them. This is not the case with the
166  Nuke multi-plane suite V1 where components types and planes are hard-coded.
167 
168  To sum-up:
169 
170  - Planes defined in the Natron extension form indicate directly their components type, in the form
171 
172  kNatronOfxImageComponentsPlaneName + planeName +
173  <optional> kNatronOfxImageComponentsPlaneLabel + planeLabel +
174  <optional> kNatronOfxImageComponentsPlaneChannelsLabel + channelsLabel +
175  kNatronOfxImageComponentsPlaneChannel + channel1Name +
176  kNatronOfxImageComponentsPlaneChannel + channel2Name +
177  kNatronOfxImageComponentsPlaneChannel + channel3Name
178 
179 - Planes defined in the Nuke multi-plane suite have hard-coded components type (see Support for Nuke above)
180 
181 - The getClipComponents action must return a list of string corresponding to planes and not components type.
182 
183  MultiPlaneEffect:
184  ----------------
185 
186  To support abitrary planes, this suite needs to deal with dynamic choice parameter menus. This is known not to be supported in Nuke.
187 
188  The MultiPlaneEffect class below is mainly a helper class inheriting ImageEffect to conveniently create and manage choice parameters
189  that allow the user to select planes or a specific channel in a plane.
190 
191  The host notifies that the planes have changed for a plug-in by calling the instanceChanged action on the output clip.
192 
193  This is where the buildChannelMenus() function should be called to refresh choice parameters menus.
194 
195  Note that this class also supports multi-planar effects in the sense of the the Nuke multi-plane suite (i.e: only motion vectors and disparity planes)
196 
197  Note that the derived plug-in still needs to set the kFnOfxImageEffectPropMultiPlanar property to 1, otherwise this class will not do much.
198 
199  */
200 namespace OFX {
201 namespace MultiPlane {
202 
203 
204 /**
205  * @brief An ImagePlaneDesc represents an image plane and its components type.
206  * The plane is uniquely identified by its planeID, it is used internally to compare planes.
207  * The plane label is used for any UI related display: this is what the user sees.
208  * If empty, the plane label is the same as the planeID.
209  * The channels label is an optional string indicating in a more convenient way the types
210  * of components expressed by the channels e.g: Instead of having "XY" for motion vectors,
211  * they could be labeled with "Motion".
212  * If empty, the channels label is set to the concatenation of all channels.
213  * The channels are the unique identifier for each channel composing the plane.
214  * The plane can only be composed from 1 to 4 (included) channels.
215     **/
216 class ImagePlaneDesc
217 {
218     public:
219 
220 
221     ImagePlaneDesc();
222 
223     ImagePlaneDesc(const std::string& planeID,
224                    const std::string& planeLabel,
225                    const std::string& channelsLabel,
226                    const std::vector<std::string>& channels);
227 
228     ImagePlaneDesc(const std::string& planeID,
229                    const std::string& planeLabel,
230                    const std::string& channelsLabel,
231                    const char** channels,
232                    int count);
233 
234 
235     ImagePlaneDesc(const ImagePlaneDesc& other);
236 
237     ImagePlaneDesc& operator=(const ImagePlaneDesc& other);
238 
239     ~ImagePlaneDesc();
240 
241     // Is it Alpha, RGB or RGBA
242     bool isColorPlane() const;
243 
244     static bool isColorPlane(const std::string& layerID);
245 
246     /**
247      * @brief Returns the number of channels in this plane.
248      **/
249     int getNumComponents() const;
250 
251     /**
252      * @brief Returns the plane unique identifier. This should be used to compare ImagePlaneDesc together.
253      * This is not supposed to be used for display purpose, use getPlaneLabel() instead.
254      **/
255     const std::string& getPlaneID() const;
256 
257     /**
258      * @brief Returns the plane label.
259      * This is what is used to display to the user.
260      **/
261     const std::string& getPlaneLabel() const;
262 
263     /**
264      * @brief Returns the channels composing this plane.
265      **/
266     const std::vector<std::string>& getChannels() const;
267 
268     /**
269      * @brief Returns a label used to better represent the type of components used by this plane.
270      * e.g: "Motion" can be used to better label "XY" component types.
271      **/
272     const std::string& getChannelsLabel() const;
273 
274 
275     bool operator==(const ImagePlaneDesc& other) const;
276 
277     bool operator!=(const ImagePlaneDesc& other) const
278     {
279         return !(*this == other);
280     }
281 
282     // For std::map
283     bool operator<(const ImagePlaneDesc& other) const;
284 
285     operator bool() const
286     {
287         return getNumComponents() > 0;
288     }
289 
290     bool operator!() const
291     {
292         return getNumComponents() == 0;
293     }
294 
295 
296     void getPlaneOption(std::string* optionID, std::string* optionLabel) const;
297     void getChannelOption(int channelIndex, std::string* optionID, std::string* optionLabel) const;
298 
299     /**
300      * @brief Maps the given nComps to the color plane
301      **/
302     static const ImagePlaneDesc& mapNCompsToColorPlane(int nComps);
303 
304     /**
305      * @brief Maps the given OpenFX plane to a ImagePlaneDesc.
306      * @param ofxPlane Can be
307 
308      *  kFnOfxImagePlaneBackwardMotionVector
309      *  kFnOfxImagePlaneForwardMotionVector
310      *  kFnOfxImagePlaneStereoDisparityLeft
311      *  kFnOfxImagePlaneStereoDisparityRight
312      *  Or any plane encoded in the format specified by the Natron multi-plane extension.
313      * This function CANNOT be used to map the color plane, instead use mapNCompsToColorPlane.
314      *
315      * This function returns an empty plane desc upon failure.
316      **/
317     static ImagePlaneDesc mapOFXPlaneStringToPlane(const std::string& ofxPlane);
318 
319     /**
320      * @brief Maps OpenFX components string to a plane, optionnally also to a paired plane in the case of disparity/motion vectors.
321      * @param ofxComponents Must be a string between
322      * kOfxImageComponentRGBA, kOfxImageComponentRGB, kOfxImageComponentAlpha, kNatronOfxImageComponentXY, kOfxImageComponentNone
323      * or kFnOfxImageComponentStereoDisparity or kFnOfxImageComponentMotionVectors
324      * Or any plane encoded in the format specified by the Natron multi-plane extension.
325      **/
326     static void mapOFXComponentsTypeStringToPlanes(const std::string& ofxComponents, ImagePlaneDesc* plane, ImagePlaneDesc* pairedPlane);
327 
328     /**
329      * @brief Does the inverse of mapOFXPlaneStringToPlane, except that it can also be used for
330      * the color plane.
331      **/
332     static std::string mapPlaneToOFXPlaneString(const ImagePlaneDesc& plane);
333 
334     /**
335      * @brief Returns an OpenFX encoded string representing the components type of the plane.
336      * @returns One of the following strings:
337      * kOfxImageComponentRGBA, kOfxImageComponentRGB, kOfxImageComponentAlpha, kNatronOfxImageComponentXY, kOfxImageComponentNone
338      * or kFnOfxImageComponentStereoDisparity or kFnOfxImageComponentMotionVectors
339      * Or any plane encoded in the format specified by the Natron multi-plane extension.
340      **/
341     static std::string mapPlaneToOFXComponentsTypeString(const ImagePlaneDesc& plane);
342 
343     /**
344      * @brief Find a layer equivalent to this layer in the other layers container.
345      * ITERATOR must be either a std::vector<ImagePlaneDesc>::iterator or std::list<ImagePlaneDesc>::iterator
346      **/
347     template <typename ITERATOR>
findEquivalentLayer(const ImagePlaneDesc & layer,ITERATOR begin,ITERATOR end)348     static ITERATOR findEquivalentLayer(const ImagePlaneDesc& layer, ITERATOR begin, ITERATOR end)
349     {
350         bool isColor = layer.isColorPlane();
351 
352         ITERATOR foundExistingColorMatch = end;
353         ITERATOR foundExistingComponents = end;
354 
355         for (ITERATOR it = begin; it != end; ++it) {
356             if (it->isColorPlane() && isColor) {
357                 foundExistingColorMatch = it;
358             } else {
359                 if (*it == layer) {
360                     foundExistingComponents = it;
361                     break;
362                 }
363             }
364         } // for each output components
365 
366         if (foundExistingComponents != end) {
367             return foundExistingComponents;
368         } else if (foundExistingColorMatch != end) {
369             return foundExistingColorMatch;
370         } else {
371             return end;
372         }
373     } // findEquivalentLayer
374 
375     /*
376      * These are default presets image components
377      */
378     static const ImagePlaneDesc& getNoneComponents();
379     static const ImagePlaneDesc& getRGBAComponents();
380     static const ImagePlaneDesc& getRGBComponents();
381     static const ImagePlaneDesc& getAlphaComponents();
382     static const ImagePlaneDesc& getBackwardMotionComponents();
383     static const ImagePlaneDesc& getForwardMotionComponents();
384     static const ImagePlaneDesc& getDisparityLeftComponents();
385     static const ImagePlaneDesc& getDisparityRightComponents();
386     static const ImagePlaneDesc& getXYComponents();
387 
388 
389 private:
390     std::string _planeID, _planeLabel;
391     std::vector<std::string> _channels;
392     std::string _channelsLabel;
393 };
394 
395 
396 struct MultiPlaneEffectPrivate;
397 class MultiPlaneEffect
398     : public OFX::ImageEffect
399 {
400     auto_ptr<MultiPlaneEffectPrivate> _imp;
401 
402 public:
403 
404 
405     MultiPlaneEffect(OfxImageEffectHandle handle);
406 
407     virtual ~MultiPlaneEffect();
408 
409     struct FetchChoiceParamOptions
410     {
411         bool splitPlanesIntoChannelOptions;
412         bool addNoneOption;
413         bool addConstantOptions;
414         bool isOutputPlaneChoice;
415         bool hideIfClipDisconnected;
416         std::vector<OFX::Clip*> dependsClips;
417 
createFetchChoiceParamOptionsForInputChannelFetchChoiceParamOptions418         static FetchChoiceParamOptions createFetchChoiceParamOptionsForInputChannel()
419         {
420             FetchChoiceParamOptions ret;
421             ret.splitPlanesIntoChannelOptions = true;
422             ret.addNoneOption = false;
423             ret.addConstantOptions = true;
424             ret.isOutputPlaneChoice = false;
425             ret.hideIfClipDisconnected = false;
426             return ret;
427         }
428 
createFetchChoiceParamOptionsForOutputPlaneFetchChoiceParamOptions429         static FetchChoiceParamOptions createFetchChoiceParamOptionsForOutputPlane()
430         {
431             FetchChoiceParamOptions ret;
432             ret.splitPlanesIntoChannelOptions = false;
433             ret.addNoneOption = false;
434             ret.addConstantOptions = false;
435             ret.isOutputPlaneChoice = true;
436             ret.hideIfClipDisconnected = false;
437             return ret;
438         }
439     };
440 
441     /**
442      * @brief Fetch a dynamic choice parameter that was declared to the factory with
443      * describeInContextAddOutputLayerChoice() or describeInContextAddChannelChoice().
444      * @param splitPlanesIntoChannelOptions If true, each option will be a channel of a plane
445      * @param dependsClips The planes available from the given clips will be used to populate the choice options.
446      * @param isOutputPlaneChoice If this
447      **/
448     void fetchDynamicMultiplaneChoiceParameter(const std::string& paramName,
449                                                const FetchChoiceParamOptions& args);
450 
451     /**
452      * @brief Should be called by the implementation to refresh the visibility of parameters once they have all been fetched.
453      **/
454     void onAllParametersFetched();
455 
456     enum GetPlaneNeededRetCodeEnum
457     {
458         eGetPlaneNeededRetCodeFailed,
459         eGetPlaneNeededRetCodeReturnedPlane,
460         eGetPlaneNeededRetCodeReturnedChannelInPlane,
461         eGetPlaneNeededRetCodeReturnedConstant0,
462         eGetPlaneNeededRetCodeReturnedConstant1,
463         eGetPlaneNeededRetCodeReturnedAllPlanes
464     };
465 
466     /**
467      * @brief Returns the plane and channel index selected by the user in the given dynamic choice parameter "paramName".
468      * @param plane Contains in output the plane selected by the user
469      * If ofxPlane is empty but the function returned true that is because the choice is either kMultiPlaneParamOutputOption0 or kMultiPlaneParamOutputOption1
470      * ofxComponents will have been set correctly to one of these values.
471      *
472      * @param channelIndexInPlane Contains in output the selected channel index in the plane set to ofxPlane
473      *
474      * @returns eGetPlaneNeededRetCodeFailed if this function failed.
475      * If the result is eGetPlaneNeededRetCodeReturnedConstant0 or eGetPlaneNeededRetCodeReturnedConstant1 is returned, the plug-in should use
476      * a corresponding constant (0 or 1) instead of a channel from the clip.
477      * If the result is eGetPlaneNeededRetCodeReturnedPlane, the plane in output will be set to the user selected plane.
478      * If the result is eGetPlaneNeededRetCodeReturnedChannelInPlane, the plane and the channelIndexInPlane in output will be set to the user
479      * selected channel.
480      * If the result is eGetPlaneNeededRetCodeReturnedAllPlanes, the plug-in is expected to render what is requested and should act as
481      * plane agnostic.
482      **/
483     GetPlaneNeededRetCodeEnum getPlaneNeeded(const std::string& paramName,
484                                              OFX::Clip** clip,
485                                              ImagePlaneDesc* plane,
486                                              int* channelIndexInPlane);
487 
488 
489     /**
490      * @brief Must be called by derived class before anything else: it refreshes the channel menus.
491      * By default it also set the dst clip pixel components according to the output plane selector if there is any
492      **/
493     virtual void getClipPreferences(ClipPreferencesSetter &clipPreferences) OVERRIDE;
494 
495     /**
496      * @brief Set the requested planes according to the selectors that were registered for each clip.
497      * By default the pass-through clip is set to the first source clip encountered in the registered plane selectors
498      **/
499     virtual OfxStatus getClipComponents(const ClipComponentsArguments& args, ClipComponentsSetter& clipComponents) OVERRIDE;
500 
501     /**
502      * @brief Force a refresh of the channel selectors. This should in general not be called as this is done for you in changedClip() in Natron > 3
503      * or getClipPreferences for any other host.
504      **/
505     void refreshPlaneChoiceMenus();
506 
507     /**
508      * @brief Overriden to handle parameter changes. Derived class must call this class implementation.
509      **/
510     virtual void changedParam(const InstanceChangedArgs &args, const std::string &paramName) OVERRIDE;
511 
512     /**
513      * @brief Overriden to handle clip changes. Derived class must call this class implementation.
514      **/
515     virtual void changedClip(const InstanceChangedArgs &args, const std::string &clipName) OVERRIDE;
516 
517 
518 };
519 
520 namespace Factory {
521 
522 /**
523  * @brief Add a dynamic choice parameter to select a plane.
524  * This should only be called for effects that flag kFnOfxImageEffectPropMultiPlanar=1 and
525  * if the host supports multi-plane suite v2 and dynamic choice parameters.
526  **/
527 OFX::ChoiceParamDescriptor* describeInContextAddPlaneChoice(OFX::ImageEffectDescriptor &desc,
528                                                             OFX::PageParamDescriptor* page,
529                                                             const std::string& name,
530                                                             const std::string& label,
531                                                             const std::string& hint);
532 
533 
534 /**
535  * @brief Add a boolean parameter indicating if true that the plug-in is plane agnostic and will fetch in input the plane requested to render
536  * in output. Note that in this case any other plane choice parameter will be made secret.
537  **/
538 OFX::BooleanParamDescriptor* describeInContextAddAllPlanesOutputCheckbox(OFX::ImageEffectDescriptor &desc, OFX::PageParamDescriptor* page);
539 
540 /**
541  * @brief Add a dynamic choice parameter to select a channel among planes available in one or multiple source clips.
542  **/
543 OFX::ChoiceParamDescriptor* describeInContextAddPlaneChannelChoice(OFX::ImageEffectDescriptor &desc,
544                                                                    OFX::PageParamDescriptor* page,
545                                                                    const std::vector<std::string>& clips,
546                                                                    const std::string& name,
547                                                                    const std::string& label,
548                                                                    const std::string& hint,
549                                                                    bool addConstants = true);
550 
551 /**
552  * @brief Add the standard R,G,B,A choices for the given clips.
553  * @param addConstants If true, it will also add the "0" and "1" choice to the list
554  **/
555 void addInputChannelOptionsRGBA(OFX::ChoiceParamDescriptor* param,
556                                 const std::vector<std::string>& clips,
557                                 bool addConstants,
558                                 bool onlyColorPlane);
559 
560 
561 /**
562  * @brief Same as above, but for a choice param instance
563  **/
564 void addInputChannelOptionsRGBA(const std::vector<std::string>& clips,
565                                 bool addConstants,
566                                 bool onlyColorPlane);
567 }         // Factory
568 }     // namespace MultiPlane
569 } // namespace OFX
570 
571 
572 #endif /* defined(openfx_supportext_ofxsMultiPlane_h) */
573