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  * OFX Generator plug-in helper
22  */
23 
24 #include "ofxsGenerator.h"
25 
26 #include <cmath>
27 #include <cfloat> // DBL_MAX
28 #include <cassert>
29 #include <limits>
30 
31 #include "ofxsFormatResolution.h"
32 #include "ofxsCoords.h"
33 
34 // Some hosts (e.g. Resolve) may not support normalized defaults (setDefaultCoordinateSystem(eCoordinatesNormalised))
35 #define kParamDefaultsNormalised "defaultsNormalisedGenerator"
36 
37 #ifdef OFX_EXTENSIONS_NATRON
38 #define OFX_COMPONENTS_OK(c) ((c)== ePixelComponentAlpha || (c) == ePixelComponentXY || (c) == ePixelComponentRGB || (c) == ePixelComponentRGBA)
39 #else
40 #define OFX_COMPONENTS_OK(c) ((c)== ePixelComponentAlpha || (c) == ePixelComponentRGB || (c) == ePixelComponentRGBA)
41 #endif
42 
43 
44 using namespace OFX;
45 
46 using std::string;
47 
48 static bool gHostSupportsDefaultCoordinateSystem = true; // for kParamDefaultsNormalised
49 
GeneratorPlugin(OfxImageEffectHandle handle,bool useOutputComponentsAndDepth,bool supportsBitDepthByte,bool supportsBitDepthUShort,bool supportsBitDepthHalf,bool supportsBitDepthFloat)50 GeneratorPlugin::GeneratorPlugin(OfxImageEffectHandle handle,
51                                  bool useOutputComponentsAndDepth,
52                                  bool supportsBitDepthByte,
53                                  bool supportsBitDepthUShort,
54                                  bool supportsBitDepthHalf,
55                                  bool supportsBitDepthFloat)
56     : ImageEffect(handle)
57     , _dstClip(NULL)
58     , _extent(NULL)
59     , _format(NULL)
60     , _formatSize(NULL)
61     , _formatPar(NULL)
62     , _reformat(NULL)
63     , _btmLeft(NULL)
64     , _size(NULL)
65     , _interactive(NULL)
66     , _outputComponents(NULL)
67     , _outputBitDepth(NULL)
68     , _range(NULL)
69     , _recenter(NULL)
70     , _useOutputComponentsAndDepth(useOutputComponentsAndDepth)
71     , _supportsByte(supportsBitDepthByte)
72     , _supportsUShort(supportsBitDepthUShort)
73     , _supportsHalf(supportsBitDepthHalf)
74     , _supportsFloat(supportsBitDepthFloat)
75     , _supportsRGBA(false)
76     , _supportsRGB(false)
77     , _supportsXY(false)
78     , _supportsAlpha(false)
79 {
80     _dstClip = fetchClip(kOfxImageEffectOutputClipName);
81     assert( _dstClip && (!_dstClip->isConnected() || OFX_COMPONENTS_OK(_dstClip->getPixelComponents())) );
82 
83     _extent = fetchChoiceParam(kParamGeneratorExtent);
84     _format = fetchChoiceParam(kParamGeneratorFormat);
85     _formatSize = fetchInt2DParam(kParamGeneratorSize);
86     _formatPar = fetchDoubleParam(kParamGeneratorPAR);
87     if ( paramExists(kParamGeneratorReformat) ) {
88         _reformat = fetchBooleanParam(kParamGeneratorReformat);
89     }
90     _btmLeft = fetchDouble2DParam(kParamRectangleInteractBtmLeft);
91     _size = fetchDouble2DParam(kParamRectangleInteractSize);
92     _recenter = fetchPushButtonParam(kParamGeneratorCenter);
93     _interactive = fetchBooleanParam(kParamRectangleInteractInteractive);
94     assert(_extent && _format && _formatSize && _formatPar && _btmLeft && _size && _interactive && _recenter);
95 
96     if (_useOutputComponentsAndDepth) {
97         _outputComponents = fetchChoiceParam(kParamGeneratorOutputComponents);
98 
99         if (getImageEffectHostDescription()->supportsMultipleClipDepths) {
100             _outputBitDepth = fetchChoiceParam(kParamGeneratorOutputBitDepth);
101         }
102     }
103     if (getContext() == eContextGeneral) {
104         _range   = fetchInt2DParam(kParamGeneratorRange);
105         assert(_range);
106     }
107 
108     // kOfxImageEffectPropSupportedPixelDepths is not a property of the effect instance
109     // (only the host and the plugin descriptor)
110     {
111         int i = 0;
112         if ( _supportsFloat && getImageEffectHostDescription()->supportsBitDepth(eBitDepthFloat) ) {
113             _outputBitDepthMap[i] = eBitDepthFloat;
114             ++i;
115         }
116         if ( _supportsHalf && getImageEffectHostDescription()->supportsBitDepth(eBitDepthHalf) ) {
117             _outputBitDepthMap[i] = eBitDepthHalf;
118             ++i;
119         }
120         if ( _supportsUShort && getImageEffectHostDescription()->supportsBitDepth(eBitDepthUShort) ) {
121             _outputBitDepthMap[i] = eBitDepthUShort;
122             ++i;
123         }
124         if ( _supportsByte && getImageEffectHostDescription()->supportsBitDepth(eBitDepthUByte) ) {
125             _outputBitDepthMap[i] = eBitDepthUByte;
126             ++i;
127         }
128         _outputBitDepthMap[i] = eBitDepthNone;
129     }
130 
131     const PropertySet &dstClipProps = _dstClip->getPropertySet();
132     int numComponents = dstClipProps.propGetDimension(kOfxImageEffectPropSupportedComponents);
133     for (int i = 0; i < numComponents; ++i) {
134         PixelComponentEnum pixelComponents = mapStrToPixelComponentEnum( dstClipProps.propGetString(kOfxImageEffectPropSupportedComponents, i) );
135         bool supported = getImageEffectHostDescription()->supportsPixelComponent(pixelComponents);
136         switch (pixelComponents) {
137         case ePixelComponentRGBA:
138             _supportsRGBA  = supported;
139             break;
140         case ePixelComponentRGB:
141             _supportsRGB = supported;
142             break;
143 #ifdef OFX_EXTENSIONS_NATRON
144        case ePixelComponentXY:
145             _supportsXY = supported;
146             break;
147 #endif
148         case ePixelComponentAlpha:
149             _supportsAlpha = supported;
150             break;
151         default:
152             // other components are not supported by this plugin
153             break;
154         }
155     }
156     int i = 0;
157     if (_supportsRGBA) {
158         _outputComponentsMap[i] = ePixelComponentRGBA;
159         ++i;
160     }
161     if (_supportsRGB) {
162         _outputComponentsMap[i] = ePixelComponentRGB;
163         ++i;
164     }
165     if (_supportsXY) {
166         _outputComponentsMap[i] = ePixelComponentXY;
167         ++i;
168     }
169     if (_supportsAlpha) {
170         _outputComponentsMap[i] = ePixelComponentAlpha;
171         ++i;
172     }
173     _outputComponentsMap[i] = ePixelComponentNone;
174 
175     // honor kParamDefaultsNormalised
176     if ( paramExists(kParamDefaultsNormalised) ) {
177         // Some hosts (e.g. Resolve) may not support normalized defaults (setDefaultCoordinateSystem(eCoordinatesNormalised))
178         // handle these ourselves!
179         BooleanParam* param = fetchBooleanParam(kParamDefaultsNormalised);
180         assert(param);
181         bool normalised = param->getValue();
182         if (normalised) {
183             OfxPointD size = getProjectExtent();
184             OfxPointD origin = getProjectOffset();
185             OfxPointD p;
186             // we must denormalise all parameters for which setDefaultCoordinateSystem(eCoordinatesNormalised) couldn't be done
187             beginEditBlock(kParamDefaultsNormalised);
188             p = _btmLeft->getValue();
189             _btmLeft->setValue(p.x * size.x + origin.x, p.y * size.y + origin.y);
190             p = _size->getValue();
191             _size->setValue(p.x * size.x, p.y * size.y);
192             param->setValue(false);
193             endEditBlock();
194         }
195     }
196 
197     // finally
198     GeneratorPlugin::syncPrivateData();
199 }
200 
201 void
checkComponents(BitDepthEnum dstBitDepth,PixelComponentEnum dstComponents)202 GeneratorPlugin::checkComponents(BitDepthEnum dstBitDepth,
203                                  PixelComponentEnum dstComponents)
204 {
205     if (!_useOutputComponentsAndDepth) {
206         return;
207     }
208 
209     // get the components of _dstClip
210     PixelComponentEnum outputComponents = _outputComponentsMap[_outputComponents->getValue()];
211     if (dstComponents != outputComponents) {
212         setPersistentMessage(Message::eMessageError, "", "OFX Host dit not take into account output components");
213         throwSuiteStatusException(kOfxStatErrImageFormat);
214 
215         return;
216     }
217 
218     if (getImageEffectHostDescription()->supportsMultipleClipDepths) {
219         // get the bitDepth of _dstClip
220         BitDepthEnum outputBitDepth = _outputBitDepthMap[_outputBitDepth->getValue()];
221         if (dstBitDepth != outputBitDepth) {
222             setPersistentMessage(Message::eMessageError, "", "OFX Host dit not take into account output bit depth");
223             throwSuiteStatusException(kOfxStatErrImageFormat);
224 
225             return;
226         }
227     }
228 
229     clearPersistentMessage();
230 }
231 
232 /* override the time domain action, only for the general context */
233 bool
getTimeDomain(OfxRangeD & range)234 GeneratorPlugin::getTimeDomain(OfxRangeD &range)
235 {
236     // this should only be called in the general context, ever!
237     if (getContext() == eContextGeneral) {
238         assert(_range);
239         // how many frames on the input clip
240         //OfxRangeD srcRange = _srcClip->getFrameRange();
241 
242         int min, max;
243         _range->getValue(min, max);
244         range.min = min;
245         range.max = max;
246 
247         return true;
248     }
249 
250     return false;
251 }
252 
253 void
updateParamsVisibility()254 GeneratorPlugin::updateParamsVisibility()
255 {
256     GeneratorExtentEnum extent = (GeneratorExtentEnum)_extent->getValue();
257     bool hasFormat = (extent == eGeneratorExtentFormat);
258     bool hasSize = (extent == eGeneratorExtentSize);
259 
260     _format->setIsSecretAndDisabled(!hasFormat);
261     if (_reformat) {
262         _reformat->setIsSecretAndDisabled(!hasSize);
263     }
264     _size->setIsSecretAndDisabled(!hasSize);
265     _recenter->setIsSecretAndDisabled(!hasSize);
266     _btmLeft->setIsSecretAndDisabled(!hasSize);
267     _interactive->setIsSecretAndDisabled(!hasSize);
268 }
269 
270 void
changedParam(const InstanceChangedArgs & args,const string & paramName)271 GeneratorPlugin::changedParam(const InstanceChangedArgs &args,
272                               const string &paramName)
273 {
274     if ( (paramName == kParamGeneratorExtent) && (args.reason == eChangeUserEdit) ) {
275         updateParamsVisibility();
276     } else if (paramName == kParamGeneratorFormat) {
277         //the host does not handle the format itself, do it ourselves
278         EParamFormat format = (EParamFormat)_format->getValue();
279         int w = 0, h = 0;
280         double par = -1;
281         getFormatResolution(format, &w, &h, &par);
282         assert(par != -1);
283         _formatPar->setValue(par);
284         _formatSize->setValue(w, h);
285     } else if (paramName == kParamGeneratorCenter) {
286         Clip* srcClip = getSrcClip();
287         OfxRectD srcRoD;
288         if ( srcClip && srcClip->isConnected() ) {
289             srcRoD = srcClip->getRegionOfDefinition(args.time);
290         } else {
291             OfxPointD siz = getProjectSize();
292             OfxPointD off = getProjectOffset();
293             srcRoD.x1 = off.x;
294             srcRoD.x2 = off.x + siz.x;
295             srcRoD.y1 = off.y;
296             srcRoD.y2 = off.y + siz.y;
297         }
298         OfxPointD center;
299         center.x = (srcRoD.x2 + srcRoD.x1) / 2.;
300         center.y = (srcRoD.y2 + srcRoD.y1) / 2.;
301 
302         OfxRectD rectangle;
303         _size->getValue(rectangle.x2, rectangle.y2);
304         _btmLeft->getValue(rectangle.x1, rectangle.y1);
305         rectangle.x2 += rectangle.x1;
306         rectangle.y2 += rectangle.y1;
307 
308         OfxRectD newRectangle;
309         newRectangle.x1 = center.x - (rectangle.x2 - rectangle.x1) / 2.;
310         newRectangle.y1 = center.y - (rectangle.y2 - rectangle.y1) / 2.;
311         newRectangle.x2 = newRectangle.x1 + (rectangle.x2 - rectangle.x1);
312         newRectangle.y2 = newRectangle.y1 + (rectangle.y2 - rectangle.y1);
313 
314         _size->setValue(newRectangle.x2 - newRectangle.x1, newRectangle.y2 - newRectangle.y1);
315         _btmLeft->setValue(newRectangle.x1, newRectangle.y1);
316     }
317 }
318 
319 bool
getRegionOfDefinition(double time,OfxRectD & rod)320 GeneratorPlugin::getRegionOfDefinition(double time, OfxRectD &rod)
321 {
322     GeneratorExtentEnum extent = (GeneratorExtentEnum)_extent->getValue();
323 
324     switch (extent) {
325     case eGeneratorExtentFormat: {
326         int w, h;
327         _formatSize->getValueAtTime(time, w, h);
328         double par;
329         _formatPar->getValueAtTime(time, par);
330         OfxRectI pixelFormat;
331         pixelFormat.x1 = pixelFormat.y1 = 0;
332         pixelFormat.x2 = w;
333         pixelFormat.y2 = h;
334         OfxPointD renderScale = {1., 1.};
335         Coords::toCanonical(pixelFormat, renderScale, par, &rod);
336 
337         return true;
338     }
339     case eGeneratorExtentSize: {
340         _size->getValueAtTime(time, rod.x2, rod.y2);
341         _btmLeft->getValueAtTime(time, rod.x1, rod.y1);
342         rod.x2 += rod.x1;
343         rod.y2 += rod.y1;
344 
345         return true;
346     }
347     case eGeneratorExtentProject: {
348         OfxPointD siz = getProjectSize();
349         OfxPointD off = getProjectOffset();
350         rod.x1 = off.x;
351         rod.x2 = off.x + siz.x;
352         rod.y1 = off.y;
353         rod.y2 = off.y + siz.y;
354 
355         return true;
356     }
357     case eGeneratorExtentDefault:
358 
359         return false;
360     }
361 
362     return false;
363 }
364 
365 void
getClipPreferences(ClipPreferencesSetter & clipPreferences)366 GeneratorPlugin::getClipPreferences(ClipPreferencesSetter &clipPreferences)
367 {
368     double par = 0.;
369     GeneratorExtentEnum extent = (GeneratorExtentEnum)_extent->getValue();
370 
371     switch (extent) {
372     case eGeneratorExtentFormat: {
373         //specific output format
374         par = _formatPar->getValue();
375         break;
376     }
377     case eGeneratorExtentProject:
378     case eGeneratorExtentDefault: {
379         /// this should be the default value given by the host, no need to set it.
380         /// @see Instance::setupClipPreferencesArgs() in HostSupport, it should have
381         /// the line:
382         /// double inputPar = getProjectPixelAspectRatio();
383 
384         par = getProjectPixelAspectRatio();
385         break;
386     }
387     case eGeneratorExtentSize:
388         // only set the format if btmLeft and size are not animated
389         if ( _reformat && _reformat->getValue() &&
390              _btmLeft->getNumKeys() == 0 &&
391              _size->getNumKeys() == 0 ) {
392             par = 1;
393         }
394         break;
395     }
396 
397     if (_useOutputComponentsAndDepth) {
398         // set the components of _dstClip
399         PixelComponentEnum outputComponents = _outputComponentsMap[_outputComponents->getValue()];
400         clipPreferences.setClipComponents(*_dstClip, outputComponents);
401 
402         if (getImageEffectHostDescription()->supportsMultipleClipDepths) {
403             // set the bitDepth of _dstClip
404             BitDepthEnum outputBitDepth = _outputBitDepthMap[_outputBitDepth->getValue()];
405             clipPreferences.setClipBitDepth(*_dstClip, outputBitDepth);
406         }
407     }
408     if (par != 0.) {
409         clipPreferences.setPixelAspectRatio(*_dstClip, par);
410 #ifdef OFX_EXTENSIONS_NATRON
411         OfxRectD rod;
412         // get the format from time = 0
413         if ( getRegionOfDefinition(0, rod) ) { // don't set format if default
414             OfxRectI format;
415             const OfxPointD rsOne = {1., 1.};
416             Coords::toPixelNearest(rod, rsOne, par, &format);
417             clipPreferences.setOutputFormat(format);
418         }
419 #endif
420     }
421 }
422 
GeneratorInteract(OfxInteractHandle handle,ImageEffect * effect)423 GeneratorInteract::GeneratorInteract(OfxInteractHandle handle,
424                                      ImageEffect* effect)
425     : RectangleInteract(handle, effect)
426     , _extent(NULL)
427     , _extentValue(eGeneratorExtentDefault)
428 {
429     _extent = effect->fetchChoiceParam(kParamGeneratorExtent);
430     assert(_extent);
431 }
432 
433 void
aboutToCheckInteractivity(OfxTime)434 GeneratorInteract::aboutToCheckInteractivity(OfxTime /*time*/)
435 {
436     _extentValue = (GeneratorExtentEnum)_extent->getValue();
437 }
438 
439 bool
allowTopLeftInteraction() const440 GeneratorInteract::allowTopLeftInteraction() const
441 {
442     return _extentValue == eGeneratorExtentSize;
443 }
444 
445 bool
allowBtmRightInteraction() const446 GeneratorInteract::allowBtmRightInteraction() const
447 {
448     return _extentValue == eGeneratorExtentSize;
449 }
450 
451 bool
allowBtmLeftInteraction() const452 GeneratorInteract::allowBtmLeftInteraction() const
453 {
454     return _extentValue == eGeneratorExtentSize;
455 }
456 
457 bool
allowBtmMidInteraction() const458 GeneratorInteract::allowBtmMidInteraction() const
459 {
460     return _extentValue == eGeneratorExtentSize;
461 }
462 
463 bool
allowMidLeftInteraction() const464 GeneratorInteract::allowMidLeftInteraction() const
465 {
466     return _extentValue == eGeneratorExtentSize;
467 }
468 
469 bool
allowCenterInteraction() const470 GeneratorInteract::allowCenterInteraction() const
471 {
472     return _extentValue == eGeneratorExtentSize;
473 }
474 
475 bool
draw(const DrawArgs & args)476 GeneratorInteract::draw(const DrawArgs &args)
477 {
478     GeneratorExtentEnum extent = (GeneratorExtentEnum)_extent->getValue();
479 
480     if (extent != eGeneratorExtentSize) {
481         return false;
482     }
483 
484     return RectangleInteract::draw(args);
485 }
486 
487 bool
penMotion(const PenArgs & args)488 GeneratorInteract::penMotion(const PenArgs &args)
489 {
490     GeneratorExtentEnum extent = (GeneratorExtentEnum)_extent->getValue();
491 
492     if (extent != eGeneratorExtentSize) {
493         return false;
494     }
495 
496     return RectangleInteract::penMotion(args);
497 }
498 
499 bool
penDown(const PenArgs & args)500 GeneratorInteract::penDown(const PenArgs &args)
501 {
502     GeneratorExtentEnum extent = (GeneratorExtentEnum)_extent->getValue();
503 
504     if (extent != eGeneratorExtentSize) {
505         return false;
506     }
507 
508     return RectangleInteract::penDown(args);
509 }
510 
511 bool
penUp(const PenArgs & args)512 GeneratorInteract::penUp(const PenArgs &args)
513 {
514     GeneratorExtentEnum extent = (GeneratorExtentEnum)_extent->getValue();
515 
516     if (extent != eGeneratorExtentSize) {
517         return false;
518     }
519 
520     return RectangleInteract::penUp(args);
521 }
522 
523 void
loseFocus(const FocusArgs & args)524 GeneratorInteract::loseFocus(const FocusArgs &args)
525 {
526     return RectangleInteract::loseFocus(args);
527 }
528 
529 bool
keyDown(const KeyArgs & args)530 GeneratorInteract::keyDown(const KeyArgs &args)
531 {
532     GeneratorExtentEnum extent = (GeneratorExtentEnum)_extent->getValue();
533 
534     if (extent != eGeneratorExtentSize) {
535         return false;
536     }
537 
538     return RectangleInteract::keyDown(args);
539 }
540 
541 bool
keyUp(const KeyArgs & args)542 GeneratorInteract::keyUp(const KeyArgs & args)
543 {
544     GeneratorExtentEnum extent = (GeneratorExtentEnum)_extent->getValue();
545 
546     if (extent != eGeneratorExtentSize) {
547         return false;
548     }
549 
550     return RectangleInteract::keyUp(args);
551 }
552 
553 namespace OFX {
554 void
generatorDescribe(ImageEffectDescriptor & desc)555 generatorDescribe(ImageEffectDescriptor &desc)
556 {
557     desc.setOverlayInteractDescriptor(new GeneratorOverlayDescriptor);
558 }
559 
560 void
generatorDescribeInContext(PageParamDescriptor * page,ImageEffectDescriptor & desc,ClipDescriptor & dstClip,GeneratorExtentEnum defaultType,PixelComponentEnum defaultComponents,bool useOutputComponentsAndDepth,ContextEnum context,bool reformat)561 generatorDescribeInContext(PageParamDescriptor *page,
562                            ImageEffectDescriptor &desc,
563                            ClipDescriptor &dstClip,
564                            GeneratorExtentEnum defaultType,
565                            PixelComponentEnum defaultComponents, // either RGBA, RGB, XY or Alpha
566                            bool useOutputComponentsAndDepth,
567                            ContextEnum context,
568                            bool reformat)
569 {
570     // extent
571     {
572         ChoiceParamDescriptor* param = desc.defineChoiceParam(kParamGeneratorExtent);
573         param->setLabel(kParamGeneratorExtentLabel);
574         param->setHint(kParamGeneratorExtentHint);
575         assert(param->getNOptions() == eGeneratorExtentFormat);
576         param->appendOption(kParamGeneratorExtentOptionFormat);
577         assert(param->getNOptions() == eGeneratorExtentSize);
578         param->appendOption(kParamGeneratorExtentOptionSize);
579         assert(param->getNOptions() == eGeneratorExtentProject);
580         param->appendOption(kParamGeneratorExtentOptionProject);
581         assert(param->getNOptions() == eGeneratorExtentDefault);
582         param->appendOption(kParamGeneratorExtentOptionDefault);
583         param->setDefault( (int)defaultType );
584         param->setLayoutHint(eLayoutHintNoNewLine);
585         param->setAnimates(false);
586         desc.addClipPreferencesSlaveParam(*param);
587         if (page) {
588             page->addChild(*param);
589         }
590     }
591 
592     // recenter
593     {
594         PushButtonParamDescriptor* param = desc.definePushButtonParam(kParamGeneratorCenter);
595         param->setLabel(kParamGeneratorCenterLabel);
596         param->setHint(kParamGeneratorCenterHint);
597         param->setLayoutHint(eLayoutHintNoNewLine);
598         if (page) {
599             page->addChild(*param);
600         }
601     }
602 
603 #ifdef OFX_EXTENSIONS_NATRON
604     // reformat
605     if (reformat && getImageEffectHostDescription()->isNatron) {
606         BooleanParamDescriptor* param = desc.defineBooleanParam(kParamGeneratorReformat);
607         param->setLabel(kParamGeneratorReformatLabel);
608         param->setHint(kParamGeneratorReformatHint);
609         param->setLayoutHint(eLayoutHintNoNewLine);
610         param->setAnimates(false);
611         desc.addClipPreferencesSlaveParam(*param);
612         if (page) {
613             page->addChild(*param);
614         }
615     }
616 #endif
617 
618     // format
619     {
620         ChoiceParamDescriptor* param = desc.defineChoiceParam(kParamGeneratorFormat);
621         param->setLabel(kParamGeneratorFormatLabel);
622         assert(param->getNOptions() == eParamFormatPCVideo);
623         param->appendOption(kParamFormatPCVideoLabel, "", kParamFormatPCVideo);
624         assert(param->getNOptions() == eParamFormatNTSC);
625         param->appendOption(kParamFormatNTSCLabel, "", kParamFormatNTSC);
626         assert(param->getNOptions() == eParamFormatPAL);
627         param->appendOption(kParamFormatPALLabel, "", kParamFormatPAL);
628         assert(param->getNOptions() == eParamFormatNTSC169);
629         param->appendOption(kParamFormatNTSC169Label, "", kParamFormatNTSC169);
630         assert(param->getNOptions() == eParamFormatPAL169);
631         param->appendOption(kParamFormatPAL169Label, "", kParamFormatPAL169);
632         assert(param->getNOptions() == eParamFormatHD720);
633         param->appendOption(kParamFormatHD720Label, "", kParamFormatHD720);
634         assert(param->getNOptions() == eParamFormatHD);
635         param->appendOption(kParamFormatHDLabel, "", kParamFormatHD);
636         assert(param->getNOptions() == eParamFormatUHD4K);
637         param->appendOption(kParamFormatUHD4KLabel, "", kParamFormatUHD4K);
638         assert(param->getNOptions() == eParamFormat1kSuper35);
639         param->appendOption(kParamFormat1kSuper35Label, "", kParamFormat1kSuper35);
640         assert(param->getNOptions() == eParamFormat1kCinemascope);
641         param->appendOption(kParamFormat1kCinemascopeLabel, "", kParamFormat1kCinemascope);
642         assert(param->getNOptions() == eParamFormat2kSuper35);
643         param->appendOption(kParamFormat2kSuper35Label, "", kParamFormat2kSuper35);
644         assert(param->getNOptions() == eParamFormat2kCinemascope);
645         param->appendOption(kParamFormat2kCinemascopeLabel, "", kParamFormat2kCinemascope);
646         assert(param->getNOptions() == eParamFormat2kDCP);
647         param->appendOption(kParamFormat2kDCPLabel, "", kParamFormat2kDCP);
648         assert(param->getNOptions() == eParamFormat4kSuper35);
649         param->appendOption(kParamFormat4kSuper35Label, "", kParamFormat4kSuper35);
650         assert(param->getNOptions() == eParamFormat4kCinemascope);
651         param->appendOption(kParamFormat4kCinemascopeLabel, "", kParamFormat4kCinemascope);
652         assert(param->getNOptions() == eParamFormat4kDCP);
653         param->appendOption(kParamFormat4kDCPLabel, "", kParamFormat4kDCP);
654         assert(param->getNOptions() == eParamFormatSquare256);
655         param->appendOption(kParamFormatSquare256Label, "", kParamFormatSquare256);
656         assert(param->getNOptions() == eParamFormatSquare512);
657         param->appendOption(kParamFormatSquare512Label, "", kParamFormatSquare512);
658         assert(param->getNOptions() == eParamFormatSquare1k);
659         param->appendOption(kParamFormatSquare1kLabel, "", kParamFormatSquare1k);
660         assert(param->getNOptions() == eParamFormatSquare2k);
661         param->appendOption(kParamFormatSquare2kLabel, "", kParamFormatSquare2k);
662         param->setDefault(eParamFormatPCVideo);
663         param->setHint(kParamGeneratorFormatHint);
664         param->setAnimates(false);
665         desc.addClipPreferencesSlaveParam(*param);
666         if (page) {
667             page->addChild(*param);
668         }
669     }
670 
671     {
672         int w = 0, h = 0;
673         double par = -1.;
674         getFormatResolution(eParamFormatPCVideo, &w, &h, &par);
675         assert(par != -1);
676         {
677             Int2DParamDescriptor* param = desc.defineInt2DParam(kParamGeneratorSize);
678             param->setLabel(kParamGeneratorSizeLabel);
679             param->setHint(kParamGeneratorSizeHint);
680             param->setIsSecretAndDisabled(true);
681             param->setDefault(w, h);
682             param->setAnimates(false); // does not animate, because we set format
683             desc.addClipPreferencesSlaveParam(*param);
684             if (page) {
685                 page->addChild(*param);
686             }
687         }
688 
689         {
690             DoubleParamDescriptor* param = desc.defineDoubleParam(kParamGeneratorPAR);
691             param->setLabel(kParamGeneratorPARLabel);
692             param->setHint(kParamGeneratorPARHint);
693             param->setIsSecretAndDisabled(true);
694             param->setRange(0., DBL_MAX);
695             param->setDisplayRange(0.5, 2.);
696             param->setDefault(par);
697             param->setAnimates(false); // does not animate, because we set format
698             desc.addClipPreferencesSlaveParam(*param);
699             if (page) {
700                 page->addChild(*param);
701             }
702         }
703     }
704 
705     // btmLeft
706     {
707         Double2DParamDescriptor* param = desc.defineDouble2DParam(kParamRectangleInteractBtmLeft);
708         param->setLabel(kParamRectangleInteractBtmLeftLabel);
709         param->setDoubleType(eDoubleTypeXYAbsolute);
710         if ( param->supportsDefaultCoordinateSystem() ) {
711             param->setDefaultCoordinateSystem(eCoordinatesNormalised); // no need of kParamDefaultsNormalised
712         } else {
713             gHostSupportsDefaultCoordinateSystem = false; // no multithread here, see kParamDefaultsNormalised
714         }
715         param->setDefault(0., 0.);
716         param->setRange(-DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
717         param->setDisplayRange(-10000, -10000, 10000, 10000); // Resolve requires display range or values are clamped to (-1,1)
718         param->setIncrement(1.);
719         param->setLayoutHint(eLayoutHintNoNewLine);
720         param->setHint("Coordinates of the bottom left corner of the size rectangle.");
721         param->setDigits(0);
722         // This parameter *can* be animated, because it only sets the format if "Reformat" is checked
723         //param->setAnimates(false); // does not animate, because we set format
724         desc.addClipPreferencesSlaveParam(*param);
725         if (page) {
726             page->addChild(*param);
727         }
728     }
729 
730     // size
731     {
732         Double2DParamDescriptor* param = desc.defineDouble2DParam(kParamRectangleInteractSize);
733         param->setLabel(kParamRectangleInteractSizeLabel);
734         param->setDoubleType(eDoubleTypeXY);
735         if ( param->supportsDefaultCoordinateSystem() ) {
736             param->setDefaultCoordinateSystem(eCoordinatesNormalised); // no need of kParamDefaultsNormalised
737         } else {
738             gHostSupportsDefaultCoordinateSystem = false; // no multithread here, see kParamDefaultsNormalised
739         }
740         param->setDefault(1., 1.);
741         param->setRange(0., 0., DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
742         param->setDisplayRange(0, 0, 10000, 10000); // Resolve requires display range or values are clamped to (-1,1)
743         param->setIncrement(1.);
744         param->setDimensionLabels(kParamRectangleInteractSizeDim1, kParamRectangleInteractSizeDim2);
745         param->setHint("Width and height of the size rectangle.");
746         param->setIncrement(1.);
747         param->setDigits(0);
748         // This parameter *can* be animated, because it only sets the format if "Reformat" is checked
749         //param->setAnimates(false); // does not animate, because we set format
750         desc.addClipPreferencesSlaveParam(*param);
751         if (page) {
752             page->addChild(*param);
753         }
754     }
755 
756 
757     // interactive
758     {
759         BooleanParamDescriptor* param = desc.defineBooleanParam(kParamRectangleInteractInteractive);
760         param->setLabel(kParamRectangleInteractInteractiveLabel);
761         param->setHint(kParamRectangleInteractInteractiveHint);
762         param->setEvaluateOnChange(false);
763         if (page) {
764             page->addChild(*param);
765         }
766     }
767 
768     // range
769     if (context == eContextGeneral) {
770         Int2DParamDescriptor *param = desc.defineInt2DParam(kParamGeneratorRange);
771         param->setLabel(kParamGeneratorRangeLabel);
772         param->setHint(kParamGeneratorRangeHint);
773         param->setDefault(1, 1);
774         param->setDimensionLabels("min", "max");
775         param->setAnimates(false); // can not animate, because it defines the time domain
776         if (page) {
777             page->addChild(*param);
778         }
779     }
780 
781     if (useOutputComponentsAndDepth) {
782         bool supportsByte  = false;
783         bool supportsUShort = false;
784         bool supportsHalf = false;
785         bool supportsFloat = false;
786         BitDepthEnum outputBitDepthMap[10];
787         const PropertySet &effectProps = desc.getPropertySet();
788         int numPixelDepths = effectProps.propGetDimension(kOfxImageEffectPropSupportedPixelDepths);
789         for (int i = 0; i < numPixelDepths; ++i) {
790             BitDepthEnum pixelDepth = mapStrToBitDepthEnum( effectProps.propGetString(kOfxImageEffectPropSupportedPixelDepths, i) );
791             bool supported = getImageEffectHostDescription()->supportsBitDepth(pixelDepth);
792             switch (pixelDepth) {
793             case eBitDepthUByte:
794                 supportsByte  = supported;
795                 break;
796             case eBitDepthUShort:
797                 supportsUShort = supported;
798                 break;
799             case eBitDepthHalf:
800                 supportsHalf = supported;
801                 break;
802             case eBitDepthFloat:
803                 supportsFloat = supported;
804                 break;
805             default:
806                 // other bitdepths are not supported by this plugin
807                 break;
808             }
809         }
810         {
811             int i = 0;
812             if (supportsFloat) {
813                 outputBitDepthMap[i] = eBitDepthFloat;
814                 ++i;
815             }
816             if (supportsHalf) {
817                 outputBitDepthMap[i] = eBitDepthHalf;
818                 ++i;
819             }
820             if (supportsUShort) {
821                 outputBitDepthMap[i] = eBitDepthUShort;
822                 ++i;
823             }
824             if (supportsByte) {
825                 outputBitDepthMap[i] = eBitDepthUByte;
826                 ++i;
827             }
828             outputBitDepthMap[i] = eBitDepthNone;
829         }
830 
831         {
832             bool supportsRGBA   = false;
833             bool supportsRGB    = false;
834             bool supportsXY     = false;
835             bool supportsAlpha  = false;
836             PixelComponentEnum outputComponentsMap[10];
837             const PropertySet &dstClipProps = dstClip.getPropertySet();
838             int numComponents = dstClipProps.propGetDimension(kOfxImageEffectPropSupportedComponents);
839             for (int i = 0; i < numComponents; ++i) {
840                 PixelComponentEnum pixelComponents = mapStrToPixelComponentEnum( dstClipProps.propGetString(kOfxImageEffectPropSupportedComponents, i) );
841                 bool supported = getImageEffectHostDescription()->supportsPixelComponent(pixelComponents);
842                 switch (pixelComponents) {
843                 case ePixelComponentRGBA:
844                     supportsRGBA  = supported;
845                     break;
846                 case ePixelComponentRGB:
847                     supportsRGB = supported;
848                     break;
849 #ifdef OFX_EXTENSIONS_NATRON
850                 case ePixelComponentXY:
851                     supportsXY = supported;
852                     break;
853 #endif
854                 case ePixelComponentAlpha:
855                     supportsAlpha = supported;
856                     break;
857                 default:
858                     // other components are not supported by this plugin
859                     break;
860                 }
861             }
862             {
863                 int i = 0;
864                 if (supportsRGBA) {
865                     outputComponentsMap[i] = ePixelComponentRGBA;
866                     ++i;
867                 }
868                 if (supportsRGB) {
869                     outputComponentsMap[i] = ePixelComponentRGB;
870                     ++i;
871                 }
872                 if (supportsXY) {
873                     outputComponentsMap[i] = ePixelComponentXY;
874                     ++i;
875                 }
876                 if (supportsAlpha) {
877                     outputComponentsMap[i] = ePixelComponentAlpha;
878                     ++i;
879                 }
880                 outputComponentsMap[i] = ePixelComponentNone;
881             }
882 
883             // outputComponents
884             {
885                 ChoiceParamDescriptor *param = desc.defineChoiceParam(kParamGeneratorOutputComponents);
886                 param->setLabel(kParamGeneratorOutputComponentsLabel);
887                 param->setHint(kParamGeneratorOutputComponentsHint);
888                 // the following must be in the same order as in describe(), so that the map works
889                 int defIndex = 0;
890                 int nOptions = 0;
891                 if (supportsRGBA) {
892                     assert(outputComponentsMap[param->getNOptions()] == ePixelComponentRGBA);
893                     param->appendOption(kParamGeneratorOutputComponentsOptionRGBA);
894                     if (defaultComponents == ePixelComponentRGBA) {
895                         defIndex = nOptions;
896                     }
897                     ++nOptions;
898                 }
899                 if (supportsRGB) {
900                     assert(outputComponentsMap[param->getNOptions()] == ePixelComponentRGB);
901                     param->appendOption(kParamGeneratorOutputComponentsOptionRGB);
902                     if (defaultComponents == ePixelComponentRGB) {
903                         defIndex = nOptions;
904                     }
905                     ++nOptions;
906                 }
907                 if (supportsXY) {
908 #ifdef OFX_EXTENSIONS_NATRON
909                     assert(outputComponentsMap[param->getNOptions()] == ePixelComponentXY);
910                     param->appendOption(kParamGeneratorOutputComponentsOptionXY);
911                     if (defaultComponents == ePixelComponentXY) {
912                         defIndex = nOptions;
913                     }
914                     ++nOptions;
915 #endif
916                 }
917                 if (supportsAlpha) {
918                     assert(outputComponentsMap[param->getNOptions()] == ePixelComponentAlpha);
919                     param->appendOption(kParamGeneratorOutputComponentsOptionAlpha);
920                     if (defaultComponents == ePixelComponentAlpha) {
921                         defIndex = nOptions;
922                     }
923                     ++nOptions;
924                 }
925                 param->setDefault(defIndex);
926                 param->setAnimates(false);
927                 desc.addClipPreferencesSlaveParam(*param);
928                 if (page) {
929                     page->addChild(*param);
930                 }
931             }
932         }
933 
934         // ouputBitDepth
935         if (getImageEffectHostDescription()->supportsMultipleClipDepths) {
936             ChoiceParamDescriptor *param = desc.defineChoiceParam(kParamGeneratorOutputBitDepth);
937             param->setLabel(kParamGeneratorOutputBitDepthLabel);
938             param->setHint(kParamGeneratorOutputBitDepthHint);
939             // the following must be in the same order as in describe(), so that the map works
940             if (supportsFloat) {
941                 // coverity[check_return]
942                 assert(0 <= param->getNOptions() && param->getNOptions() < 10 && outputBitDepthMap[param->getNOptions()] == eBitDepthFloat);
943                 param->appendOption(kParamGeneratorOutputBitDepthOptionFloat);
944             }
945             if (supportsHalf) {
946                 // coverity[check_return]
947                 assert(0 <= param->getNOptions() && param->getNOptions() < 10 && outputBitDepthMap[param->getNOptions()] == eBitDepthHalf);
948                 param->appendOption(kParamGeneratorOutputBitDepthOptionHalf);
949             }
950             if (supportsUShort) {
951                 // coverity[check_return]
952                 assert(0 <= param->getNOptions() && param->getNOptions() < 10 && outputBitDepthMap[param->getNOptions()] == eBitDepthUShort);
953                 param->appendOption(kParamGeneratorOutputBitDepthOptionShort);
954             }
955             if (supportsByte) {
956                 // coverity[check_return]
957                 assert(0 <= param->getNOptions() && param->getNOptions() < 10 && outputBitDepthMap[param->getNOptions()] == eBitDepthUByte);
958                 param->appendOption(kParamGeneratorOutputBitDepthOptionByte);
959             }
960             param->setDefault(0);
961 #ifndef DEBUG
962             // Shuffle only does linear conversion, which is useless for 8-bits and 16-bits formats.
963             // Disable it for now (in the future, there may be colorspace conversion options)
964             param->setIsSecretAndDisabled(true); // always secret
965 #endif
966             param->setAnimates(false);
967             desc.addClipPreferencesSlaveParam(*param);
968             if (page) {
969                 page->addChild(*param);
970             }
971         }
972     } // useOutputComponentsAndDepth
973 
974 
975     // Some hosts (e.g. Resolve) may not support normalized defaults (setDefaultCoordinateSystem(eCoordinatesNormalised))
976     if (!gHostSupportsDefaultCoordinateSystem) {
977         BooleanParamDescriptor* param = desc.defineBooleanParam(kParamDefaultsNormalised);
978         param->setDefault(true);
979         param->setEvaluateOnChange(false);
980         param->setIsSecretAndDisabled(true);
981         param->setIsPersistent(true);
982         param->setAnimates(false);
983         if (page) {
984             page->addChild(*param);
985         }
986     }
987 } // generatorDescribeInContext
988 } // OFX
989