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 ¶mName)
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