1 /* ***** BEGIN LICENSE BLOCK *****
2  * This file is part of openfx-misc <https://github.com/devernay/openfx-misc>,
3  * Copyright (C) 2013-2018 INRIA
4  *
5  * openfx-misc is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * openfx-misc is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with openfx-misc.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>
17  * ***** END LICENSE BLOCK ***** */
18 
19 /*
20  * OFX TestGroups plugin.
21  */
22 
23 #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
24 #include <windows.h>
25 #endif
26 
27 #include <sstream> // stringstream
28 
29 #include "ofxsImageEffect.h"
30 #include "ofxsThreadSuite.h"
31 #include "ofxsMultiThread.h"
32 
33 #include "ofxsProcessing.H"
34 #include "ofxsMaskMix.h"
35 #include "ofxsCoords.h"
36 #include "ofxsCopier.h"
37 #include "ofxsMacros.h"
38 
39 using namespace OFX;
40 
41 OFXS_NAMESPACE_ANONYMOUS_ENTER
42 
43 #define kPluginName "TestGroupsOFX"
44 #define kPluginGrouping "Other/Test"
45 #define kPluginDescription \
46     "Test parameter groups. See https://github.com/MrKepzie/Natron/issues/521"
47 
48 #define kPluginIdentifier "net.sf.openfx.TestGroups"
49 #define kPluginVersionMajor 1 // Incrementing this number means that you have broken backwards compatibility of the plug-in.
50 #define kPluginVersionMinor 0 // Increment this when you have fixed a bug or made it faster.
51 
52 #define kSupportsMultipleClipPARs false
53 #define kSupportsMultipleClipDepths false
54 
55 #define kParamColor0      "color0"
56 #define kParamColor0Label "Color 0"
57 
58 #define kParamTestButton "testButton"
59 #define kParamTestButtonLabel "Click me!"
60 
61 #define kParamLabelString "labelString"
62 #define kParamDouble2 "double2"
63 
64 #define kParamClipInfo      "clipInfo"
65 #define kParamClipInfoLabel "Clip Info..."
66 #define kParamClipInfoHint  "Display information about the inputs"
67 
68 #define kParamForceCopy "forceCopy"
69 #define kParamForceCopyLabel "Force Copy"
70 #define kParamForceCopyHint "Force copy from input to output"
71 
72 // test setting double param properties on the instance
73 #define kParamDoubleTest "doubleTest"
74 #define kParamDoubleTestLabel "doubleTestLabel"
75 #define kParamDoubleTestHint "doubleTestHint"
76 #define kParamDoubleTestDefault "doubleTestDefault"
77 #define kParamDoubleTestMin "doubleTestMin"
78 #define kParamDoubleTestMax "doubleTestMax"
79 #define kParamDoubleTestDisplayMin "doubleTestDisplayMin"
80 #define kParamDoubleTestDisplayMax "doubleTestDisplayMax"
81 #define kParamOptionalClipLabel "optionalClipLabel"
82 #define kParamOptionalClipHint "optionalClipHint"
83 
84 #define kClipOptional "optional"
85 #define kClipOptionalLabel "Optional Clip"
86 
87 ////////////////////////////////////////////////////////////////////////////////
88 /** @brief The plugin that does our work */
89 class TestGroupsPlugin
90     : public ImageEffect
91 {
92 public:
93     /** @brief ctor */
TestGroupsPlugin(OfxImageEffectHandle handle)94     TestGroupsPlugin(OfxImageEffectHandle handle)
95         : ImageEffect(handle)
96         , _dstClip(NULL)
97         , _srcClip(NULL)
98         , _optionalClip(NULL)
99         , _maskClip(NULL)
100         , _testButton(NULL)
101         , _labelString(NULL)
102         , _double2(NULL)
103     {
104         _dstClip = fetchClip(kOfxImageEffectOutputClipName);
105         assert( _dstClip && (!_dstClip->isConnected() || _dstClip->getPixelComponents() == ePixelComponentRGB ||
106                              _dstClip->getPixelComponents() == ePixelComponentRGBA ||
107                              _dstClip->getPixelComponents() == ePixelComponentAlpha) );
108         _srcClip = getContext() == eContextGenerator ? NULL : fetchClip(kOfxImageEffectSimpleSourceClipName);
109         assert( (!_srcClip && getContext() == eContextGenerator) ||
110                 ( _srcClip && (!_srcClip->isConnected() || _srcClip->getPixelComponents() ==  ePixelComponentRGB ||
111                                _srcClip->getPixelComponents() == ePixelComponentRGBA ||
112                                _srcClip->getPixelComponents() == ePixelComponentAlpha) ) );
113         _optionalClip = fetchClip(kClipOptional);
114         _maskClip = fetchClip(getContext() == eContextPaint ? "Brush" : "Mask");
115         assert(!_maskClip || !_maskClip->isConnected() || _maskClip->getPixelComponents() == ePixelComponentAlpha);
116 
117         _color = fetchRGBAParam(kParamColor0);
118         assert(_color);
119 
120         _forceCopy = fetchBooleanParam(kParamForceCopy);
121         assert(_forceCopy);
122 
123         _mix = fetchDoubleParam(kParamMix);
124         _maskApply = ( ofxsMaskIsAlwaysConnected( OFX::getImageEffectHostDescription() ) && paramExists(kParamMaskApply) ) ? fetchBooleanParam(kParamMaskApply) : 0;
125         _maskInvert = fetchBooleanParam(kParamMaskInvert);
126         assert(_mix && _maskInvert);
127 
128         _testButton = fetchPushButtonParam(kParamTestButton);
129         _labelString = fetchStringParam(kParamLabelString);
130         _double2 = fetchDoubleParam(kParamDouble2);
131         _doubleTest = fetchDoubleParam(kParamDoubleTest);
132         _doubleTestLabel = fetchStringParam(kParamDoubleTestLabel);
133         _doubleTestHint = fetchStringParam(kParamDoubleTestHint);
134         _doubleTestDefault = fetchDoubleParam(kParamDoubleTestDefault);
135         _doubleTestMin = fetchDoubleParam(kParamDoubleTestMin);
136         _doubleTestMax = fetchDoubleParam(kParamDoubleTestMax);
137         _doubleTestDisplayMin = fetchDoubleParam(kParamDoubleTestDisplayMin);
138         _doubleTestDisplayMax = fetchDoubleParam(kParamDoubleTestDisplayMax);
139         _optionalClipLabel = fetchStringParam(kParamOptionalClipLabel);
140         _optionalClipHint = fetchStringParam(kParamOptionalClipHint);
141     }
142 
143 private:
144     virtual void render(const RenderArguments &args) OVERRIDE FINAL;
145     virtual bool isIdentity(const IsIdentityArguments &args, Clip * &identityClip, double &identityTime, int& view, std::string& plane) OVERRIDE FINAL;
146     virtual void changedParam(const InstanceChangedArgs &args, const std::string &paramName) OVERRIDE FINAL;
147 
148 private:
149     // do not need to delete these, the ImageEffect is managing them for us
150     Clip *_dstClip;
151     Clip *_srcClip;
152     Clip *_optionalClip;
153     Clip *_maskClip;
154     RGBAParam* _color;
155     BooleanParam *_forceCopy;
156     DoubleParam* _mix;
157     BooleanParam* _maskApply;
158     BooleanParam* _maskInvert;
159     PushButtonParam* _testButton;
160     StringParam* _labelString;
161     DoubleParam* _double2;
162     DoubleParam* _doubleTest;
163     StringParam* _doubleTestLabel;
164     StringParam* _doubleTestHint;
165     DoubleParam* _doubleTestDefault;
166     DoubleParam* _doubleTestMin;
167     DoubleParam* _doubleTestMax;
168     DoubleParam* _doubleTestDisplayMin;
169     DoubleParam* _doubleTestDisplayMax;
170     StringParam* _optionalClipLabel;
171     StringParam* _optionalClipHint;
172 };
173 
174 
175 // the overridden render function
176 void
render(const RenderArguments & args)177 TestGroupsPlugin::render(const RenderArguments &args)
178 {
179     const double time = args.time;
180     bool forceCopy;
181 
182     _forceCopy->getValueAtTime(time, forceCopy);
183 
184 #ifdef DEBUG
185     if (!forceCopy) {
186         setPersistentMessage(Message::eMessageError, "", "OFX Host should not render");
187         throwSuiteStatusException(kOfxStatFailed);
188     }
189 #endif
190 
191     assert( kSupportsMultipleClipPARs   || !_srcClip || _srcClip->getPixelAspectRatio() == _dstClip->getPixelAspectRatio() );
192     assert( kSupportsMultipleClipDepths || !_srcClip || _srcClip->getPixelDepth()       == _dstClip->getPixelDepth() );
193     // do the rendering
194     auto_ptr<Image> dst( _dstClip->fetchImage(args.time) );
195     if ( !dst.get() ) {
196         throwSuiteStatusException(kOfxStatFailed);
197     }
198     if ( (dst->getRenderScale().x != args.renderScale.x) ||
199          ( dst->getRenderScale().y != args.renderScale.y) ||
200          ( ( dst->getField() != eFieldNone) /* for DaVinci Resolve */ && ( dst->getField() != args.fieldToRender) ) ) {
201         setPersistentMessage(Message::eMessageError, "", "OFX Host gave image with wrong scale or field properties");
202         throwSuiteStatusException(kOfxStatFailed);
203     }
204     BitDepthEnum dstBitDepth       = dst->getPixelDepth();
205     PixelComponentEnum dstComponents  = dst->getPixelComponents();
206     auto_ptr<const Image> src( ( _srcClip && _srcClip->isConnected() ) ?
207                                     _srcClip->fetchImage(args.time) : 0 );
208     if ( src.get() ) {
209         if ( (src->getRenderScale().x != args.renderScale.x) ||
210              ( src->getRenderScale().y != args.renderScale.y) ||
211              ( ( src->getField() != eFieldNone) /* for DaVinci Resolve */ && ( src->getField() != args.fieldToRender) ) ) {
212             setPersistentMessage(Message::eMessageError, "", "OFX Host gave image with wrong scale or field properties");
213             throwSuiteStatusException(kOfxStatFailed);
214         }
215         BitDepthEnum srcBitDepth      = src->getPixelDepth();
216         PixelComponentEnum srcComponents = src->getPixelComponents();
217         if ( (srcBitDepth != dstBitDepth) || (srcComponents != dstComponents) ) {
218             throwSuiteStatusException(kOfxStatErrImageFormat);
219         }
220     }
221     copyPixels( *this, args.renderWindow, src.get(), dst.get() );
222 }
223 
224 bool
isIdentity(const IsIdentityArguments & args,Clip * & identityClip,double &,int &,std::string &)225 TestGroupsPlugin::isIdentity(const IsIdentityArguments &args,
226                              Clip * &identityClip,
227                              double & /*identityTime*/
228                              , int& /*view*/, std::string& /*plane*/)
229 {
230     if (!_srcClip || !_srcClip->isConnected()) {
231         return false;
232     }
233     const double time = args.time;
234     bool forceCopy;
235     _forceCopy->getValueAtTime(time, forceCopy);
236 
237     if (!forceCopy) {
238         identityClip = _srcClip;
239 
240         return true;
241     }
242 
243     double mix;
244     _mix->getValueAtTime(args.time, mix);
245 
246     if (mix == 0.) {
247         identityClip = _srcClip;
248 
249         return true;
250     }
251 
252     bool doMasking = ( ( !_maskApply || _maskApply->getValueAtTime(args.time) ) && _maskClip && _maskClip->isConnected() );
253     if (doMasking) {
254         bool maskInvert;
255         _maskInvert->getValueAtTime(args.time, maskInvert);
256         if (!maskInvert) {
257             OfxRectI maskRoD;
258             if (getImageEffectHostDescription()->supportsMultiResolution) {
259                 // In Sony Catalyst Edit, clipGetRegionOfDefinition returns the RoD in pixels instead of canonical coordinates.
260                 // In hosts that do not support multiResolution (e.g. Sony Catalyst Edit), all inputs have the same RoD anyway.
261                 Coords::toPixelEnclosing(_maskClip->getRegionOfDefinition(args.time), args.renderScale, _maskClip->getPixelAspectRatio(), &maskRoD);
262                 // effect is identity if the renderWindow doesn't intersect the mask RoD
263                 if ( !Coords::rectIntersection<OfxRectI>(args.renderWindow, maskRoD, 0) ) {
264                     identityClip = _srcClip;
265 
266                     return true;
267                 }
268             }
269         }
270     }
271 
272     return false;
273 }
274 
275 static const char*
bitDepthString(BitDepthEnum bitDepth)276 bitDepthString(BitDepthEnum bitDepth)
277 {
278     switch (bitDepth) {
279     case eBitDepthUByte:
280 
281         return "8u";
282     case eBitDepthUShort:
283 
284         return "16u";
285     case eBitDepthHalf:
286 
287         return "16f";
288     case eBitDepthFloat:
289 
290         return "32f";
291     case eBitDepthCustom:
292 
293         return "x";
294     case eBitDepthNone:
295 
296         return "0";
297 #ifdef OFX_EXTENSIONS_VEGAS
298     case eBitDepthUByteBGRA:
299 
300         return "8uBGRA";
301     case eBitDepthUShortBGRA:
302 
303         return "16uBGRA";
304     case eBitDepthFloatBGRA:
305 
306         return "32fBGRA";
307 #endif
308     default:
309 
310         return "[unknown bit depth]";
311     }
312 }
313 
314 static std::string
pixelComponentString(const std::string & p)315 pixelComponentString(const std::string& p)
316 {
317     const std::string prefix = "OfxImageComponent";
318 
319     std::string s = p;
320 
321     return s.replace(s.find(prefix), prefix.length(), "");
322 }
323 
324 static const char*
premultString(PreMultiplicationEnum e)325 premultString(PreMultiplicationEnum e)
326 {
327     switch (e) {
328     case eImageOpaque:
329 
330         return "Opaque";
331     case eImagePreMultiplied:
332 
333         return "PreMultiplied";
334     case eImageUnPreMultiplied:
335 
336         return "UnPreMultiplied";
337     default:
338 
339         return "[unknown premult]";
340     }
341 }
342 
343 #ifdef OFX_EXTENSIONS_VEGAS
344 static const char*
pixelOrderString(PixelOrderEnum e)345 pixelOrderString(PixelOrderEnum e)
346 {
347     switch (e) {
348     case ePixelOrderRGBA:
349 
350         return "RGBA";
351     case ePixelOrderBGRA:
352 
353         return "BGRA";
354     default:
355 
356         return "[unknown pixel order]";
357     }
358 }
359 
360 #endif
361 
362 static const char*
fieldOrderString(FieldEnum e)363 fieldOrderString(FieldEnum e)
364 {
365     switch (e) {
366     case eFieldNone:
367 
368         return "None";
369     case eFieldBoth:
370 
371         return "Both";
372     case eFieldLower:
373 
374         return "Lower";
375     case eFieldUpper:
376 
377         return "Upper";
378     case eFieldSingle:
379 
380         return "Single";
381     case eFieldDoubled:
382 
383         return "Doubled";
384     default:
385 
386         return "[unknown field order]";
387     }
388 }
389 
390 void
changedParam(const InstanceChangedArgs & args,const std::string & paramName)391 TestGroupsPlugin::changedParam(const InstanceChangedArgs &args,
392                                const std::string &paramName)
393 {
394     const double time = args.time;
395 
396     if (paramName == kParamTestButton) {
397         _testButton->setLabel("Clicked!");
398         _testButton->setHint("You clicked me!");
399         _labelString->setValue("New Label");
400         _labelString->setLabel("New labellabel");
401         _double2->setLabel("Double param got a new name");
402     } else if (paramName == kParamClipInfo) {
403         std::ostringstream oss;
404         oss << "Clip Info:\n\n";
405         oss << "Input: ";
406         if (!_srcClip) {
407             oss << "N/A";
408         } else {
409             Clip &c = *_srcClip;
410             oss << pixelComponentString( c.getPixelComponentsProperty() );
411             oss << bitDepthString( c.getPixelDepth() );
412             oss << " (unmapped: ";
413             oss << pixelComponentString( c.getUnmappedPixelComponentsProperty() );
414             oss << bitDepthString( c.getUnmappedPixelDepth() );
415             oss << ")\npremultiplication: ";
416             oss << premultString( c.getPreMultiplication() );
417 #ifdef OFX_EXTENSIONS_VEGAS
418             oss << "\npixel order: ";
419             oss << pixelOrderString( c.getPixelOrder() );
420 #endif
421             oss << "\nfield order: ";
422             oss << fieldOrderString( c.getFieldOrder() );
423             oss << "\n";
424             oss << (c.isConnected() ? "connected" : "not connected");
425             oss << "\n";
426             oss << (c.hasContinuousSamples() ? "continuous samples" : "discontinuous samples");
427 #ifdef OFX_EXTENSIONS_NATRON
428             oss << "\nformat: ";
429             OfxRectI format;
430             c.getFormat(format);
431             oss << format.x2 - format.x1 << 'x' << format.y2 - format.y1;
432             if ( (format.x1 != 0) && (format.y1 != 0) ) {
433                 if (format.x1 < 0) {
434                     oss << format.x1;
435                 } else {
436                     oss << '+' << format.x1;
437                 }
438                 if (format.y1 < 0) {
439                     oss << format.y1;
440                 } else {
441                     oss << '+' << format.y1;
442                 }
443             }
444 #endif
445             oss << "\npixel aspect ratio: ";
446             oss << c.getPixelAspectRatio();
447             oss << "\nframe rate: ";
448             oss << c.getFrameRate();
449             oss << " (unmapped: ";
450             oss << c.getUnmappedFrameRate();
451             oss << ")";
452             OfxRangeD range = c.getFrameRange();
453             oss << "\nframe range: ";
454             oss << range.min << "..." << range.max;
455             oss << " (unmapped: ";
456             range = c.getUnmappedFrameRange();
457             oss << range.min << "..." << range.max;
458             oss << ")";
459             oss << "\nregion of definition: ";
460             OfxRectD rod = c.getRegionOfDefinition(args.time);
461             oss << rod.x1 << ' ' << rod.y1 << ' ' << rod.x2 << ' ' << rod.y2;
462         }
463         oss << "\n\n";
464         oss << "Output: ";
465         if (!_dstClip) {
466             oss << "N/A";
467         } else {
468             Clip &c = *_dstClip;
469             oss << pixelComponentString( c.getPixelComponentsProperty() );
470             oss << bitDepthString( c.getPixelDepth() );
471             oss << " (unmapped: ";
472             oss << pixelComponentString( c.getUnmappedPixelComponentsProperty() );
473             oss << bitDepthString( c.getUnmappedPixelDepth() );
474             oss << ")\npremultiplication: ";
475             oss << premultString( c.getPreMultiplication() );
476 #ifdef OFX_EXTENSIONS_VEGAS
477             oss << "\npixel order: ";
478             oss << pixelOrderString( c.getPixelOrder() );
479 #endif
480             oss << "\nfield order: ";
481             oss << fieldOrderString( c.getFieldOrder() );
482             oss << "\n";
483             oss << (c.isConnected() ? "connected" : "not connected");
484             oss << "\n";
485             oss << (c.hasContinuousSamples() ? "continuous samples" : "discontinuous samples");
486 #ifdef OFX_EXTENSIONS_NATRON
487             oss << "\nformat: ";
488             OfxRectI format;
489             c.getFormat(format);
490             oss << format.x2 - format.x1 << 'x' << format.y2 - format.y1;
491             if ( (format.x1 != 0) && (format.y1 != 0) ) {
492                 if (format.x1 < 0) {
493                     oss << format.x1;
494                 } else {
495                     oss << '+' << format.x1;
496                 }
497                 if (format.y1 < 0) {
498                     oss << format.y1;
499                 } else {
500                     oss << '+' << format.y1;
501                 }
502             }
503 #endif
504             oss << "\npixel aspect ratio: ";
505             oss << c.getPixelAspectRatio();
506             oss << "\nframe rate: ";
507             oss << c.getFrameRate();
508             oss << " (unmapped: ";
509             oss << c.getUnmappedFrameRate();
510             oss << ")";
511             OfxRangeD range = c.getFrameRange();
512             oss << "\nframe range: ";
513             oss << range.min << "..." << range.max;
514             oss << " (unmapped: ";
515             range = c.getUnmappedFrameRange();
516             oss << range.min << "..." << range.max;
517             oss << ")";
518             oss << "\nregion of definition: ";
519             OfxRectD rod = c.getRegionOfDefinition(args.time);
520             oss << rod.x1 << ' ' << rod.y1 << ' ' << rod.x2 << ' ' << rod.y2;
521         }
522         oss << "\n\n";
523         oss << "time: " << args.time << ", renderscale: " << args.renderScale.x << 'x' << args.renderScale.y << '\n';
524 
525         sendMessage( Message::eMessageMessage, "", oss.str() );
526     } else if (paramName == kParamDoubleTestLabel) {
527         std::string s;
528         _doubleTestLabel->getValueAtTime(time, s);
529         _doubleTest->setLabel(s);
530     } else if (paramName == kParamDoubleTestHint) {
531         std::string s;
532         _doubleTestHint->getValueAtTime(time, s);
533         _doubleTest->setHint(s);
534     } else if (paramName == kParamDoubleTestDefault) {
535         _doubleTest->setDefault( _doubleTestDefault->getValueAtTime(time) );
536     } else if ( (paramName == kParamDoubleTestMin) || (paramName == kParamDoubleTestMax) ) {
537         _doubleTest->setRange( _doubleTestMin->getValueAtTime(time), _doubleTestMax->getValueAtTime(time) );
538     } else if ( (paramName == kParamDoubleTestDisplayMin) || (paramName == kParamDoubleTestMax) ) {
539         _doubleTest->setDisplayRange( _doubleTestDisplayMin->getValueAtTime(time), _doubleTestDisplayMax->getValueAtTime(time) );
540     } else if (paramName == kParamOptionalClipLabel) {
541         std::string s;
542         _optionalClipLabel->getValueAtTime(time, s);
543         _optionalClip->setLabel(s);
544     } else if (paramName == kParamOptionalClipHint) {
545         std::string s;
546         _optionalClipHint->getValueAtTime(time, s);
547         _optionalClip->setHint(s);
548     }
549 } // TestGroupsPlugin::changedParam
550 
551 mDeclarePluginFactory(TestGroupsPluginFactory, {ofxsThreadSuiteCheck();}, {});
552 void
describe(ImageEffectDescriptor & desc)553 TestGroupsPluginFactory::describe(ImageEffectDescriptor &desc)
554 {
555     // basic labels
556     desc.setLabel(kPluginName);
557     desc.setPluginGrouping(kPluginGrouping);
558     desc.setPluginDescription(kPluginDescription);
559 
560     // add the supported contexts
561     desc.addSupportedContext(eContextFilter);
562     desc.addSupportedContext(eContextGeneral);
563     desc.addSupportedContext(eContextPaint);
564     desc.addSupportedContext(eContextGenerator);
565 
566     // add supported pixel depths
567     desc.addSupportedBitDepth(eBitDepthUByte);
568     desc.addSupportedBitDepth(eBitDepthUShort);
569     desc.addSupportedBitDepth(eBitDepthFloat);
570 
571     // set a few flags
572     desc.setSupportsMultipleClipPARs(kSupportsMultipleClipPARs);
573     desc.setSupportsMultipleClipDepths(kSupportsMultipleClipDepths);
574 
575     desc.setRenderThreadSafety(eRenderFullySafe);
576 }
577 
578 void
describeInContext(ImageEffectDescriptor & desc,ContextEnum context)579 TestGroupsPluginFactory::describeInContext(ImageEffectDescriptor &desc,
580                                            ContextEnum context)
581 {
582     // Source clip only in the filter context
583     // create the mandated source clip
584     ClipDescriptor *srcClip = desc.defineClip(kOfxImageEffectSimpleSourceClipName);
585 
586     srcClip->addSupportedComponent(ePixelComponentRGBA);
587     srcClip->addSupportedComponent(ePixelComponentRGB);
588     srcClip->addSupportedComponent(ePixelComponentAlpha);
589     srcClip->setTemporalClipAccess(false);
590     srcClip->setIsMask(false);
591 
592     {
593         ClipDescriptor *clip = desc.defineClip(kClipOptional);
594         clip->setLabel(kClipOptionalLabel);
595         clip->addSupportedComponent(ePixelComponentRGBA);
596         clip->setOptional(true);
597     }
598 
599     // create the mandated output clip
600     ClipDescriptor *dstClip = desc.defineClip(kOfxImageEffectOutputClipName);
601     dstClip->addSupportedComponent(ePixelComponentRGBA);
602     dstClip->addSupportedComponent(ePixelComponentRGB);
603     dstClip->addSupportedComponent(ePixelComponentAlpha);
604 
605     if (context != eContextGenerator) {
606         ClipDescriptor *maskClip = (context == eContextPaint) ? desc.defineClip("Brush") : desc.defineClip("Mask");
607         maskClip->addSupportedComponent(ePixelComponentAlpha);
608         maskClip->setTemporalClipAccess(false);
609         if (context != eContextPaint) {
610             maskClip->setOptional(true);
611         }
612         maskClip->setIsMask(true);
613     }
614 
615     // make some pages and to things in
616     //PageParamDescriptor *page = NULL;
617     PageParamDescriptor *page = desc.definePageParam("Controls");
618 
619     // color0
620     {
621         RGBAParamDescriptor *param = desc.defineRGBAParam(kParamColor0);
622         param->setLabel(kParamColor0Label);
623         param->setDefault(0.0, 1.0, 1.0, 1.0);
624         param->setAnimates(true); // can animate
625         if (page) {
626             page->addChild(*param);
627         }
628     }
629 
630     // forceCopy
631     {
632         BooleanParamDescriptor *param = desc.defineBooleanParam(kParamForceCopy);
633         param->setLabel(kParamForceCopyLabel);
634         param->setHint(kParamForceCopyHint);
635         param->setDefault(false);
636         param->setAnimates(false);
637         if (page) {
638             page->addChild(*param);
639         }
640     }
641 
642     // clipInfo
643     {
644         PushButtonParamDescriptor *param = desc.definePushButtonParam(kParamClipInfo);
645         param->setLabel(kParamClipInfoLabel);
646         param->setHint(kParamClipInfoHint);
647         if (page) {
648             page->addChild(*param);
649         }
650     }
651 
652     // testButton
653     {
654         PushButtonParamDescriptor *param = desc.definePushButtonParam(kParamTestButton);
655         param->setLabel(kParamTestButtonLabel);
656         param->setHint("Please click me and see what happens.");
657         if (page) {
658             page->addChild(*param);
659         }
660     }
661 
662     // a string param of type label, followed by a double param with no label... does it work?
663     {
664         StringParamDescriptor *param = desc.defineStringParam(kParamLabelString);
665         param->setLabel("");
666         param->setDefault("The label");
667         param->setStringType(eStringTypeLabel);
668         param->setLayoutHint(eLayoutHintNoNewLine, 1);
669         if (page) {
670             page->addChild(*param);
671         }
672     }
673     {
674         DoubleParamDescriptor *param = desc.defineDoubleParam("doubleParam");
675         param->setLabel("");
676         param->setDefault(0.5);
677         param->setRange(0., 1.);
678         param->setDisplayRange(0., 1.);
679         if (page) {
680             page->addChild(*param);
681         }
682     }
683     {
684         DoubleParamDescriptor *param = desc.defineDoubleParam(kParamDouble2);
685         param->setLabel("A Double Param");
686         param->setDefault(0.5);
687         param->setRange(0., 1.);
688         param->setDisplayRange(0., 1.);
689         if (page) {
690             page->addChild(*param);
691         }
692     }
693 
694     {
695         DoubleParamDescriptor *param = desc.defineDoubleParam(kParamDoubleTest);
696         if (page) {
697             page->addChild(*param);
698         }
699     }
700     {
701         StringParamDescriptor *param = desc.defineStringParam(kParamDoubleTestLabel);
702         param->setAnimates(false);
703         param->setEvaluateOnChange(false);
704         if (page) {
705             page->addChild(*param);
706         }
707     }
708     {
709         StringParamDescriptor *param = desc.defineStringParam(kParamDoubleTestHint);
710         param->setAnimates(false);
711         param->setEvaluateOnChange(false);
712         if (page) {
713             page->addChild(*param);
714         }
715     }
716     {
717         DoubleParamDescriptor *param = desc.defineDoubleParam(kParamDoubleTestDefault);
718         param->setAnimates(false);
719         param->setEvaluateOnChange(false);
720         if (page) {
721             page->addChild(*param);
722         }
723     }
724     {
725         DoubleParamDescriptor *param = desc.defineDoubleParam(kParamDoubleTestMin);
726         param->setAnimates(false);
727         param->setEvaluateOnChange(false);
728         if (page) {
729             page->addChild(*param);
730         }
731     }
732     {
733         DoubleParamDescriptor *param = desc.defineDoubleParam(kParamDoubleTestMax);
734         param->setAnimates(false);
735         param->setEvaluateOnChange(false);
736         if (page) {
737             page->addChild(*param);
738         }
739     }
740     {
741         DoubleParamDescriptor *param = desc.defineDoubleParam(kParamDoubleTestDisplayMin);
742         param->setAnimates(false);
743         param->setEvaluateOnChange(false);
744         if (page) {
745             page->addChild(*param);
746         }
747     }
748     {
749         DoubleParamDescriptor *param = desc.defineDoubleParam(kParamDoubleTestDisplayMax);
750         param->setAnimates(false);
751         param->setEvaluateOnChange(false);
752         if (page) {
753             page->addChild(*param);
754         }
755     }
756     {
757         StringParamDescriptor *param = desc.defineStringParam(kParamOptionalClipLabel);
758         param->setAnimates(false);
759         param->setEvaluateOnChange(false);
760         if (page) {
761             page->addChild(*param);
762         }
763     }
764     {
765         StringParamDescriptor *param = desc.defineStringParam(kParamOptionalClipHint);
766         param->setAnimates(false);
767         param->setEvaluateOnChange(false);
768         if (page) {
769             page->addChild(*param);
770         }
771     }
772 
773     // Groups
774     {
775         GroupParamDescriptor* group = desc.defineGroupParam("group");
776         //group->setAsTab();
777         {
778             GroupParamDescriptor* subgroup = desc.defineGroupParam("subGroup1");
779             if (subgroup) {
780                 if (group) {
781                     subgroup->setParent(*group);
782                 }
783                 if (page) {
784                     page->addChild(*subgroup);
785                 }
786             }
787             {
788                 DoubleParamDescriptor* param = desc.defineDoubleParam("valueInsideSubGroup1");
789                 if (page) {
790                     page->addChild(*param);
791                 }
792                 if (subgroup) {
793                     param->setParent(*subgroup);
794                 }
795             }
796         }
797         {
798             GroupParamDescriptor* subgroup = desc.defineGroupParam("subGroup2AsTab");
799             if (subgroup) {
800                 subgroup->setAsTab();
801                 if (group) {
802                     subgroup->setParent(*group);
803                 }
804                 if (page) {
805                     page->addChild(*subgroup);
806                 }
807             }
808             {
809                 DoubleParamDescriptor* param = desc.defineDoubleParam("valueInsideSubGroup2AsTab");
810                 if (page) {
811                     page->addChild(*param);
812                 }
813                 if (subgroup) {
814                     param->setParent(*subgroup);
815                 }
816             }
817         }
818         {
819             GroupParamDescriptor* subgroup = desc.defineGroupParam("subGroup3AsTab");
820             if (subgroup) {
821                 subgroup->setAsTab();
822                 if (group) {
823                     subgroup->setParent(*group);
824                 }
825                 if (page) {
826                     page->addChild(*subgroup);
827                 }
828             }
829             {
830                 DoubleParamDescriptor* param = desc.defineDoubleParam("valueInsideSubGroup3AsTab");
831                 if (page) {
832                     page->addChild(*param);
833                 }
834                 if (subgroup) {
835                     param->setParent(*subgroup);
836                 }
837             }
838         }
839     }
840     GroupParamDescriptor* formatGroup = desc.defineGroupParam( "kParamFormatGroup" );
841     GroupParamDescriptor* videoGroup  = desc.defineGroupParam( "kParamVideoGroup" );
842     formatGroup->setLabel( "Format" );
843     videoGroup->setLabel( "Video" );
844 
845     formatGroup->setAsTab( );
846     videoGroup->setAsTab( );
847 
848     /// FORMAT PARAMETERS
849     //avtranscoder::FormatContext formatContext( AV_OPT_FLAG_DECODING_PARAM );
850     //avtranscoder::OptionArray formatOptions = formatContext.getOptions();
851     //common::addOptionsToGroup( desc, formatGroup, formatOptions, common::kPrefixFormat );
852     {
853         ParamDescriptor* param = NULL;
854         BooleanParamDescriptor* boolParam = desc.defineBooleanParam( "opt1" );
855         boolParam->setDefault( true );
856         param = boolParam;
857         param->setLabel( "Opt1" );
858         param->setHint( "Opt1 help" );
859         param->setParent( *formatGroup );
860     }
861 
862     {
863         ParamDescriptor* param = NULL;
864         IntParamDescriptor* intParam = desc.defineIntParam( "int" );
865         param = intParam;
866         param->setLabel( "Int1" );
867         param->setHint( "Int1 help" );
868         param->setParent( *formatGroup );
869     }
870 
871     GroupParamDescriptor* formatDetailledGroup = desc.defineGroupParam( "kParamFormatDetailledGroup" );
872     formatDetailledGroup->setLabel( "Detailled" );
873     formatDetailledGroup->setAsTab( );
874     formatDetailledGroup->setParent( *formatGroup );
875 
876     //avtranscoder::OptionArrayMap formatDetailledGroupOptions = avtranscoder::getOutputFormatOptions();
877     //common::addOptionsToGroup( desc, formatDetailledGroup, formatDetailledGroupOptions, common::kPrefixFormat );
878     {
879         ParamDescriptor* param = NULL;
880         BooleanParamDescriptor* boolParam = desc.defineBooleanParam( "opt2" );
881         boolParam->setDefault( true );
882         param = boolParam;
883         param->setLabel( "Opt2" );
884         param->setHint( "Opt2 help" );
885         param->setParent( *formatDetailledGroup );
886     }
887 
888     /// VIDEO PARAMETERS
889     BooleanParamDescriptor* useCustomSAR = desc.defineBooleanParam( "kParamUseCustomSAR" );
890     useCustomSAR->setLabel( "Override SAR" );
891     useCustomSAR->setDefault( false );
892     useCustomSAR->setHint( "Override the file SAR (Storage Aspect Ratio) with a custom SAR value." );
893     useCustomSAR->setParent( *videoGroup );
894 
895     DoubleParamDescriptor* customSAR = desc.defineDoubleParam( "kParamCustomSAR" );
896     customSAR->setLabel( "Custom SAR" );
897     customSAR->setDefault( 1.0 );
898     customSAR->setRange( 0., 10. );
899     customSAR->setDisplayRange( 0., 3. );
900     customSAR->setHint( "Choose a custom value to override the file SAR (Storage Aspect Ratio). Maximum value: 10." );
901     customSAR->setParent( *videoGroup );
902 
903     IntParamDescriptor* streamIndex = desc.defineIntParam( "kParamVideoStreamIndex" );
904     streamIndex->setLabel( "kParamVideoStreamIndexLabel" );
905     streamIndex->setDefault( 0 );
906     streamIndex->setRange( 0., 100. );
907     streamIndex->setDisplayRange( 0., 16. );
908     streamIndex->setHint( "Choose a custom value to decode the video stream you want. Maximum value: 100." );
909     streamIndex->setParent( *videoGroup );
910 
911     GroupParamDescriptor* videoDetailledGroup  = desc.defineGroupParam( "kParamVideoDetailledGroup" );
912     videoDetailledGroup->setLabel( "Detailled" );
913     videoDetailledGroup->setAsTab( );
914     videoDetailledGroup->setParent( *videoGroup );
915 
916     //avtranscoder::OptionArrayMap videoDetailledGroupOptions =  avtranscoder::getVideoCodecOptions();
917     //common::addOptionsToGroup( desc, videoDetailledGroup, videoDetailledGroupOptions, common::kPrefixVideo );
918     {
919         ParamDescriptor* param = NULL;
920         BooleanParamDescriptor* boolParam = desc.defineBooleanParam( "opt3" );
921         boolParam->setDefault( true );
922         param = boolParam;
923         param->setLabel( "Op3" );
924         param->setHint( "Opt3 help" );
925         param->setParent( *videoDetailledGroup );
926     }
927 
928     /// VERBOSE
929     BooleanParamDescriptor* useVerbose = desc.defineBooleanParam( "kParamVerbose" );
930     useVerbose->setLabel( "Set to verbose" );
931     useVerbose->setDefault( false );
932     useVerbose->setHint( "Set plugin to verbose to get debug informations." );
933 
934     ofxsMaskMixDescribeParams(desc, page);
935 } // TestGroupsPluginFactory::describeInContext
936 
937 ImageEffect*
createInstance(OfxImageEffectHandle handle,ContextEnum)938 TestGroupsPluginFactory::createInstance(OfxImageEffectHandle handle,
939                                         ContextEnum /*context*/)
940 {
941     return new TestGroupsPlugin(handle);
942 }
943 
944 static TestGroupsPluginFactory p(kPluginIdentifier, kPluginVersionMajor, kPluginVersionMinor);
945 mRegisterPluginFactoryInstance(p)
946 
947 OFXS_NAMESPACE_ANONYMOUS_EXIT
948