From f831bbc64dcb82cae5bba3b66278ca7ed60092e4 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Wed, 6 Dec 2023 11:03:22 -0500 Subject: [PATCH 01/22] Add kOfxParamPropChoiceValue Signed-off-by: Gary Oberbrunner --- .../sources/Reference/ofxParameter.rst | 56 ++++++++++++++++++- .../Reference/ofxPropertiesByObject.rst | 2 + .../Reference/ofxPropertiesReference.rst | 2 + Support/Library/ofxsParams.cpp | 4 +- include/ofxParam.h | 52 ++++++++++++++--- 5 files changed, 104 insertions(+), 12 deletions(-) diff --git a/Documentation/sources/Reference/ofxParameter.rst b/Documentation/sources/Reference/ofxParameter.rst index 7ee73cffd..bb965b780 100644 --- a/Documentation/sources/Reference/ofxParameter.rst +++ b/Documentation/sources/Reference/ofxParameter.rst @@ -166,8 +166,9 @@ Choice Parameters This is typed by :c:macro:`kOfxParamTypeChoice`. -Choice parameters are integer values from 0 to N-1, which correspond to -N labeled options. +Choice parameters are integer values from 0 to N-1, which correspond +to N labeled options, but see :c:macro:`kOfxParamPropChoiceValue` and +the section below this for how to change that. Choice parameters have their individual options set via the :c:macro:`kOfxParamPropChoiceOption` property, @@ -184,6 +185,55 @@ for example It is an error to have gaps in the choices after the describe action has returned. +Setting Choice Param Values +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +As of OFX v1.5, plugins can optionally specify the value the host +should store and return for each choice option, using +:c:macro:`kOfxParamPropChoiceValue`. + +This property contains the set of values to be stored when the user +chooses the corresponding (same-index) option for the choice +parameter. + +This property is useful when changing order of choice param options, or adding new options in the middle, +in a new version of the plugin. + + :: + + // Plugin v1: + Option = {"OptA", "OptB", "OptC"} + Value = {0, 1, 2} + + // Plugin v2: + Option = {"OptA", "OptB", "NewOpt", "OptC"} + Value = {0, 1, 4, 2} + +In this case if the user had selected "OptC" in v1, and then loaded the +project in v2, "OptC" will still be selected even though it is now the 4th +option, and the plugin will get the param value 2, as it did in its previous +version. + +The default, if unspecified, is ordinal integers starting from zero. + +Values may be arbitrary 32-bit integers. The same value must not occur +more than once in the values list; behavior is undefined if the same +value occurs twice in the list. The values list must contain the +default value as specified by :c:macro:`kOfxParamPropDefault`; +behavior is unspecified if it does not. + +To query whether a host supports this, just attempt to set the +property and check the return status. If the host does not support +:c:macro:`kOfxParamPropChoiceValue`, a plugin should not insert new +values into the middle of the options list, nor reorder the options, +in a new version, otherwise old projects will not load properly. + +Note: this property does not help if a plugin wants to *remove* an option. One way to +handle that case is to define a new choice param in v2 and hide the old v1 param, then use some +custom logic to populate the v2 param appropriately. + +Available since 1.5. + String Parameters ----------------- @@ -205,6 +255,8 @@ these are - .. doxygendefine:: kOfxParamStringIsLabel +- .. doxygendefine:: kOfxParamStringIsRichTextFormat + Group Parameters ---------------- diff --git a/Documentation/sources/Reference/ofxPropertiesByObject.rst b/Documentation/sources/Reference/ofxPropertiesByObject.rst index 097047816..ccc77e83f 100644 --- a/Documentation/sources/Reference/ofxPropertiesByObject.rst +++ b/Documentation/sources/Reference/ofxPropertiesByObject.rst @@ -458,6 +458,8 @@ Properties On Choice Parameters - kOfxParamPropChoiceOption read/write in the descriptor and instance +- kOfxParamPropChoiceValue + read/write in the descriptor and instance Properties On Custom Parameters ------------------------------- diff --git a/Documentation/sources/Reference/ofxPropertiesReference.rst b/Documentation/sources/Reference/ofxPropertiesReference.rst index 6bc551e81..b0151d01e 100644 --- a/Documentation/sources/Reference/ofxPropertiesReference.rst +++ b/Documentation/sources/Reference/ofxPropertiesReference.rst @@ -201,6 +201,8 @@ Properties Reference .. doxygendefine:: kOfxParamPropChoiceOption +.. doxygendefine:: kOfxParamPropChoiceValue + .. doxygendefine:: kOfxParamPropCustomInterpCallbackV1 .. doxygendefine:: kOfxParamPropCustomValue diff --git a/Support/Library/ofxsParams.cpp b/Support/Library/ofxsParams.cpp index 5077d3dcf..7229ab6d5 100644 --- a/Support/Library/ofxsParams.cpp +++ b/Support/Library/ofxsParams.cpp @@ -747,7 +747,7 @@ namespace OFX { return nCurrentValues; } - /** @brief set the default value */ + /** @brief append an option to the choice param */ void ChoiceParamDescriptor::appendOption(const std::string &v, const std::string& label) { int nCurrentValues = _paramProps.propGetDimension(kOfxParamPropChoiceOption); @@ -769,7 +769,7 @@ namespace OFX { } } - /** @brief set the default value */ + /** @brief reset all options */ void ChoiceParamDescriptor::resetOptions(void) { _paramProps.propReset(kOfxParamPropChoiceOption); diff --git a/include/ofxParam.h b/include/ofxParam.h index 45a39ce9d..08332593e 100644 --- a/include/ofxParam.h +++ b/include/ofxParam.h @@ -600,16 +600,52 @@ This data pointer is unique to each parameter instance, so two instances of the */ #define kOfxParamPropDataPtr "OfxParamPropDataPtr" -/** @brief Set an option in a choice parameter. +/** @brief Set options of a choice parameter. - Type - UTF8 C string X N - Property Set - plugin parameter descriptor (read/write) and instance (read/write), - Default - the property is empty with no options set. -This property contains the set of options that will be presented to a user from a choice parameter. See @ref ParametersChoice for more details. +This property contains the set of options that will be presented to a user +from a choice parameter. See @ref ParametersChoice for more details. */ #define kOfxParamPropChoiceOption "OfxParamPropChoiceOption" +/** @brief Set values the host should store for a choice parameter. + + - Type - int X N + - Property Set - plugin parameter descriptor (read/write) and instance (read/write), + - Default - Zero-based ordinal list of same length as `OfxParamPropChoiceOption` + +This property contains the set of values to be stored when the user chooses the corresponding +(same-index) option for the choice parameter. See @ref "Choice Parameters" for more details. +This property is optional; if not set, the host will store the zero-based integer index +of the chosen ::kOfxParamPropChoiceOption. +This property is useful when changing order of choice param options, or adding new options in the middle, +in a new version of the plugin. + + @verbatim + Plugin v1: + Option = {"OptA", "OptB", "OptC"} + Value = {1, 2, 3} + + Plugin v2: + Option = {"OptA", "OptB", "NewOpt", "OptC"} + Value = {1, 2, 4, 3} + @endverbatim + +In this case if the user had selected "OptC" in v1, and then loaded the +project in v2, "OptC" will still be selected even though it is now the 4th +option, and the plugin will get the param value 3 (as it did in its previous +version). + +Values may be arbitrary 32-bit integers. Behavior is undefined if the same +value occurs twice in the list; plugins should not do that. + +\since Version 1.5 +*/ +#define kOfxParamPropChoiceValue "OfxParamPropChoiceValue" + /** @brief The minimum value for a numeric parameter. - Type - int or double X N @@ -732,26 +768,26 @@ If set to 0, it implies the user can specify a new file name, not just a pre-exi #define kOfxParamPropStringFilePathExists "OfxParamPropStringFilePathExists" /** @brief Used to set a string parameter to be single line, - value to be passed to a kOfxParamPropStringMode property */ + value to be passed to a ::kOfxParamPropStringMode property */ #define kOfxParamStringIsSingleLine "OfxParamStringIsSingleLine" /** @brief Used to set a string parameter to be multiple line, - value to be passed to a kOfxParamPropStringMode property */ + value to be passed to a ::kOfxParamPropStringMode property */ #define kOfxParamStringIsMultiLine "OfxParamStringIsMultiLine" /** @brief Used to set a string parameter to be a file path, - value to be passed to a kOfxParamPropStringMode property */ + value to be passed to a ::kOfxParamPropStringMode property */ #define kOfxParamStringIsFilePath "OfxParamStringIsFilePath" /** @brief Used to set a string parameter to be a directory path, - value to be passed to a kOfxParamPropStringMode property */ + value to be passed to a ::kOfxParamPropStringMode property */ #define kOfxParamStringIsDirectoryPath "OfxParamStringIsDirectoryPath" /** @brief Use to set a string parameter to be a simple label, - value to be passed to a kOfxParamPropStringMode property */ + value to be passed to a ::kOfxParamPropStringMode property */ #define kOfxParamStringIsLabel "OfxParamStringIsLabel" -/** @brief String value on the OfxParamPropStringMode property of a + /** @brief String value on the ::kOfxParamPropStringMode property of a string parameter (added in 1.3) */ #define kOfxParamStringIsRichTextFormat "OfxParamStringIsRichTextFormat" From 54db582f44ba856a4ef1ee0629be923b1aca0c55 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Sat, 2 Mar 2024 15:05:31 -0500 Subject: [PATCH 02/22] Change kOfxParamPropChoiceValue to kOfxParamPropChoiceOrder w/ doc Update the doc to describe the operation of the new property. Signed-off-by: Gary Oberbrunner --- .../sources/Reference/ofxParameter.rst | 57 ++++++++++++------- .../Reference/ofxPropertiesByObject.rst | 2 +- .../Reference/ofxPropertiesReference.rst | 2 +- include/ofxParam.h | 35 ++++++------ 4 files changed, 59 insertions(+), 37 deletions(-) diff --git a/Documentation/sources/Reference/ofxParameter.rst b/Documentation/sources/Reference/ofxParameter.rst index bb965b780..d07321a63 100644 --- a/Documentation/sources/Reference/ofxParameter.rst +++ b/Documentation/sources/Reference/ofxParameter.rst @@ -167,7 +167,7 @@ Choice Parameters This is typed by :c:macro:`kOfxParamTypeChoice`. Choice parameters are integer values from 0 to N-1, which correspond -to N labeled options, but see :c:macro:`kOfxParamPropChoiceValue` and +to N labeled options, but see :c:macro:`kOfxParamPropChoiceOrder` and the section below this for how to change that. Choice parameters have their individual options set via the @@ -185,16 +185,22 @@ for example It is an error to have gaps in the choices after the describe action has returned. -Setting Choice Param Values -^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Note: plugins can change the *text* of options strings in new versions +with no compatibility impact, since the host should only store the +index. But they should not change the *order* of options without using +:c:macro:`kOfxParamPropChoiceOrder`. -As of OFX v1.5, plugins can optionally specify the value the host -should store and return for each choice option, using -:c:macro:`kOfxParamPropChoiceValue`. +Setting Choice Param Order +^^^^^^^^^^^^^^^^^^^^^^^^^^ -This property contains the set of values to be stored when the user -chooses the corresponding (same-index) option for the choice -parameter. +As of OFX v1.5, plugins can optionally specify the order in which the host +should display each choice option, using +:c:macro:`kOfxParamPropChoiceOrder`. + +This property contains a set of integers, of the same length as the +options for the choice parameter. If the host supports this property, +it should sort the options and the order list together, and display +the options in increasing order. This property is useful when changing order of choice param options, or adding new options in the middle, in a new version of the plugin. @@ -203,28 +209,35 @@ in a new version of the plugin. // Plugin v1: Option = {"OptA", "OptB", "OptC"} - Value = {0, 1, 2} + Order = {0, 1, 2} // default, or explicit // Plugin v2: - Option = {"OptA", "OptB", "NewOpt", "OptC"} - Value = {0, 1, 4, 2} + // add NewOpt at the end of the list, but specify order so it comes one before the end in the UI + // Will display OptA / OptB / NewOpt / OptC + Option = {"OptA", "OptB", "OptC", "NewOpt"} + Order = {0, 1, 3, 2} // or anything that sorts the same, e.g. {-100, 100, 300, 200} In this case if the user had selected "OptC" in v1, and then loaded the project in v2, "OptC" will still be selected even though it is now the 4th option, and the plugin will get the param value 2, as it did in its previous version. -The default, if unspecified, is ordinal integers starting from zero. +The default, if unspecified, is ordinal integers starting from zero, +so the options are displayed in their natural order. Values may be arbitrary 32-bit integers. The same value must not occur -more than once in the values list; behavior is undefined if the same -value occurs twice in the list. The values list must contain the -default value as specified by :c:macro:`kOfxParamPropDefault`; -behavior is unspecified if it does not. +more than once in the order list; behavior is undefined if the same +value occurs twice in the list. -To query whether a host supports this, just attempt to set the +Note that :c:macro:`kOfxParamPropChoiceOrder` does not affect project +storage or operation; it is only used by the host UI. This way it is 100% +backward compatible; even if the plugin sets it and the host doesn't support it, +the plugin will still work as usual. Its options will just appear with the new +ones at the end rather than the preferred order. + +To query whether a host supports this, a plugin should attempt to set the property and check the return status. If the host does not support -:c:macro:`kOfxParamPropChoiceValue`, a plugin should not insert new +:c:macro:`kOfxParamPropChoiceOrder`, a plugin should not insert new values into the middle of the options list, nor reorder the options, in a new version, otherwise old projects will not load properly. @@ -232,6 +245,12 @@ Note: this property does not help if a plugin wants to *remove* an option. One w handle that case is to define a new choice param in v2 and hide the old v1 param, then use some custom logic to populate the v2 param appropriately. +Also in 1.5, see the new :c:macro:`kOfxParamTypeStrChoice` param type +for another way to do this: the plugin specifies a set of string +values as well as user-visible options, and the host stores the string +value. Plugins can then change the UI order at will in new versions, +by reordering the options and enum arrays. + Available since 1.5. diff --git a/Documentation/sources/Reference/ofxPropertiesByObject.rst b/Documentation/sources/Reference/ofxPropertiesByObject.rst index ccc77e83f..c46b84031 100644 --- a/Documentation/sources/Reference/ofxPropertiesByObject.rst +++ b/Documentation/sources/Reference/ofxPropertiesByObject.rst @@ -458,7 +458,7 @@ Properties On Choice Parameters - kOfxParamPropChoiceOption read/write in the descriptor and instance -- kOfxParamPropChoiceValue +- kOfxParamPropChoiceOrder read/write in the descriptor and instance Properties On Custom Parameters diff --git a/Documentation/sources/Reference/ofxPropertiesReference.rst b/Documentation/sources/Reference/ofxPropertiesReference.rst index b0151d01e..2dea2de86 100644 --- a/Documentation/sources/Reference/ofxPropertiesReference.rst +++ b/Documentation/sources/Reference/ofxPropertiesReference.rst @@ -201,7 +201,7 @@ Properties Reference .. doxygendefine:: kOfxParamPropChoiceOption -.. doxygendefine:: kOfxParamPropChoiceValue +.. doxygendefine:: kOfxParamPropChoiceOrder .. doxygendefine:: kOfxParamPropCustomInterpCallbackV1 diff --git a/include/ofxParam.h b/include/ofxParam.h index 08332593e..a8b167a0a 100644 --- a/include/ofxParam.h +++ b/include/ofxParam.h @@ -614,37 +614,40 @@ from a choice parameter. See @ref ParametersChoice for more details. /** @brief Set values the host should store for a choice parameter. - Type - int X N - - Property Set - plugin parameter descriptor (read/write) and instance (read/write), - - Default - Zero-based ordinal list of same length as `OfxParamPropChoiceOption` + - Property Set - plugin parameter descriptor (read/write) and instance +(read/write), + - Default - Zero-based ordinal list of same length as +`OfxParamPropChoiceOption` + +This property specifies the order in which the options are presented. +See @ref "Choice Parameters" for more details. +This property is optional; if not set, the host will present the options in +their natural order. -This property contains the set of values to be stored when the user chooses the corresponding -(same-index) option for the choice parameter. See @ref "Choice Parameters" for more details. -This property is optional; if not set, the host will store the zero-based integer index -of the chosen ::kOfxParamPropChoiceOption. -This property is useful when changing order of choice param options, or adding new options in the middle, -in a new version of the plugin. +This property is useful when changing order of choice param options, or adding +new options in the middle, in a new version of the plugin. @verbatim Plugin v1: Option = {"OptA", "OptB", "OptC"} - Value = {1, 2, 3} + Order = {1, 2, 3} Plugin v2: - Option = {"OptA", "OptB", "NewOpt", "OptC"} - Value = {1, 2, 4, 3} + // will be shown as OptA / OptB / NewOpt / OptC + Option = {"OptA", "OptB", "OptC", NewOpt"} + Order = {1, 2, 4, 3} @endverbatim -In this case if the user had selected "OptC" in v1, and then loaded the -project in v2, "OptC" will still be selected even though it is now the 4th -option, and the plugin will get the param value 3 (as it did in its previous -version). +Note that this only affects the host UI's display order; the project still +stores the index of the selected option as always. Plugins should never +reorder existing options if they desire backward compatibility. Values may be arbitrary 32-bit integers. Behavior is undefined if the same value occurs twice in the list; plugins should not do that. \since Version 1.5 */ -#define kOfxParamPropChoiceValue "OfxParamPropChoiceValue" +#define kOfxParamPropChoiceOrder "OfxParamPropChoiceOrder" /** @brief The minimum value for a numeric parameter. From e1de2da3d46ff55f9a1c3f6c03b75b836ea9abcf Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Sat, 2 Mar 2024 15:45:03 -0500 Subject: [PATCH 03/22] Add string-valued StrChoice param type Signed-off-by: Gary Oberbrunner --- .../sources/Reference/ofxParameter.rst | 49 ++++++++++++++++++- .../Reference/ofxPropertiesReference.rst | 6 +++ include/ofx.dtd | 6 +-- include/ofxParam.h | 39 +++++++++++++++ 4 files changed, 96 insertions(+), 4 deletions(-) diff --git a/Documentation/sources/Reference/ofxParameter.rst b/Documentation/sources/Reference/ofxParameter.rst index d07321a63..f174f1018 100644 --- a/Documentation/sources/Reference/ofxParameter.rst +++ b/Documentation/sources/Reference/ofxParameter.rst @@ -75,7 +75,7 @@ properties, and getting derivatives and integrals of param values. Parameter Types --------------- -There are seventeen types of parameter. These are +There are eighteen types of parameter. These are * .. doxygendefine:: kOfxParamTypeInteger * .. doxygendefine:: kOfxParamTypeInteger2D @@ -87,6 +87,7 @@ There are seventeen types of parameter. These are * .. doxygendefine:: kOfxParamTypeRGBA * .. doxygendefine:: kOfxParamTypeBoolean * .. doxygendefine:: kOfxParamTypeChoice +* .. doxygendefine:: kOfxParamTypeStrChoice * .. doxygendefine:: kOfxParamTypeString * .. doxygendefine:: kOfxParamTypeCustom * .. doxygendefine:: kOfxParamTypePushButton @@ -254,6 +255,49 @@ by reordering the options and enum arrays. Available since 1.5. +String-Valued Choice Parameters +------------------------------- + +This is typed by :c:macro:`kOfxParamTypeStrChoice`. + +String Choice ("StrChoice") parameters are string-valued, unlike +standard Choice parameters. This way plugins may change the text and +order of the choices in new versions as desired, without sacrificing +compatibility. The host stores the string value rather than the index +of the option, and when loading a project, finds and selects the +option with the corresponding enum. + +Choice parameters have their individual options and enums set via the +:c:macro:`kOfxParamPropChoiceOption` and :c:macro:`kOfxParamPropChoiceEnum` properties, +for example + + :: + + gPropHost->propSetString(myChoiceParam, kOfxParamPropChoiceOption, 0, "1st Choice"); + gPropHost->propSetString(myChoiceParam, kOfxParamPropChoiceOption, 1, "2nd Choice"); + gPropHost->propSetString(myChoiceParam, kOfxParamPropChoiceOption, 2, "3nd Choice"); + ... + // enums: string values to be returned as param value, and stored by the host in the project + gPropHost->propSetString(myChoiceParam, kOfxParamPropChoiceEnum, 0, "choice-1"); + gPropHost->propSetString(myChoiceParam, kOfxParamPropChoiceEnum, 1, "choice-2"); + gPropHost->propSetString(myChoiceParam, kOfxParamPropChoiceEnum, 2, "choice-3"); + +The default value of a StrChoice param must be one of the specified +enums, or the behavior is undefined. + +It is an error to have gaps in the choices after the describe action +has returned. The Option and Enum arrays must be of the same length, +otherwise the behavior is undefined. + +If a plugin removes enums in later versions and a project is saved +with the removed enum, behavior is undefined, but it is recommended +that the host use the default value in that case. + +To check for availability of this param type, a plugin may check the +host property :c:macro:`kOfxParamHostPropSupportsStrChoice`. + +Available since 1.5. + String Parameters ----------------- @@ -381,6 +425,7 @@ they do **not** animate by default. They are... - :c:macro:`kOfxParamTypeString` - :c:macro:`kOfxParamTypeBoolean` - :c:macro:`kOfxParamTypeChoice` +- :c:macro:`kOfxParamTypeStrChoice` By default the :cpp:func:`OfxParameterSuiteV1::paramGetValue` @@ -604,6 +649,8 @@ The variant property types are as follows.... int X 1 - :c:macro:`kOfxParamTypeChoice` int X 1 +- :c:macro:`kOfxParamTypeStrChoice` + char \* X 1 - :c:macro:`kOfxParamTypeRGBA` double X 4 (normalised to 0..1 range) - :c:macro:`kOfxParamTypeRGB` diff --git a/Documentation/sources/Reference/ofxPropertiesReference.rst b/Documentation/sources/Reference/ofxPropertiesReference.rst index 2dea2de86..c38454c28 100644 --- a/Documentation/sources/Reference/ofxPropertiesReference.rst +++ b/Documentation/sources/Reference/ofxPropertiesReference.rst @@ -191,6 +191,10 @@ Properties Reference .. doxygendefine:: kOfxParamHostPropSupportsParametricAnimation +.. doxygendefine:: kOfxParamHostPropSupportsStrChoice + +.. doxygendefine:: kOfxParamHostPropSupportsStrChoiceAnimation + .. doxygendefine:: kOfxParamHostPropSupportsStringAnimation .. doxygendefine:: kOfxParamPropAnimates @@ -199,6 +203,8 @@ Properties Reference .. doxygendefine:: kOfxParamPropCanUndo +.. doxygendefine:: kOfxParamPropChoiceEnum + .. doxygendefine:: kOfxParamPropChoiceOption .. doxygendefine:: kOfxParamPropChoiceOrder diff --git a/include/ofx.dtd b/include/ofx.dtd index 11cff907c..e287fc990 100644 --- a/include/ofx.dtd +++ b/include/ofx.dtd @@ -26,9 +26,9 @@ A message redefinition is associated with the 'messageId' passed into the OfxMes A resource set is made up of parameter redefinitions, clip redefinitions, parameter hierarchy redefinitions and parameter page set redefinitions. -Any parameter (except page and hierachy params) in the binaray may have certain properties overridden. Each parameter type has a corresponding element, for example the 2D integer parameter is specified via the 'OfxParamTypeInteger2D', the choice parameter by the 'OfxParamTypeChoice' element. There are 13 different elements, each of which corresponds to an OFX parameter type. The 'name' attribute of a parameter element is used to associate it with a specific parameter in the binary. +Any parameter (except page and hierachy params) in the binary may have certain properties overridden. Each parameter type has a corresponding element, for example the 2D integer parameter is specified via the 'OfxParamTypeInteger2D', the choice parameter by the 'OfxParamTypeChoice' element. There are many different elements, each of which corresponds to an OFX parameter type. The 'name' attribute of a parameter element is used to associate it with a specific parameter in the binary. -Each parameter element has it's own specific set of sub elements, as not all parameters support all properties. +Each parameter element has its own specific set of sub elements, as not all parameters support all properties. An image effect clip may simply have its labels and hints overridden. This is the 'OfxImageClip' element. It is associated with a clip in the binary via its 'name' element. @@ -107,7 +107,7 @@ Errors should be handled robustly. If a respecified parameter doesn't exist in t - + diff --git a/include/ofxParam.h b/include/ofxParam.h index a8b167a0a..4bfe91553 100644 --- a/include/ofxParam.h +++ b/include/ofxParam.h @@ -46,6 +46,8 @@ These strings are used to identify the type of the parameter when it is defined, #define kOfxParamTypeBoolean "OfxParamTypeBoolean" /** @brief String to identify a param as a Single valued, 'one-of-many' parameter */ #define kOfxParamTypeChoice "OfxParamTypeChoice" +/** @brief String to identify a param as a string-valued 'one-of-many' parameter. \since Version 1.5 */ +#define kOfxParamTypeStrChoice "OfxParamTypeStrChoice" /** @brief String to identify a param as a Red, Green, Blue and Alpha colour parameter */ #define kOfxParamTypeRGBA "OfxParamTypeRGBA" /** @brief String to identify a param as a Red, Green and Blue colour parameter */ @@ -398,6 +400,7 @@ The exact type and dimension is dependant on the type of the parameter. These ar - ::kOfxParamTypeDouble - double property of one dimension - ::kOfxParamTypeBoolean - integer property of one dimension - ::kOfxParamTypeChoice - integer property of one dimension + - ::kOfxParamTypeStrChoice - string property of one dimension - ::kOfxParamTypeRGBA - double property of four dimensions - ::kOfxParamTypeRGB - double property of three dimensions - ::kOfxParamTypeDouble2D - double property of two dimensions @@ -649,6 +652,42 @@ value occurs twice in the list; plugins should not do that. */ #define kOfxParamPropChoiceOrder "OfxParamPropChoiceOrder" + +/** @brief Set a enumeration string in a StrChoice (string-valued choice) parameter. + + - Type - UTF8 C string X N + - Property Set - plugin parameter descriptor (read/write) and instance +(read/write), + - Default - the property is empty with no options set. + +This property contains the set of enumeration strings stored by the host in +the project corresponding to the options that will be presented to a user +from a StrChoice parameter. See @ref ParametersChoice for more details. + +\since Version 1.5 +*/ +#define kOfxParamPropChoiceEnum "OfxParamPropChoiceEnum" + +/** @brief Indicates if the host supports animation of string choice params. + + - Type - int X 1 + - Property Set - host descriptor (read only) + - Valid Values - 0 or 1 + +\since Version 1.5 +*/ +#define kOfxParamHostPropSupportsStrChoiceAnimation "OfxParamHostPropSupportsStrChoiceAnimation" + +/** @brief Indicates if the host supports the StrChoice param type. + + - Type - int X 1 + - Property Set - host descriptor (read only) + - Valid Values - 0 or 1 + +\since Version 1.5 +*/ +#define kOfxParamHostPropSupportsStrChoice "OfxParamHostPropSupportsStrChoice" + /** @brief The minimum value for a numeric parameter. - Type - int or double X N From 3692bc7919e3cfe3e96bad1e614358444be72b6d Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Sat, 2 Mar 2024 15:54:51 -0500 Subject: [PATCH 04/22] Add support for StrChoice params in plugin support lib This code was contributed by BlackMagic. Signed-off-by: Gary Oberbrunner --- Support/Library/ofxsImageEffect.cpp | 1 + Support/Library/ofxsParams.cpp | 144 ++++++++++++++++++++- Support/Library/ofxsPropertyValidation.cpp | 31 ++++- Support/include/ofxsImageEffect.h | 1 + Support/include/ofxsParam.h | 70 ++++++++++ release-notes.md | 5 +- 6 files changed, 248 insertions(+), 4 deletions(-) diff --git a/Support/Library/ofxsImageEffect.cpp b/Support/Library/ofxsImageEffect.cpp index 6d956ae68..9e592d5d5 100644 --- a/Support/Library/ofxsImageEffect.cpp +++ b/Support/Library/ofxsImageEffect.cpp @@ -1809,6 +1809,7 @@ namespace OFX { gHostDescription.supportsStringAnimation = hostProps.propGetInt(kOfxParamHostPropSupportsStringAnimation) != 0; gHostDescription.supportsCustomInteract = hostProps.propGetInt(kOfxParamHostPropSupportsCustomInteract) != 0; gHostDescription.supportsChoiceAnimation = hostProps.propGetInt(kOfxParamHostPropSupportsChoiceAnimation) != 0; + gHostDescription.supportsStrChoiceAnimation = hostProps.propGetInt(kOfxParamHostPropSupportsStrChoiceAnimation) != 0; gHostDescription.supportsBooleanAnimation = hostProps.propGetInt(kOfxParamHostPropSupportsBooleanAnimation) != 0; gHostDescription.supportsCustomAnimation = hostProps.propGetInt(kOfxParamHostPropSupportsCustomAnimation) != 0; gHostDescription.osHandle = hostProps.propGetPointer(kOfxPropHostOSHandle, false); diff --git a/Support/Library/ofxsParams.cpp b/Support/Library/ofxsParams.cpp index 7229ab6d5..a8238c96b 100644 --- a/Support/Library/ofxsParams.cpp +++ b/Support/Library/ofxsParams.cpp @@ -32,6 +32,7 @@ namespace OFX { case eRGBAParam : return kOfxParamTypeRGBA ; case eBooleanParam : return kOfxParamTypeBoolean ; case eChoiceParam : return kOfxParamTypeChoice ; + case eStrChoiceParam : return kOfxParamTypeStrChoice; case eCustomParam : return kOfxParamTypeCustom ; case eGroupParam : return kOfxParamTypeGroup ; case ePageParam : return kOfxParamTypePage ; @@ -73,6 +74,8 @@ namespace OFX { return eBooleanParam ; else if(isEqual(kOfxParamTypeChoice,v)) return eChoiceParam ; + else if (isEqual(kOfxParamTypeStrChoice, v)) + return eStrChoiceParam; else if(isEqual(kOfxParamTypeCustom ,v)) return eCustomParam ; else if(isEqual(kOfxParamTypeGroup,v)) @@ -775,6 +778,47 @@ namespace OFX { _paramProps.propReset(kOfxParamPropChoiceOption); } + //////////////////////////////////////////////////////////////////////////////// + // string choice param descriptor + + /** @brief hidden constructor */ + StrChoiceParamDescriptor::StrChoiceParamDescriptor(const std::string& p_Name, OfxPropertySetHandle p_Props) + : ValueParamDescriptor(p_Name, eStrChoiceParam, p_Props) + { + } + + /** @brief set the default value */ + void StrChoiceParamDescriptor::setDefault(const std::string& p_DefaultValue) + { + _paramProps.propSetString(kOfxParamPropDefault, p_DefaultValue); + } + + /** @brief append an option */ + void StrChoiceParamDescriptor::appendOption(const std::string& p_Enum, const std::string& p_Option) + { + const int numOptions = _paramProps.propGetDimension(kOfxParamPropChoiceOption); + assert(numOptions == _paramProps.propGetDimension(kOfxParamPropChoiceEnum)); + + _paramProps.propSetString(kOfxParamPropChoiceEnum, p_Enum, numOptions); + _paramProps.propSetString(kOfxParamPropChoiceOption, p_Option, numOptions); + } + + /** @brief how many options do we have */ + int StrChoiceParamDescriptor::getNOptions() + { + const int numOptions = _paramProps.propGetDimension(kOfxParamPropChoiceOption); + assert(numOptions == _paramProps.propGetDimension(kOfxParamPropChoiceEnum)); + + return numOptions; + } + + /** @brief clear all the options so as to add some new ones in */ + void StrChoiceParamDescriptor::resetOptions(void) + { + _paramProps.propReset(kOfxParamPropChoiceEnum); + _paramProps.propReset(kOfxParamPropChoiceOption); + } + //////////////////////////////////////////////////////////////////////////////// // string param descriptor @@ -1114,6 +1158,14 @@ namespace OFX { return param; } + /** @brief Define a String Choice param */ + StrChoiceParamDescriptor* ParamSetDescriptor::defineStrChoiceParam(const std::string& p_Name) + { + StrChoiceParamDescriptor* param = NULL; + defineParamDescriptor(p_Name, eStrChoiceParam, param); + return param; + } + /** @brief Define a group param */ GroupParamDescriptor *ParamSetDescriptor::defineGroupParam(const std::string &name) { @@ -2448,6 +2500,82 @@ namespace OFX { _paramProps.propReset(kOfxParamPropChoiceOption); } + //////////////////////////////////////////////////////////////////////////////// + // Wraps up a string choice param */ + + /** @brief hidden constructor */ + StrChoiceParam::StrChoiceParam(const ParamSet* p_ParamSet, const std::string& p_Name, OfxParamHandle p_Handle) + : StringParam(p_ParamSet, p_Name, p_Handle) + { + _paramType = eStrChoiceParam; + } + + /** @brief how many options do we have */ + int StrChoiceParam::getNOptions() + { + const int numOptions = _paramProps.propGetDimension(kOfxParamPropChoiceOption); + assert(numOptions == _paramProps.propGetDimension(kOfxParamPropChoiceEnum)); + + return numOptions; + } + + /** @brief add another option */ + void StrChoiceParam::appendOption(const std::string& p_Enum, const std::string& p_Option) + { + const int numOptions = _paramProps.propGetDimension(kOfxParamPropChoiceOption); + assert(numOptions == _paramProps.propGetDimension(kOfxParamPropChoiceEnum)); + + _paramProps.propSetString(kOfxParamPropChoiceEnum, p_Enum, numOptions); + _paramProps.propSetString(kOfxParamPropChoiceOption, p_Option, numOptions); + } + + /** @brief set the string of a specific option */ + void StrChoiceParam::setOption(const std::string& p_Index, const std::string& p_Option) + { + const int numOptions = _paramProps.propGetDimension(kOfxParamPropChoiceOption); + assert(numOptions == _paramProps.propGetDimension(kOfxParamPropChoiceEnum)); + + for (int i = 0; i < numOptions; ++i) + { + const std::string& enumStr = _paramProps.propGetString(kOfxParamPropChoiceEnum, i); + if (enumStr == p_Index) + { + _paramProps.propSetString(kOfxParamPropChoiceOption, p_Option, i); + + return; + } + } + + throwSuiteStatusException(kOfxStatErrBadIndex); + } + + /** @brief get the option value */ + void StrChoiceParam::getOption(const std::string& p_Index, std::string& p_Option) + { + const int numOptions = _paramProps.propGetDimension(kOfxParamPropChoiceOption); + assert(numOptions == _paramProps.propGetDimension(kOfxParamPropChoiceEnum)); + + for (int i = 0; i < numOptions; ++i) + { + const std::string& enumStr = _paramProps.propGetString(kOfxParamPropChoiceEnum, i); + if (enumStr == p_Index) + { + p_Option = _paramProps.propGetString(kOfxParamPropChoiceOption, i); + + return; + } + } + + throwSuiteStatusException(kOfxStatErrBadIndex); + } + + /** @brief set to the default value */ + void StrChoiceParam::resetOptions() + { + _paramProps.propReset(kOfxParamPropChoiceEnum); + _paramProps.propReset(kOfxParamPropChoiceOption); + } + //////////////////////////////////////////////////////////////////////////////// // Wraps up a custom param */ @@ -2871,7 +2999,13 @@ namespace OFX { fetchParam(name, t, ptr); return ptr; } - case eCustomParam : + case eStrChoiceParam : + { + StrChoiceParam* ptr = 0; + fetchParam(name, t, ptr); + return ptr; + } + case eCustomParam : { CustomParam* ptr = 0; fetchParam(name, t, ptr); @@ -3009,6 +3143,14 @@ namespace OFX { return param; } + /** @brief Fetch a StrChoice param */ + StrChoiceParam* ParamSet::fetchStrChoiceParam(const std::string& p_Name) const + { + StrChoiceParam* param = NULL; + fetchParam(p_Name, eStrChoiceParam, param); + return param; + } + /** @brief Fetch a group param */ GroupParam *ParamSet::fetchGroupParam(const std::string &name) const { diff --git a/Support/Library/ofxsPropertyValidation.cpp b/Support/Library/ofxsPropertyValidation.cpp index df5e991f7..7daf7bdb7 100644 --- a/Support/Library/ofxsPropertyValidation.cpp +++ b/Support/Library/ofxsPropertyValidation.cpp @@ -284,6 +284,7 @@ namespace OFX { PropertyDescription(kOfxParamHostPropSupportsStringAnimation, OFX::eInt, 1, eDescFinished), PropertyDescription(kOfxParamHostPropSupportsCustomInteract, OFX::eInt, 1, eDescFinished), PropertyDescription(kOfxParamHostPropSupportsChoiceAnimation, OFX::eInt, 1, eDescFinished), + PropertyDescription(kOfxParamHostPropSupportsStrChoiceAnimation, OFX::eInt, 1, eDescFinished), PropertyDescription(kOfxParamHostPropSupportsBooleanAnimation, OFX::eInt, 1, eDescFinished), PropertyDescription(kOfxParamHostPropSupportsCustomAnimation, OFX::eInt, 1, eDescFinished), PropertyDescription(kOfxParamHostPropMaxParameters, OFX::eInt, 1, eDescFinished), @@ -792,7 +793,7 @@ namespace OFX { }; - /** @brief properties for a boolean param */ + /** @brief properties for a choice param */ static PropertyDescription gChoiceParamProps[ ] = { PropertyDescription(kOfxParamPropDefault, OFX::eInt, 1, eDescFinished), @@ -800,6 +801,15 @@ namespace OFX { PropertyDescription(kOfxParamPropChoiceOption, OFX::eString, -1, eDescFinished), }; + /** @brief properties for a string choice param */ + static PropertyDescription gStrChoiceParamProps[ ] = + { + PropertyDescription(kOfxParamPropDefault, OFX::eString, 1, eDescFinished), + PropertyDescription(kOfxParamPropAnimates, OFX::eInt, 1, eDescDefault, 0, eDescFinished), + PropertyDescription(kOfxParamPropChoiceEnum, OFX::eString, -1, eDescFinished), + PropertyDescription(kOfxParamPropChoiceOption, OFX::eString, -1, eDescFinished), + }; + /** @brief properties for a 1D integer param */ static PropertyDescription gInt1DParamProps[ ] = { @@ -1002,6 +1012,14 @@ namespace OFX { mPropDescriptionArg(gChoiceParamProps), NULLPTR); + /** @brief Property set for string choice params */ + static PropertySetDescription gStrChoiceParamPropSet("String Choice parameter", + mPropDescriptionArg(gBasicParamProps), + mPropDescriptionArg(gInteractOverideParamProps), + mPropDescriptionArg(gValueHolderParamProps), + mPropDescriptionArg(gStrChoiceParamProps), + NULLPTR); + /** @brief Property set for push button params */ static PropertySetDescription gPushButtonParamPropSet("PushButton parameter", mPropDescriptionArg(gBasicParamProps), @@ -1220,6 +1238,9 @@ namespace OFX { case eChoiceParam : gChoiceParamPropSet.validate(paramProps, checkDefaults); break; + case eStrChoiceParam : + gStrChoiceParamPropSet.validate(paramProps, checkDefaults); + break; case eCustomParam : gCustomParamPropSet.validate(paramProps, checkDefaults); break; @@ -1275,7 +1296,13 @@ namespace OFX { eDescFinished); gChoiceParamPropSet.addProperty(desc, true); - // do choice params animate + // do string choice params animate + desc = new PropertyDescription(kOfxParamPropAnimates, OFX::eInt, 1, + eDescDefault, int(getImageEffectHostDescription()->supportsStrChoiceAnimation), + eDescFinished); + gStrChoiceParamPropSet.addProperty(desc, true); + + // do boolean params animate desc = new PropertyDescription(kOfxParamPropAnimates, OFX::eInt, 1, eDescDefault, int(getImageEffectHostDescription()->supportsBooleanAnimation), eDescFinished); diff --git a/Support/include/ofxsImageEffect.h b/Support/include/ofxsImageEffect.h index 2074aa049..4ea98e6ba 100644 --- a/Support/include/ofxsImageEffect.h +++ b/Support/include/ofxsImageEffect.h @@ -244,6 +244,7 @@ namespace OFX { bool supportsStringAnimation; bool supportsCustomInteract; bool supportsChoiceAnimation; + bool supportsStrChoiceAnimation; bool supportsBooleanAnimation; bool supportsCustomAnimation; void* osHandle; diff --git a/Support/include/ofxsParam.h b/Support/include/ofxsParam.h index ac725be38..aa7e776fe 100644 --- a/Support/include/ofxsParam.h +++ b/Support/include/ofxsParam.h @@ -46,6 +46,7 @@ namespace OFX { class RGBParamDescriptor; class BooleanParamDescriptor; class ChoiceParamDescriptor; + class StrChoiceParamDescriptor; class GroupParamDescriptor; class PageParamDescriptor; class PushButtonParamDescriptor; @@ -66,6 +67,7 @@ namespace OFX { class StringParam; class BooleanParam; class ChoiceParam; + class StrChoiceParam; class CustomParam; class GroupParam; class PageParam; @@ -86,6 +88,7 @@ namespace OFX { eRGBAParam, eBooleanParam, eChoiceParam, + eStrChoiceParam, eCustomParam, eGroupParam, ePageParam, @@ -586,6 +589,35 @@ namespace OFX { void resetOptions(void); }; + //////////////////////////////////////////////////////////////////////////////// + /** @brief Wraps up a string choice param */ + class StrChoiceParamDescriptor : public ValueParamDescriptor + { + protected : + mDeclareProtectedAssignAndCCBase(StrChoiceParamDescriptor, ValueParamDescriptor); + StrChoiceParamDescriptor() { assert(false); } + + protected : + /** @brief hidden constructor */ + StrChoiceParamDescriptor(const std::string& p_Name, OfxPropertySetHandle p_Props); + + // so it can make one + friend class ParamSetDescriptor; + + public : + /** @brief set the default value */ + void setDefault(const std::string& p_DefaultValue); + + /** @brief append an option */ + void appendOption(const std::string& p_Enum, const std::string& p_Option); + + /** @brief how many options do we have */ + int getNOptions(); + + /** @brief clear all the options so as to add some new ones in */ + void resetOptions(); + }; + //////////////////////////////////////////////////////////////////////////////// /** @brief Wraps up a group param, not much to it really */ class GroupParamDescriptor : public ParamDescriptor { @@ -807,6 +839,9 @@ namespace OFX { /** @brief Define a Choice param */ ChoiceParamDescriptor *defineChoiceParam(const std::string &name); + /** @brief Define a String Choice param */ + StrChoiceParamDescriptor* defineStrChoiceParam(const std::string& p_Name); + /** @brief Define a group param */ GroupParamDescriptor *defineGroupParam(const std::string &name); @@ -1488,6 +1523,38 @@ namespace OFX { void setValueAtTime(double t, int v); }; + //////////////////////////////////////////////////////////////////////////////// + /** @brief Wraps up a string choice param */ + class StrChoiceParam : public StringParam + { + protected : + mDeclareProtectedAssignAndCCBase(StrChoiceParam, StringParam); + StrChoiceParam() { assert(false); } + + protected : + /** @brief hidden constructor */ + StrChoiceParam(const ParamSet* p_ParamSet, const std::string& p_Name, OfxParamHandle p_Handle); + + // so it can make one + friend class ParamSet; + + public : + /** @brief how many options do we have */ + int getNOptions(); + + /** @brief append an option */ + void appendOption(const std::string& p_Enum, const std::string& p_Option); + + /** @brief set an option */ + void setOption(const std::string& p_Index, const std::string& p_Option); + + /** @brief get the option value */ + void getOption(const std::string& p_Index, std::string& p_Option); + + /** @brief clear all the options so as to add some new ones in */ + void resetOptions(); + }; + //////////////////////////////////////////////////////////////////////////////// /** @brief Wraps up a boolean param */ class BooleanParam : public ValueParam { @@ -1771,6 +1838,9 @@ namespace OFX { /** @brief Fetch a Choice param */ ChoiceParam *fetchChoiceParam(const std::string &name) const; + /** @brief Fetch a String Choice param */ + StrChoiceParam* fetchStrChoiceParam(const std::string& p_Name) const; + /** @brief Fetch a group param */ GroupParam *fetchGroupParam(const std::string &name) const; diff --git a/release-notes.md b/release-notes.md index 3b2c47fda..eca4d077f 100644 --- a/release-notes.md +++ b/release-notes.md @@ -23,7 +23,10 @@ None ## Detailed List of Changes -- Add OfxDrawSuite for drawing image overlays without use of OpenGL +- Add `OfxSetHost` call +- Add `OfxDrawSuite` for drawing image overlays without use of OpenGL +- Add OpenCL, CUDA and Metal support for direct GPU rendering +- Add :c:macro:`kOfxParamPropChoiceOrder` for reordering Choice Params # Release Notes - 1.4 From 6d500a614aab60903d8dc1c1fbd20dde2f94fd30 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Sat, 2 Mar 2024 17:16:24 -0500 Subject: [PATCH 05/22] Add ChoiceParams example, showing new choice param types Signed-off-by: Gary Oberbrunner --- Examples/CMakeLists.txt | 1 + Examples/ChoiceParams/Info.plist | 20 + Examples/ChoiceParams/Makefile | 6 + Examples/ChoiceParams/MakefileOSX | 7 + Examples/ChoiceParams/choiceparams.cpp | 768 +++++++++++++++++++++++++ 5 files changed, 802 insertions(+) create mode 100644 Examples/ChoiceParams/Info.plist create mode 100644 Examples/ChoiceParams/Makefile create mode 100644 Examples/ChoiceParams/MakefileOSX create mode 100644 Examples/ChoiceParams/choiceparams.cpp diff --git a/Examples/CMakeLists.txt b/Examples/CMakeLists.txt index 3ac069a2e..b6b58982c 100644 --- a/Examples/CMakeLists.txt +++ b/Examples/CMakeLists.txt @@ -3,6 +3,7 @@ file(GLOB_RECURSE OFX_SUPPORT_HEADER_FILES "${OFX_SUPPORT_HEADER_DIR}/*.h") set(PLUGINS Basic + ChoiceParams Custom DepthConverter Invert diff --git a/Examples/ChoiceParams/Info.plist b/Examples/ChoiceParams/Info.plist new file mode 100644 index 000000000..750eda640 --- /dev/null +++ b/Examples/ChoiceParams/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + choiceparams.ofx + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleSignature + ???? + CFBundleVersion + 0.0.1d1 + CSResourcesFileMapped + + + diff --git a/Examples/ChoiceParams/Makefile b/Examples/ChoiceParams/Makefile new file mode 100644 index 000000000..f727d382a --- /dev/null +++ b/Examples/ChoiceParams/Makefile @@ -0,0 +1,6 @@ +PLUGINOBJECTS = choiceparams.o +PLUGINNAME = choiceparams +PATHTOROOT = ../ + +include ../Makefile.master + diff --git a/Examples/ChoiceParams/MakefileOSX b/Examples/ChoiceParams/MakefileOSX new file mode 100644 index 000000000..043910eab --- /dev/null +++ b/Examples/ChoiceParams/MakefileOSX @@ -0,0 +1,7 @@ +CXXFLAGS = -I../../include +OPTIMIZER = -g + +choiceparams.ofx : choiceparams.o + $(CXX) -bundle choiceparams.o -o choiceparams.ofx + mkdir -p choiceparams.ofx.bundle/Contents/MacOS/ + cp choiceparams.ofx choiceparams.ofx.bundle/Contents/MacOS/ diff --git a/Examples/ChoiceParams/choiceparams.cpp b/Examples/ChoiceParams/choiceparams.cpp new file mode 100644 index 000000000..0fa1fff9a --- /dev/null +++ b/Examples/ChoiceParams/choiceparams.cpp @@ -0,0 +1,768 @@ +// Copyright OpenFX and contributors to the OpenFX project. +// SPDX-License-Identifier: BSD-3-Clause + + +/* + OpenFX Example plugin that exercises various forms of Choice Params + + - Regular Choice param + - Choice param with order specified + - StrChoice param + + The latter two are only available in hosts supporting OpenFX 1.5 or later. + */ +#include +#include +#include +#include +#include +#include +#include "ofxImageEffect.h" +#include "ofxMemory.h" +#include "ofxMultiThread.h" + +#include "../include/ofxUtilities.H" // example support utils + +#if defined __APPLE__ || defined __linux__ || defined __FreeBSD__ +# define EXPORT __attribute__((visibility("default"))) +#elif defined _WIN32 +# define EXPORT OfxExport +#else +# error Not building on your operating system quite yet +#endif + +// pointers64 to various bits of the host +OfxHost *gHost; +OfxImageEffectSuiteV1 *gEffectHost = 0; +OfxPropertySuiteV1 *gPropHost = 0; +OfxParameterSuiteV1 *gParamHost = 0; +OfxMemorySuiteV1 *gMemoryHost = 0; +OfxMultiThreadSuiteV1 *gThreadHost = 0; +OfxMessageSuiteV1 *gMessageSuite = 0; +OfxInteractSuiteV1 *gInteractHost = 0; + +// some flags about the host's behaviour +std::string gHostName; +int gHostSupportsMultipleBitDepths = false; +int gHostSupportsStrChoice = false; +bool gHostSupportsChoiceOrder = true; // assume OK + +// private instance data type +struct MyInstanceData { + bool isGeneralEffect; + + // handles to the clips we deal with + OfxImageClipHandle sourceClip; + OfxImageClipHandle outputClip; + + // handles to a our parameters + OfxParamHandle redChoiceParam; + OfxParamHandle greenChoiceParam; + OfxParamHandle blueChoiceParam; +}; + +/* mandatory function to set up the host structures */ + + +// Convenience wrapper to get private data +static MyInstanceData * +getMyInstanceData( OfxImageEffectHandle effect) +{ + // get the property handle for the plugin + OfxPropertySetHandle effectProps; + gEffectHost->getPropertySet(effect, &effectProps); + + // get my data pointer out of that + MyInstanceData *myData = 0; + gPropHost->propGetPointer(effectProps, kOfxPropInstanceData, 0, + (void **) &myData); + return myData; +} + +/** @brief Called at load */ +static OfxStatus +onLoad(void) +{ + return kOfxStatOK; +} + +/** @brief Called before unload */ +static OfxStatus +onUnLoad(void) +{ + return kOfxStatOK; +} + +// instance construction +static OfxStatus +createInstance( OfxImageEffectHandle effect) +{ + // get a pointer to the effect properties + OfxPropertySetHandle effectProps; + gEffectHost->getPropertySet(effect, &effectProps); + + // get a pointer to the effect's parameter set + OfxParamSetHandle paramSet; + gEffectHost->getParamSet(effect, ¶mSet); + + // make private instance data + MyInstanceData *myData = new MyInstanceData; + char *context = 0; + + // is this instance a general effect? + gPropHost->propGetString(effectProps, kOfxImageEffectPropContext, 0, &context); + myData->isGeneralEffect = context && (strcmp(context, kOfxImageEffectContextGeneral) == 0); + + // cache param handles + gParamHost->paramGetHandle(paramSet, "red_choice", &myData->redChoiceParam, 0); + gParamHost->paramGetHandle(paramSet, "green_choice", &myData->greenChoiceParam, 0); + gParamHost->paramGetHandle(paramSet, "blue_choice", &myData->blueChoiceParam, 0); + + // cache clip handles + gEffectHost->clipGetHandle(effect, kOfxImageEffectSimpleSourceClipName, &myData->sourceClip, 0); + gEffectHost->clipGetHandle(effect, kOfxImageEffectOutputClipName, &myData->outputClip, 0); + + // set private instance data + gPropHost->propSetPointer(effectProps, kOfxPropInstanceData, 0, (void *) myData); + + return kOfxStatOK; +} + +// instance destruction +static OfxStatus +destroyInstance( OfxImageEffectHandle effect) +{ + // get my instance data + MyInstanceData *myData = getMyInstanceData(effect); + + // and delete it + if(myData) + delete myData; + return kOfxStatOK; +} + +// tells the host what region we are capable of filling +OfxStatus +getSpatialRoD( OfxImageEffectHandle effect, OfxPropertySetHandle inArgs, OfxPropertySetHandle outArgs) +{ + // retrieve any instance data associated with this effect + MyInstanceData *myData = getMyInstanceData(effect); + + OfxTime time; + gPropHost->propGetDouble(inArgs, kOfxPropTime, 0, &time); + + // my RoD is the same as my input's + OfxRectD rod; + gEffectHost->clipGetRegionOfDefinition(myData->sourceClip, time, &rod); + + // set the rod in the out args + gPropHost->propSetDoubleN(outArgs, kOfxImageEffectPropRegionOfDefinition, 4, &rod.x1); + + return kOfxStatOK; +} + +// tells the host how much of the input we need to fill the given window +OfxStatus +getSpatialRoI( OfxImageEffectHandle effect, OfxPropertySetHandle inArgs, OfxPropertySetHandle outArgs) +{ + // get the RoI the effect is interested in from inArgs + OfxRectD roi; + gPropHost->propGetDoubleN(inArgs, kOfxImageEffectPropRegionOfInterest, 4, &roi.x1); + + // the input needed is the same as the output, so set that on the source clip + gPropHost->propSetDoubleN(outArgs, "OfxImageClipPropRoI_Source", 4, &roi.x1); + + // retrieve any instance data associated with this effect + MyInstanceData *myData = getMyInstanceData(effect); + + return kOfxStatOK; +} + +// Tells the host how many frames we can fill, only called in the general context. +// This is actually redundant as this is the default behaviour, but for illustrative +// purposes. +OfxStatus +getTemporalDomain( OfxImageEffectHandle effect, OfxPropertySetHandle /*inArgs*/, OfxPropertySetHandle outArgs) +{ + MyInstanceData *myData = getMyInstanceData(effect); + + double sourceRange[2]; + + // get the frame range of the source clip + OfxPropertySetHandle props; gEffectHost->clipGetPropertySet(myData->sourceClip, &props); + gPropHost->propGetDoubleN(props, kOfxImageEffectPropFrameRange, 2, sourceRange); + + // set it on the out args + gPropHost->propSetDoubleN(outArgs, kOfxImageEffectPropFrameRange, 2, sourceRange); + + return kOfxStatOK; +} + + +// Set our clip preferences +static OfxStatus +getClipPreferences( OfxImageEffectHandle effect, OfxPropertySetHandle /*inArgs*/, OfxPropertySetHandle outArgs) +{ + // retrieve any instance data associated with this effect + MyInstanceData *myData = getMyInstanceData(effect); + + // get the component type and bit depth of our main input + int bitDepth; + bool isRGBA; + ofxuClipGetFormat(myData->sourceClip, bitDepth, isRGBA, true); // get the unmapped clip component + + // get the strings used to label the various bit depths + const char *bitDepthStr = bitDepth == 8 ? kOfxBitDepthByte : (bitDepth == 16 ? kOfxBitDepthShort : kOfxBitDepthFloat); + + // set out output to be the same same as the input bitdepth + if(gHostSupportsMultipleBitDepths) + gPropHost->propSetString(outArgs, "OfxImageClipPropDepth_Output", 0, bitDepthStr); + + return kOfxStatOK; +} + +// are the settings of the effect performing an identity operation +static OfxStatus +isIdentity( OfxImageEffectHandle effect, + OfxPropertySetHandle inArgs, + OfxPropertySetHandle outArgs) +{ + return kOfxStatReplyDefault; +} + +//////////////////////////////////////////////////////////////////////////////// +// function called when the instance has been changed by anything +static OfxStatus +instanceChanged( OfxImageEffectHandle effect, + OfxPropertySetHandle inArgs, + OfxPropertySetHandle /*outArgs*/) +{ + // see why it changed + char *changeReason; + gPropHost->propGetString(inArgs, kOfxPropChangeReason, 0, &changeReason); + + // we are only interested in user edits + if(strcmp(changeReason, kOfxChangeUserEdited) != 0) return kOfxStatReplyDefault; + + // fetch the type of the object that changed + char *typeChanged; + gPropHost->propGetString(inArgs, kOfxPropType, 0, &typeChanged); + + // was it a clip or a param? + bool isClip = strcmp(typeChanged, kOfxTypeClip) == 0; + bool isParam = strcmp(typeChanged, kOfxTypeParameter) == 0; + + // get the name of the thing that changed + char *objChanged; + gPropHost->propGetString(inArgs, kOfxPropName, 0, &objChanged); + + // don't trap any others + return kOfxStatReplyDefault; +} + +//////////////////////////////////////////////////////////////////////////////// +// rendering routines +template inline T +Clamp(T v, int min, int max) +{ + if(v < T(min)) return T(min); + if(v > T(max)) return T(max); + return v; +} + +// look up a pixel in the image, does bounds checking to see if it is in the image rectangle +template inline PIX * +pixelAddress(PIX *img, OfxRectI rect, int x, int y, int bytesPerLine) +{ + if(x < rect.x1 || x >= rect.x2 || y < rect.y1 || y >= rect.y2 || !img) + return 0; + PIX *pix = (PIX *) (((char *) img) + (y - rect.y1) * bytesPerLine); + pix += x - rect.x1; + return pix; +} + +//////////////////////////////////////////////////////////////////////////////// +// base class to process images with +class Processor { + protected : + OfxImageEffectHandle instance; + float rScale, gScale, bScale, aScale; + void *srcV, *dstV; + OfxRectI srcRect, dstRect; + int srcBytesPerLine, dstBytesPerLine; + OfxRectI window; + + public : + Processor(OfxImageEffectHandle inst, + float rScal, float gScal, float bScal, float aScal, + void *src, OfxRectI sRect, int sBytesPerLine, + void *dst, OfxRectI dRect, int dBytesPerLine, + OfxRectI win) + : instance(inst) + , rScale(rScal) + , gScale(gScal) + , bScale(bScal) + , aScale(aScal) + , srcV(src) + , dstV(dst) + , srcRect(sRect) + , dstRect(dRect) + , srcBytesPerLine(sBytesPerLine) + , dstBytesPerLine(dBytesPerLine) + , window(win) + {} + + static void multiThreadProcessing(unsigned int threadId, unsigned int nThreads, void *arg); + virtual void doProcessing(OfxRectI window) = 0; + void process(void); +}; + + +// function call once for each thread by the host +void +Processor::multiThreadProcessing(unsigned int threadId, unsigned int nThreads, void *arg) +{ + Processor *proc = (Processor *) arg; + + // slice the y range into the number of threads it has + unsigned int dy = proc->window.y2 - proc->window.y1; + + unsigned int y1 = proc->window.y1 + threadId * dy/nThreads; + unsigned int y2 = proc->window.y1 + std::min((threadId + 1) * dy/nThreads, dy); + + OfxRectI win = proc->window; + win.y1 = y1; win.y2 = y2; + + // and render that thread on each + proc->doProcessing(win); +} + +// function to kick off rendering across multiple CPUs +void +Processor::process(void) +{ + unsigned int nThreads; + gThreadHost->multiThreadNumCPUs(&nThreads); + gThreadHost->multiThread(multiThreadProcessing, nThreads, (void *) this); +} + +// template to do the RGBA processing +template +class ProcessRGBA : public Processor{ +public : + ProcessRGBA(OfxImageEffectHandle instance, + float rScale, float gScale, float bScale, float aScale, + void *srcV, OfxRectI srcRect, int srcBytesPerLine, + void *dstV, OfxRectI dstRect, int dstBytesPerLine, + OfxRectI window) + : Processor(instance, + rScale, gScale, bScale, aScale, + srcV, srcRect, srcBytesPerLine, + dstV, dstRect, dstBytesPerLine, + window) + { + } + + void doProcessing(OfxRectI procWindow) + { + PIX *src = (PIX *) srcV; + PIX *dst = (PIX *) dstV; + + for(int y = procWindow.y1; y < procWindow.y2; y++) { + if(gEffectHost->abort(instance)) break; + + PIX *dstPix = pixelAddress(dst, dstRect, procWindow.x1, y, dstBytesPerLine); + + for(int x = procWindow.x1; x < procWindow.x2; x++) { + + PIX *srcPix = pixelAddress(src, srcRect, x, y, srcBytesPerLine); + + // figure the scale values per component + float sR = 1.0 + (rScale - 1.0); + float sG = 1.0 + (gScale - 1.0); + float sB = 1.0 + (bScale - 1.0); + float sA = 1.0 + (aScale - 1.0); + + if(srcPix) { + // switch will be compiled out + if(isFloat) { + dstPix->r = srcPix->r * sR; + dstPix->g = srcPix->g * sG; + dstPix->b = srcPix->b * sB; + dstPix->a = srcPix->a * sA; + } + else { + dstPix->r = Clamp(int(srcPix->r * sR), 0, max); + dstPix->g = Clamp(int(srcPix->g * sG), 0, max); + dstPix->b = Clamp(int(srcPix->b * sB), 0, max); + dstPix->a = Clamp(int(srcPix->a * sA), 0, max); + } + srcPix++; + } + else { + dstPix->r = dstPix->g = dstPix->b = dstPix->a= 0; + } + dstPix++; + } + } + } +}; + +// the process code that the host sees +static OfxStatus render( OfxImageEffectHandle instance, + OfxPropertySetHandle inArgs, + OfxPropertySetHandle /*outArgs*/) +{ + // get the render window and the time from the inArgs + OfxTime time; + OfxRectI renderWindow; + OfxStatus status = kOfxStatOK; + + gPropHost->propGetDouble(inArgs, kOfxPropTime, 0, &time); + gPropHost->propGetIntN(inArgs, kOfxImageEffectPropRenderWindow, 4, &renderWindow.x1); + + // retrieve any instance data associated with this effect + MyInstanceData *myData = getMyInstanceData(instance); + + // property handles and members of each image + // in reality, we would put this in a struct as the C++ support layer does + OfxPropertySetHandle sourceImg = NULL, outputImg = NULL; + int srcRowBytes, srcBitDepth, dstRowBytes, dstBitDepth; + OfxRectI dstRect, srcRect; + void *src, *dst; + + try { + // get the source image + bool _isAlpha; + sourceImg = ofxuGetImage(myData->sourceClip, time, srcRowBytes, srcBitDepth, _isAlpha, srcRect, src); + if(sourceImg == NULL) throw OfxuNoImageException(); + + // get the output image + outputImg = ofxuGetImage(myData->outputClip, time, dstRowBytes, dstBitDepth, _isAlpha, dstRect, dst); + if(outputImg == NULL) throw OfxuNoImageException(); + + // see if they have the same depths and bytes and all + if(srcBitDepth != dstBitDepth) { + throw OfxuStatusException(kOfxStatErrImageFormat); + } + + float rScale, gScale, bScale, aScale; + + int rChoice; + int gChoice; + gParamHost->paramGetValueAtTime(myData->redChoiceParam, time, &rChoice); + gParamHost->paramGetValueAtTime(myData->greenChoiceParam, time, &gChoice); + std::string blueChoice = "blue_1.0"; + if (gHostSupportsStrChoice) { + char *bChoice; // NOTE: string! + gParamHost->paramGetValueAtTime(myData->blueChoiceParam, time, &bChoice); + blueChoice = bChoice; + } + + if (rChoice == 0) + rScale = 0; + if (rChoice == 1) + rScale = 0.5; + if (rChoice == 2) + rScale = 1.0; + + if (gChoice == 0) + gScale = 0; + if (gChoice == 1) + gScale = 0.5; + if (gChoice == 2) + gScale = 1.0; + + // string-valued param + if (blueChoice == "blue_0.0") + bScale = 0; + if (blueChoice == "blue_0.5") + bScale = 0.5; + if (blueChoice == "blue_1.0") + bScale = 1.0; + + // always + aScale = 1.0; + + // do the rendering + switch(dstBitDepth) { + case 8 : { + ProcessRGBA fred(instance, rScale, gScale, bScale, aScale, + src, srcRect, srcRowBytes, + dst, dstRect, dstRowBytes, + renderWindow); + fred.process(); + } + break; + + case 16 : { + ProcessRGBA fred(instance, rScale, gScale, bScale, aScale, + src, srcRect, srcRowBytes, + dst, dstRect, dstRowBytes, + renderWindow); + fred.process(); + } + break; + + case 32 : { + ProcessRGBA fred(instance, rScale, gScale, bScale, aScale, + src, srcRect, srcRowBytes, + dst, dstRect, dstRowBytes, + renderWindow); + fred.process(); + break; + } + } + } + catch(OfxuNoImageException &ex) { + // if we were interrupted, the failed fetch is fine, just return kOfxStatOK + // otherwise, something weird happened + if(!gEffectHost->abort(instance)) { + status = kOfxStatFailed; + } + } + catch(OfxuStatusException &ex) { + status = ex.status(); + } + + // release the data pointers + if(sourceImg) + gEffectHost->clipReleaseImage(sourceImg); + if(outputImg) + gEffectHost->clipReleaseImage(outputImg); + + return status; +} + +// describe the plugin in context +static OfxStatus +describeInContext( OfxImageEffectHandle effect, OfxPropertySetHandle inArgs) +{ + // get the context from the inArgs handle + char *context; + gPropHost->propGetString(inArgs, kOfxImageEffectPropContext, 0, &context); + bool isGeneralContext = strcmp(context, kOfxImageEffectContextGeneral) == 0; + + OfxPropertySetHandle props; + // define the single output clip in both contexts + gEffectHost->clipDefine(effect, kOfxImageEffectOutputClipName, &props); + + // set the component types we can handle on out output + gPropHost->propSetString(props, kOfxImageEffectPropSupportedComponents, 0, kOfxImageComponentRGBA); + + // define the single source clip in both contexts + gEffectHost->clipDefine(effect, kOfxImageEffectSimpleSourceClipName, &props); + + // set the component types we can handle on our main input + gPropHost->propSetString(props, kOfxImageEffectPropSupportedComponents, 0, kOfxImageComponentRGBA); + + //////////////////////////////////////////////////////////////////////////////// + // define the parameters for this context + // fetch the parameter set from the effect + OfxParamSetHandle paramSet; + gEffectHost->getParamSet(effect, ¶mSet); + + // First choice param + gParamHost->paramDefine(paramSet, kOfxParamTypeChoice, "red_choice", &props); + gPropHost->propSetInt(props, kOfxParamPropDefault, 0, 0); + gPropHost->propSetString(props, kOfxParamPropScriptName, 0, "red_choice"); + gPropHost->propSetString(props, kOfxPropLabel, 0, "Red Choice Param"); + gPropHost->propSetString(props, kOfxParamPropChoiceOption, 0, "Red: none"); + gPropHost->propSetString(props, kOfxParamPropChoiceOption, 1, "Red: some"); + gPropHost->propSetString(props, kOfxParamPropChoiceOption, 2, "Red: lots"); + + + // Second choice param: using order. + // This will produce incorrect results in hosts that don't support order. + gParamHost->paramDefine(paramSet, kOfxParamTypeChoice, "green_choice", &props); + gPropHost->propSetInt(props, kOfxParamPropDefault, 0, 0); + gPropHost->propSetString(props, kOfxParamPropScriptName, 0, "green_choice"); + gPropHost->propSetString(props, kOfxPropLabel, 0, "Green Choice Param"); + gPropHost->propSetString(props, kOfxParamPropChoiceOption, 0, "Green: none"); + gPropHost->propSetString(props, kOfxParamPropChoiceOption, 1, "Green: lots"); + gPropHost->propSetString(props, kOfxParamPropChoiceOption, 2, "Green: some"); + // Order sets the display order: choices will be displayed in this order + auto stat = gPropHost->propSetInt(props, kOfxParamPropChoiceOrder, 0, 0); // first + if (stat == kOfxStatOK) { + gPropHost->propSetInt(props, kOfxParamPropChoiceOrder, 1, 2); // last + gPropHost->propSetInt(props, kOfxParamPropChoiceOrder, 2, 1); // middle + } else { + gHostSupportsChoiceOrder = false; + std::cout << "Host does not support kOfxParamPropChoiceOrder: green results will look wrong\n"; + } + + // Third choice param: using StrChoice, string-valued choice param + if (gHostSupportsStrChoice) { + gParamHost->paramDefine(paramSet, kOfxParamTypeStrChoice, "blue_choice", &props); + gPropHost->propSetString(props, kOfxParamPropDefault, 0, "blue_1.0"); + gPropHost->propSetString(props, kOfxParamPropScriptName, 0, "blue_choice"); + gPropHost->propSetString(props, kOfxPropLabel, 0, "Blue Choice Param"); + gPropHost->propSetString(props, kOfxParamPropChoiceOption, 0, "Blue: none"); + gPropHost->propSetString(props, kOfxParamPropChoiceOption, 1, "Blue: some"); + gPropHost->propSetString(props, kOfxParamPropChoiceOption, 2, "Blue: lots"); + // host will return and store these values + gPropHost->propSetString(props, kOfxParamPropChoiceEnum, 0, "blue_0.0"); + gPropHost->propSetString(props, kOfxParamPropChoiceEnum, 1, "blue_0.5"); + gPropHost->propSetString(props, kOfxParamPropChoiceEnum, 2, "blue_1.0"); + } + + // make a page of controls and add my parameters to it + gParamHost->paramDefine(paramSet, kOfxParamTypePage, "Main", &props); + gPropHost->propSetString(props, kOfxParamPropPageChild, 0, "red_choice"); + gPropHost->propSetString(props, kOfxParamPropPageChild, 1, "green_choice"); + gPropHost->propSetString(props, kOfxParamPropPageChild, 2, "blue_choice"); + + return kOfxStatOK; +} + +//////////////////////////////////////////////////////////////////////////////// +// the plugin's description routine +static OfxStatus +describe(OfxImageEffectHandle effect) +{ + // first fetch the host APIs, this cannot be done before this call + OfxStatus stat; + if((stat = ofxuFetchHostSuites()) != kOfxStatOK) + return stat; + + // record a few host features + gPropHost->propGetInt(gHost->host, kOfxImageEffectPropSupportsMultipleClipDepths, 0, &gHostSupportsMultipleBitDepths); + gPropHost->propGetInt(gHost->host, kOfxParamHostPropSupportsStrChoice, 0, &gHostSupportsStrChoice); + { + char *hostName; + gPropHost->propGetString(gHost->host, kOfxPropName, 0, &hostName); + gHostName = hostName; + if (gHostName == "DaVinciResolve") + gHostSupportsStrChoice = true; // As of version 18.1, Resolve supports StrChoice but not yet the support prop + } + + // get the property handle for the plugin + OfxPropertySetHandle effectProps; + gEffectHost->getPropertySet(effect, &effectProps); + + // We can render both fields in a fielded images in one hit if there is no animation + // So set the flag that allows us to do this + gPropHost->propSetInt(effectProps, kOfxImageEffectPluginPropFieldRenderTwiceAlways, 0, 0); + + // say we can support multiple pixel depths and let the clip preferences action deal with it all. + gPropHost->propSetInt(effectProps, kOfxImageEffectPropSupportsMultipleClipDepths, 0, 1); + + // set the bit depths the plugin can handle + gPropHost->propSetString(effectProps, kOfxImageEffectPropSupportedPixelDepths, 0, kOfxBitDepthByte); + gPropHost->propSetString(effectProps, kOfxImageEffectPropSupportedPixelDepths, 1, kOfxBitDepthShort); + gPropHost->propSetString(effectProps, kOfxImageEffectPropSupportedPixelDepths, 2, kOfxBitDepthFloat); + + // set some labels and the group it belongs to + gPropHost->propSetString(effectProps, kOfxPropLabel, 0, "OFX Choice Param Example"); + gPropHost->propSetString(effectProps, kOfxImageEffectPluginPropGrouping, 0, "OFX Example"); + + // define the contexts we can be used in + gPropHost->propSetString(effectProps, kOfxImageEffectPropSupportedContexts, 0, kOfxImageEffectContextFilter); + gPropHost->propSetString(effectProps, kOfxImageEffectPropSupportedContexts, 1, kOfxImageEffectContextGeneral); + + return kOfxStatOK; +} + +//////////////////////////////////////////////////////////////////////////////// +// The main function +static OfxStatus +pluginMain(const char *action, const void *handle, OfxPropertySetHandle inArgs, OfxPropertySetHandle outArgs) +{ + try { + // cast to appropriate type + OfxImageEffectHandle effect = (OfxImageEffectHandle) handle; + + if(strcmp(action, kOfxActionDescribe) == 0) { + return describe(effect); + } + else if(strcmp(action, kOfxImageEffectActionDescribeInContext) == 0) { + return describeInContext(effect, inArgs); + } + else if(strcmp(action, kOfxActionLoad) == 0) { + return onLoad(); + } + else if(strcmp(action, kOfxActionUnload) == 0) { + return onUnLoad(); + } + else if(strcmp(action, kOfxActionCreateInstance) == 0) { + return createInstance(effect); + } + else if(strcmp(action, kOfxActionDestroyInstance) == 0) { + return destroyInstance(effect); + } + else if(strcmp(action, kOfxImageEffectActionIsIdentity) == 0) { + return isIdentity(effect, inArgs, outArgs); + } + else if(strcmp(action, kOfxImageEffectActionRender) == 0) { + return render(effect, inArgs, outArgs); + } + else if(strcmp(action, kOfxImageEffectActionGetRegionOfDefinition) == 0) { + return getSpatialRoD(effect, inArgs, outArgs); + } + else if(strcmp(action, kOfxImageEffectActionGetRegionsOfInterest) == 0) { + return getSpatialRoI(effect, inArgs, outArgs); + } + else if(strcmp(action, kOfxImageEffectActionGetClipPreferences) == 0) { + return getClipPreferences(effect, inArgs, outArgs); + } + else if(strcmp(action, kOfxActionInstanceChanged) == 0) { + return instanceChanged(effect, inArgs, outArgs); + } + else if(strcmp(action, kOfxImageEffectActionGetTimeDomain) == 0) { + return getTemporalDomain(effect, inArgs, outArgs); + } + } catch (std::bad_alloc) { + // catch memory + //std::cout << "OFX Plugin Memory error." << std::endl; + return kOfxStatErrMemory; + } catch ( const std::exception& e ) { + // standard exceptions + //std::cout << "OFX Plugin error: " << e.what() << std::endl; + return kOfxStatErrUnknown; + } catch (int err) { + // ho hum, gone wrong somehow + return err; + } catch ( ... ) { + // everything else + //std::cout << "OFX Plugin error" << std::endl; + return kOfxStatErrUnknown; + } + + // other actions to take the default value + return kOfxStatReplyDefault; +} + +// function to set the host structure +static void +setHostFunc(OfxHost *hostStruct) +{ + gHost = hostStruct; +} + +//////////////////////////////////////////////////////////////////////////////// +// the plugin struct +static OfxPlugin basicPlugin = +{ + kOfxImageEffectPluginApi, + 1, + "org.openfx.ChoiceParamPlugin", + 1, + 0, + setHostFunc, + pluginMain +}; + +// the two mandated functions +EXPORT OfxPlugin * +OfxGetPlugin(int nth) +{ + if(nth == 0) + return &basicPlugin; + return 0; +} + +EXPORT int +OfxGetNumberOfPlugins(void) +{ + return 1; +} From 613d00e7b13dce08200fe126e889ce83b33f3403 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Sun, 3 Mar 2024 10:00:01 -0500 Subject: [PATCH 06/22] Add support in Support lib for ChoiceParam order Signed-off-by: Gary Oberbrunner --- Support/Library/ofxsParams.cpp | 12 ++++++++++-- Support/Plugins/CMakeLists.txt | 1 + Support/include/ofxsParam.h | 4 ++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Support/Library/ofxsParams.cpp b/Support/Library/ofxsParams.cpp index a8238c96b..b47b0d7d1 100644 --- a/Support/Library/ofxsParams.cpp +++ b/Support/Library/ofxsParams.cpp @@ -751,7 +751,7 @@ namespace OFX { } /** @brief append an option to the choice param */ - void ChoiceParamDescriptor::appendOption(const std::string &v, const std::string& label) + void ChoiceParamDescriptor::appendOption(const std::string &v, const std::string& label, const int order) { int nCurrentValues = _paramProps.propGetDimension(kOfxParamPropChoiceOption); _paramProps.propSetString(kOfxParamPropChoiceOption, v, nCurrentValues); @@ -770,6 +770,10 @@ namespace OFX { _paramProps.propSetString(kOfxParamPropHint, hint); } } + if (order >= 0) { + // Host may not support this prop (added in 1.5); continue without it. + _paramProps.propSetInt(kOfxParamPropChoiceOrder, order, false); + } } /** @brief reset all options */ @@ -2467,7 +2471,7 @@ namespace OFX { } /** @brief add another option */ - void ChoiceParam::appendOption(const std::string &v, const std::string& label) + void ChoiceParam::appendOption(const std::string &v, const std::string& label, const int order) { int nCurrentValues = _paramProps.propGetDimension(kOfxParamPropChoiceOption); _paramProps.propSetString(kOfxParamPropChoiceOption, v, nCurrentValues); @@ -2486,6 +2490,10 @@ namespace OFX { _paramProps.propSetString(kOfxParamPropHint, hint); } } + if (order >= 0) { + // Host may not support this prop (added in 1.5); continue without it. + _paramProps.propSetInt(kOfxParamPropChoiceOrder, order, false); + } } /** @brief set the string of a specific option */ diff --git a/Support/Plugins/CMakeLists.txt b/Support/Plugins/CMakeLists.txt index 0bb2493b6..09a5b8d45 100644 --- a/Support/Plugins/CMakeLists.txt +++ b/Support/Plugins/CMakeLists.txt @@ -2,6 +2,7 @@ include(OpenFX) set(PLUGINS Basic + ChoiceParams Field Generator Invert diff --git a/Support/include/ofxsParam.h b/Support/include/ofxsParam.h index aa7e776fe..c50075ed0 100644 --- a/Support/include/ofxsParam.h +++ b/Support/include/ofxsParam.h @@ -580,7 +580,7 @@ namespace OFX { void setDefault(int v); /** @brief append an option, default is to have not there */ - void appendOption(const std::string &v, const std::string& label = ""); + void appendOption(const std::string &v, const std::string& label = "", const int order = -1); /** @brief how many options do we have */ int getNOptions(void); @@ -1499,7 +1499,7 @@ namespace OFX { int getNOptions(void); /** @brief append an option, default is to have not there */ - void appendOption(const std::string &v, const std::string& label = ""); + void appendOption(const std::string &v, const std::string& label = "", const int order = -1); /** @brief set an option */ void setOption(int item, const std::string &str); From 4627acaf8b78c8509a09557910089cb9b40e3740 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Sun, 3 Mar 2024 10:01:05 -0500 Subject: [PATCH 07/22] Add Support example for ChoiceParams, showing order and StrChoice Signed-off-by: Gary Oberbrunner --- Support/Plugins/ChoiceParams/Info.plist | 20 + Support/Plugins/ChoiceParams/Makefile | 6 + Support/Plugins/ChoiceParams/choiceparams.cpp | 668 ++++++++++++++++++ 3 files changed, 694 insertions(+) create mode 100644 Support/Plugins/ChoiceParams/Info.plist create mode 100644 Support/Plugins/ChoiceParams/Makefile create mode 100644 Support/Plugins/ChoiceParams/choiceparams.cpp diff --git a/Support/Plugins/ChoiceParams/Info.plist b/Support/Plugins/ChoiceParams/Info.plist new file mode 100644 index 000000000..21152b3e8 --- /dev/null +++ b/Support/Plugins/ChoiceParams/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + basic.ofx + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleSignature + ???? + CFBundleVersion + 0.0.1d1 + CSResourcesFileMapped + + + diff --git a/Support/Plugins/ChoiceParams/Makefile b/Support/Plugins/ChoiceParams/Makefile new file mode 100644 index 000000000..ab80b9391 --- /dev/null +++ b/Support/Plugins/ChoiceParams/Makefile @@ -0,0 +1,6 @@ +PLUGINOBJECTS = choiceparams.o +PLUGINNAME = choiceparams +PATHTOROOT = ../../ + +include ../Makefile.master + diff --git a/Support/Plugins/ChoiceParams/choiceparams.cpp b/Support/Plugins/ChoiceParams/choiceparams.cpp new file mode 100644 index 000000000..c44d393bb --- /dev/null +++ b/Support/Plugins/ChoiceParams/choiceparams.cpp @@ -0,0 +1,668 @@ +// Copyright OpenFX and contributors to the OpenFX project. +// SPDX-License-Identifier: BSD-3-Clause + +#ifdef _WINDOWS +#include +#endif + +#ifdef __APPLE__ +#include +#else +#include +#endif + +#include +#include "ofxsImageEffect.h" + +#include "../include/ofxsProcessing.H" + +//////////////////////////////////////////////////////////////////////////////// +// a dumb interact that just draw's a square you can drag +static const OfxPointD kBoxSize = {20, 20}; + +class ChoiceParamsInteract : public OFX::OverlayInteract { +protected : + enum StateEnum { + eInActive, + ePoised, + ePicked + }; + + OfxPointD _position; + StateEnum _state; + +public : + ChoiceParamsInteract(OfxInteractHandle handle, OFX::ImageEffect* /*effect*/) + : OFX::OverlayInteract(handle) + , _state(eInActive) + { + _position.x = 0; + _position.y = 0; + } + + // overridden functions from OFX::Interact to do things + virtual bool draw(const OFX::DrawArgs &args); + virtual bool penMotion(const OFX::PenArgs &args); + virtual bool penDown(const OFX::PenArgs &args); + virtual bool penUp(const OFX::PenArgs &args); +}; + +//////////////////////////////////////////////////////////////////////////////// +// rendering routines +template inline T +Minimum(T a, T b) { return (a < b) ? a : b;} + +template inline T +Absolute(T a) { return (a < 0) ? -a : a;} + +template inline T +Clamp(T v, int min, int max) +{ + if(v < T(min)) return T(min); + if(v > T(max)) return T(max); + return v; +} + +// Base class for the RGBA and the Alpha processor +class ImageScalerBase : public OFX::ImageProcessor { +protected : + OFX::Image *_srcImg; + OFX::Image *_maskImg; + double _rScale, _gScale, _bScale, _aScale; + bool _doMasking; + +public : + /** @brief no arg ctor */ + ImageScalerBase(OFX::ImageEffect &instance) + : OFX::ImageProcessor(instance) + , _srcImg(0) + , _maskImg(0) + , _rScale(1) + , _gScale(1) + , _bScale(1) + , _aScale(1) + , _doMasking(false) + { + } + + /** @brief set the src image */ + void setSrcImg(OFX::Image *v) {_srcImg = v;} + + /** @brief set the optional mask image */ + void setMaskImg(OFX::Image *v) {_maskImg = v;} + + // Are we masking. We can't derive this from the mask image being set as NULL is a valid value for an input image + void doMasking(bool v) {_doMasking = v;} + + + /** @brief set the scale */ + void setScales(float r, float g, float b, float a) + { + _rScale = r; + _gScale = g; + _bScale = b; + _aScale = a; + } + +}; + +// template to do the RGBA processing +template +class ImageScaler : public ImageScalerBase { +public : + // ctor + ImageScaler(OFX::ImageEffect &instance) + : ImageScalerBase(instance) + {} + + // and do some processing + void multiThreadProcessImages(OfxRectI procWindow) + { + float scales[4]; + scales[0] = nComponents == 1 ? (float)_aScale : (float)_rScale; + scales[1] = (float)_gScale; + scales[2] = (float)_bScale; + scales[3] = (float)_aScale; + + float maskScale = 1.0f; + + for(int y = procWindow.y1; y < procWindow.y2; y++) { + if(_effect.abort()) break; + + PIX *dstPix = (PIX *) _dstImg->getPixelAddress(procWindow.x1, y); + + for(int x = procWindow.x1; x < procWindow.x2; x++) { + + PIX *srcPix = (PIX *) (_srcImg ? _srcImg->getPixelAddress(x, y) : 0); + + // are we doing masking + if(_doMasking) { + // we do, get the pixel from the mask + if(!_maskImg) + maskScale = 1.0f; + else + { + PIX *maskPix = (PIX *) (_maskImg ? _maskImg->getPixelAddress(x, y) : 0); + // figure the scale factor from that pixel + maskScale = maskPix != 0 ? float(*maskPix)/float(max) : 0.0f; + } + } + + // do we have a source image to scale up + if(srcPix) { + for(int c = 0; c < nComponents; c++) { + float v; + + // scale the component up by the scale factor, modulated by the maskScale + if(maskScale != 1.0f) + v = srcPix[c] * (1.0f + (scales[c] - 1.0f) * maskScale); + else + v = srcPix[c] * scales[c]; + + if(max == 1) // implies floating point and so no clamping + dstPix[c] = PIX(v); + else // integer based and we need to clamp + dstPix[c] = PIX(Clamp(v, 0, max)); + } + } + else { + // no src pixel here, be black and transparent + for(int c = 0; c < nComponents; c++) { + dstPix[c] = 0; + } + } + // increment the dst pixel + dstPix += nComponents; + } + } + } +}; + +//////////////////////////////////////////////////////////////////////////////// +/** @brief The plugin that does our work */ +class ChoiceParamsPlugin : public OFX::ImageEffect { +protected : + // do not need to delete these, the ImageEffect is managing them for us + OFX::Clip *dstClip_; + OFX::Clip *srcClip_; + OFX::Clip *maskClip_; + + OFX::ChoiceParam *red_choice_; + OFX::ChoiceParam *green_choice_; + OFX::StrChoiceParam *blue_choice_; + +public : + /** @brief ctor */ + ChoiceParamsPlugin(OfxImageEffectHandle handle) + : ImageEffect(handle) + , dstClip_(0) + , srcClip_(0) + , red_choice_(0) + , green_choice_(0) + , blue_choice_() + { + dstClip_ = fetchClip(kOfxImageEffectOutputClipName); + srcClip_ = fetchClip(kOfxImageEffectSimpleSourceClipName); + // name of mask clip depends on the context + maskClip_ = getContext() == OFX::eContextFilter ? NULL : fetchClip(getContext() == OFX::eContextPaint ? "Brush" : "Mask"); + red_choice_ = fetchChoiceParam("red_choice"); + green_choice_ = fetchChoiceParam("green_choice"); + blue_choice_ = fetchStrChoiceParam("blue_choice"); + } + + /* Override the render */ + virtual void render(const OFX::RenderArguments &args); + + /* override is identity */ + virtual bool isIdentity(const OFX::IsIdentityArguments &args, OFX::Clip * &identityClip, double &identityTime); + + /* override changedParam */ + virtual void changedParam(const OFX::InstanceChangedArgs &args, const std::string ¶mName); + + /* override changed clip */ + virtual void changedClip(const OFX::InstanceChangedArgs &args, const std::string &clipName); + + // override the rod call + virtual bool getRegionOfDefinition(const OFX::RegionOfDefinitionArguments &args, OfxRectD &rod); + + // override the roi call + virtual void getRegionsOfInterest(const OFX::RegionsOfInterestArguments &args, OFX::RegionOfInterestSetter &rois); + + /* set up and run a processor */ + void + setupAndProcess(ImageScalerBase &, const OFX::RenderArguments &args); +}; + + +//////////////////////////////////////////////////////////////////////////////// +/** @brief render for the filter */ + +//////////////////////////////////////////////////////////////////////////////// +// basic plugin render function, just a skelington to instantiate templates from + + +/* set up and run a processor */ +void +ChoiceParamsPlugin::setupAndProcess(ImageScalerBase &processor, const OFX::RenderArguments &args) +{ + // get a dst image + std::unique_ptr dst(dstClip_->fetchImage(args.time)); + OFX::BitDepthEnum dstBitDepth = dst->getPixelDepth(); + OFX::PixelComponentEnum dstComponents = dst->getPixelComponents(); + + // fetch main input image + std::unique_ptr src(srcClip_->fetchImage(args.time)); + + // make sure bit depths are sane + if(src.get()) { + OFX::BitDepthEnum srcBitDepth = src->getPixelDepth(); + OFX::PixelComponentEnum srcComponents = src->getPixelComponents(); + + // see if they have the same depths and bytes and all + if(srcBitDepth != dstBitDepth || srcComponents != dstComponents) + throw int(1); // HACK!! need to throw an sensible exception here! + } + + std::unique_ptr mask; + + // do we do masking + if(getContext() != OFX::eContextFilter) { + mask.reset(maskClip_->fetchImage(args.time)); + // say we are masking + processor.doMasking(true); + + // Set it in the processor + processor.setMaskImg(mask.get()); + } + + // get the scale parameter values... + + int ri = 0; + int gi = 0; + std::string bi; + double r = 0, g = 0, b = 0, a = 0; + + red_choice_->getValueAtTime(args.time, ri); + green_choice_->getValueAtTime(args.time, gi); + blue_choice_->getValueAtTime(args.time, bi); + + if (ri == 0) + r = 0; + if (ri == 1) + r = 0.5; + if (ri == 2) + r = 1.0; + + if (gi == 0) + g = 0; + if (gi == 1) + g = 0.5; + if (gi == 2) + g = 1.0; + + if (bi == "blue_0.0") + b = 0; + if (bi == "blue_0.5") + b = 0.5; + if (bi == "blue_1.0") + b = 1.0; + + a = 1.0; // always + + // set the images + processor.setDstImg(dst.get()); + processor.setSrcImg(src.get()); + + + // set the render window + processor.setRenderWindow(args.renderWindow); + + // set the scales + processor.setScales((float)r, (float)g, (float)b, (float)a); + + // Call the base class process member, this will call the derived templated process code + processor.process(); +} + +// override the rod call +bool +ChoiceParamsPlugin::getRegionOfDefinition(const OFX::RegionOfDefinitionArguments &args, OfxRectD &rod) +{ + // our RoD is the same as the 'Source' clip's, we are not interested in the mask + rod = srcClip_->getRegionOfDefinition(args.time); + + // say we set it + return true; +} + +// override the roi call +void +ChoiceParamsPlugin::getRegionsOfInterest(const OFX::RegionsOfInterestArguments &args, OFX::RegionOfInterestSetter &rois) +{ + // we don't actually need to do this as this is the default, but do it for examples sake + rois.setRegionOfInterest(*srcClip_, args.regionOfInterest); + + // set it on the mask only if we are in an interesting context + if(getContext() != OFX::eContextFilter) + rois.setRegionOfInterest(*maskClip_, args.regionOfInterest); +} + +// the overridden render function +void +ChoiceParamsPlugin::render(const OFX::RenderArguments &args) +{ + // instantiate the render code based on the pixel depth of the dst clip + OFX::BitDepthEnum dstBitDepth = dstClip_->getPixelDepth(); + OFX::PixelComponentEnum dstComponents = dstClip_->getPixelComponents(); + + // do the rendering + if(dstComponents == OFX::ePixelComponentRGBA) { + switch(dstBitDepth) { +case OFX::eBitDepthUByte : { + ImageScaler fred(*this); + setupAndProcess(fred, args); + } + break; + +case OFX::eBitDepthUShort : { + ImageScaler fred(*this); + setupAndProcess(fred, args); + } + break; + +case OFX::eBitDepthFloat : { + ImageScaler fred(*this); + setupAndProcess(fred, args); + } + break; +default : + OFX::throwSuiteStatusException(kOfxStatErrUnsupported); + } + } + else { + switch(dstBitDepth) { +case OFX::eBitDepthUByte : { + ImageScaler fred(*this); + setupAndProcess(fred, args); + } + break; + +case OFX::eBitDepthUShort : { + ImageScaler fred(*this); + setupAndProcess(fred, args); + } + break; + +case OFX::eBitDepthFloat : { + ImageScaler fred(*this); + setupAndProcess(fred, args); + } + break; +default : + OFX::throwSuiteStatusException(kOfxStatErrUnsupported); + } + } +} + +// overridden is identity +bool +ChoiceParamsPlugin:: isIdentity(const OFX::IsIdentityArguments &args, OFX::Clip * &identityClip, double &identityTime) +{ + return false; +} + +// we have changed a param +void +ChoiceParamsPlugin::changedParam(const OFX::InstanceChangedArgs &/*args*/, const std::string ¶mName) +{ +} + +// we have changed a param +void +ChoiceParamsPlugin::changedClip(const OFX::InstanceChangedArgs &/*args*/, const std::string &clipName) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// stuff for the interact + +// draw the interact +bool ChoiceParamsInteract::draw(const OFX::DrawArgs &args) +{ + OfxRGBColourF col; + switch(_state) + { + case eInActive : col.r = col.g = col.b = 0.0f; break; + case ePoised : col.r = col.g = col.b = 0.5f; break; + case ePicked : col.r = col.g = col.b = 1.0f; break; + } + + // make the box a constant size on screen by scaling by the pixel scale + float dx = (float)(kBoxSize.x * args.pixelScale.x); + float dy = (float)(kBoxSize.y * args.pixelScale.y); + + // Draw a cross hair, the current coordinate system aligns with the image plane. + glPushMatrix(); + + // draw the bo + glColor3f(col.r, col.g, col.b); + glTranslated(_position.x, _position.y, 0); + glBegin(GL_POLYGON); + glVertex2f(-dx, -dy); + glVertex2f(-dx, dy); + glVertex2f( dx, dy); + glVertex2f( dx, -dy); + glEnd(); + glPopMatrix(); + + glPushMatrix(); + // draw a complementary outline + glColor3f(1.0f - col.r, 1.0f - col.g, 1.0f - col.b); + glTranslated(_position.x, _position.y, 0); + glBegin(GL_LINE_LOOP); + glVertex2f(-dx, -dy); + glVertex2f(-dx, dy); + glVertex2f( dx, dy); + glVertex2f( dx, -dy); + glEnd(); + glPopMatrix(); + + return true; +} + +// overridden functions from OFX::Interact to do things +bool +ChoiceParamsInteract::penMotion(const OFX::PenArgs &args) +{ + // figure the size of the box in cannonical coords + float dx = (float)(kBoxSize.x * args.pixelScale.x); + float dy = (float)(kBoxSize.y * args.pixelScale.y); + + // pen position is in cannonical coords + OfxPointD penPos = args.penPosition; + + switch(_state) { +case eInActive : +case ePoised : + { + // are we in the box, become 'poised' + StateEnum newState; + penPos.x -= _position.x; + penPos.y -= _position.y; + if(Absolute(penPos.x) < dx && + Absolute(penPos.y) < dy) { + newState = ePoised; + } + else { + newState = eInActive; + } + + if(_state != newState) { + // we have a new state + _state = newState; + + // and force an overlay redraw + _effect->redrawOverlays(); + } + } + break; + +case ePicked : + { + // move our position + _position = penPos; + + // and force an overlay redraw + _effect->redrawOverlays(); + } + break; + } + + // we have trapped it only if the mouse ain't over it or we are actively dragging + return _state != eInActive; +} + +bool +ChoiceParamsInteract::penDown(const OFX::PenArgs &args) +{ + // this will refigure the state + penMotion(args); + + // if poised means we were over it when the pen went down, so pick it + if(_state == ePoised) { + // we are now picked + _state = ePicked; + + // move our position + _position = args.penPosition; + + // and request a redraw just incase + _effect->redrawOverlays(); + } + + return _state == ePicked; +} + +bool +ChoiceParamsInteract::penUp(const OFX::PenArgs &args) +{ + if(_state == ePicked) { + // reset to poised for a moment + _state = ePoised; + + // this will refigure the state + penMotion(args); + + // and redraw for good measure + _effect->redrawOverlays(); + + // we did trap it + return true; + } + + // we didn't trap it + return false; +} + + +using namespace OFX; + +mDeclarePluginFactory(ChoiceParamsExamplePluginFactory, {}, {}); + +class ChoiceParamsExampleOverlayDescriptor : public DefaultEffectOverlayDescriptor {}; + +void ChoiceParamsExamplePluginFactory::describe(OFX::ImageEffectDescriptor& desc) +{ + // basic labels + desc.setLabels("Choice Params (Support)", "ChoiceParams(spt)", "Choice Params (Support)"); + desc.setPluginGrouping("OFX"); + + // add the supported contexts, only filter at the moment + desc.addSupportedContext(eContextFilter); + desc.addSupportedContext(eContextGeneral); + desc.addSupportedContext(eContextPaint); + + // add supported pixel depths + desc.addSupportedBitDepth(eBitDepthUByte); + desc.addSupportedBitDepth(eBitDepthUShort); + desc.addSupportedBitDepth(eBitDepthFloat); + + // set a few flags + desc.setSingleInstance(false); + desc.setHostFrameThreading(false); + desc.setSupportsMultiResolution(true); + desc.setSupportsTiles(true); + desc.setTemporalClipAccess(false); + desc.setRenderTwiceAlways(false); + desc.setSupportsMultipleClipPARs(false); + + desc.setOverlayInteractDescriptor( new ChoiceParamsExampleOverlayDescriptor); +} + +void ChoiceParamsExamplePluginFactory::describeInContext(OFX::ImageEffectDescriptor& desc, OFX::ContextEnum context) +{ + // Source clip only in the filter context + // create the mandated source clip + ClipDescriptor *srcClip = desc.defineClip(kOfxImageEffectSimpleSourceClipName); + srcClip->addSupportedComponent(ePixelComponentRGBA); + srcClip->addSupportedComponent(ePixelComponentAlpha); + srcClip->setTemporalClipAccess(false); + srcClip->setSupportsTiles(true); + srcClip->setIsMask(false); + + // if general or paint context, define the mask clip + if(context == eContextGeneral || context == eContextPaint) { + // if paint context, it is a mandated input called 'brush' + ClipDescriptor *maskClip = context == eContextGeneral ? desc.defineClip("Mask") : desc.defineClip("Brush"); + maskClip->addSupportedComponent(ePixelComponentAlpha); + maskClip->setTemporalClipAccess(false); + if(context == eContextGeneral) + maskClip->setOptional(true); + maskClip->setSupportsTiles(true); + maskClip->setIsMask(true); // we are a mask input + } + + // create the mandated output clip + ClipDescriptor *dstClip = desc.defineClip(kOfxImageEffectOutputClipName); + dstClip->addSupportedComponent(ePixelComponentRGBA); + dstClip->addSupportedComponent(ePixelComponentAlpha); + dstClip->setSupportsTiles(true); + + // make some pages and to things in + PageParamDescriptor *page = desc.definePageParam("Controls"); + + auto *choice1 = desc.defineChoiceParam("red_choice"); + choice1->appendOption("red: none"); + choice1->appendOption("red: some"); + choice1->appendOption("red: lots"); + page->addChild(*choice1); + + auto *choice2 = desc.defineChoiceParam("green_choice"); + choice2->appendOption("green: none", "", 0); + choice2->appendOption("green: lots", "", 2); + choice2->appendOption("green: some", "", 1); + page->addChild(*choice2); + + auto *choice3 = desc.defineStrChoiceParam("blue_choice"); + choice3->appendOption("blue_0.0", "blue: none"); + choice3->appendOption("blue_0.5", "blue: some"); + choice3->appendOption("blue_1.0", "blue: lots"); + page->addChild(*choice3); +} + +ImageEffect *ChoiceParamsExamplePluginFactory::createInstance(OfxImageEffectHandle handle, ContextEnum /*context*/) +{ + return new ChoiceParamsPlugin(handle); +} + +namespace OFX +{ + namespace Plugin + { + void getPluginIDs(OFX::PluginFactoryArray &ids) + { + static ChoiceParamsExamplePluginFactory p("org.openeffects.support.choiceParamsPlugin", 1, 0); + ids.push_back(&p); + } + } +} From 88b0da98f209f4bc9669a0af3f6384a8ff747bfd Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Sun, 3 Mar 2024 10:37:57 -0500 Subject: [PATCH 08/22] Add support for cmake install into alt dir, for CI - Add INSTALLDIR CMake cache var - Use bash getopts to parse args in `scripts/build-cmake.sh` Signed-off-by: Gary Oberbrunner --- cmake/OpenFX.cmake | 7 +++--- scripts/build-cmake.sh | 50 ++++++++++++++++++++++++++++++------------ 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/cmake/OpenFX.cmake b/cmake/OpenFX.cmake index a9931a18c..1c5a3cec8 100644 --- a/cmake/OpenFX.cmake +++ b/cmake/OpenFX.cmake @@ -12,6 +12,7 @@ else() set(ARCHDIR "unknown-arch") endif() +set(INSTALLDIR "${PLUGINDIR}" CACHE PATH "Path to install plugins") # Add a new OFX plugin target # Arguments: TARGET # Optional argument: DIR, defaults to same as TARGET (use when renaming TARGET) @@ -34,7 +35,7 @@ function(add_ofx_plugin TARGET) C_VISIBILITY_PRESET hidden CXX_VISIBILITY_PRESET hidden) - # To install plugins: cmake --install Build - install(TARGETS ${TARGET} DESTINATION "${PLUGINDIR}/${TARGET}.ofx.bundle/Contents/${ARCHDIR}") - install(FILES ${DIR}/Info.plist DESTINATION "${PLUGINDIR}/${TARGET}.ofx.bundle/Contents") + # To install plugins: cmake --install build/Release + install(TARGETS ${TARGET} DESTINATION "${INSTALLDIR}/${TARGET}.ofx.bundle/Contents/${ARCHDIR}") + install(FILES ${DIR}/Info.plist DESTINATION "${INSTALLDIR}/${TARGET}.ofx.bundle/Contents") endfunction() diff --git a/scripts/build-cmake.sh b/scripts/build-cmake.sh index f96c61222..e66d2c395 100755 --- a/scripts/build-cmake.sh +++ b/scripts/build-cmake.sh @@ -5,17 +5,38 @@ set -e # Build everything with Conan and CMake BUILDTYPE=Release # "Release" or "Debug" - -if [[ $1 = "-v" ]]; then - VERBOSE="--verbose"; shift -fi - -if [[ -n ${1:-} ]]; then - GENERATOR=$1 - GENERATOR_OPTION="-c tools.cmake.cmaketoolchain:generator=${GENERATOR}" -fi - -echo "Building OpenFX $BUILDTYPE in build/ with ${GENERATOR:-conan platform default generator}" +VERBOSE="" +GENERATOR="" + +# Parse options +while getopts ":vG:" opt; do + echo "Parsing ${opt}" + case ${opt} in + v ) + VERBOSE="--verbose" + ;; + G ) + GENERATOR=${OPTARG} + GENERATOR_OPTION="-c tools.cmake.cmaketoolchain:generator=${GENERATOR}" + ;; + \? ) + echo "Invalid option: $OPTARG" 1>&2 + exit 1 + ;; + : ) + echo "Invalid option: $OPTARG requires an argument" 1>&2 + exit 1 + ;; + esac +done +shift $((OPTIND -1)) + +# First positional argument as BUILDTYPE +BUILDTYPE=$1; shift + +ARGS="$@" + +echo "Building OpenFX $BUILDTYPE in build/ with ${GENERATOR:-conan platform default generator}, $ARGS" CONAN_VERSION=$(conan -v | sed -e 's/Conan version //g') @@ -50,7 +71,7 @@ conan install ${GENERATOR_OPTION} -s build_type=$BUILDTYPE -pr:b=default --build echo === Running cmake # Generate the build files -cmake --preset ${PRESET_NAME} -DBUILD_EXAMPLE_PLUGINS=TRUE +cmake --preset ${PRESET_NAME} -DBUILD_EXAMPLE_PLUGINS=TRUE $ARGS echo === Building plugins and support libs cmake --build ${CMAKE_BUILD_DIR} --config $BUILDTYPE $VERBOSE @@ -60,5 +81,6 @@ echo "=== Build complete." echo " Sample Plugins are in ${CMAKE_BUILD_DIR}/Examples/*/${BUILDTYPE}" echo " Plugin support lib and examples are in ${CMAKE_BUILD_DIR}/Support/{Library,Plugins}" echo " Host lib is in ${CMAKE_BUILD_DIR}/HostSupport/${BUILDTYPE}" -echo "=== To install the sample plugins to your OFX plugins folder, become root and then do:" -echo " cmake --install ${CMAKE_BUILD_DIR} --config $BUILDTYPE" +echo "=== To install the sample plugins to your OFX plugins folder, become root if necessary, and then do:" +echo " cmake --install ${CMAKE_BUILD_DIR}" +echo " (pass -DINSTALLDIR= to this script or cmake to install elsewhere than the standard OFX folder)" From c4e5c7191f18749c8cd4f684e89db31080da9b76 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Sun, 3 Mar 2024 10:42:05 -0500 Subject: [PATCH 09/22] Create separate CI artifact from built/installed plugins This way the plugin artifact can be unzipped directly into the OFX plugins folder, because the plugins have the proper bundle structure. I removed the "*.ofx" files from the include/lib artifact because they're not really useful as is, without the proper bundle structure. Signed-off-by: Gary Oberbrunner --- .github/workflows/build.yml | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ba74c154d..e7091afc6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -150,7 +150,7 @@ jobs: run: | if [[ ${{ matrix.has_cmake_presets }} = true ]]; then # Sets up to build in e.g. build/Release - cmake --preset $CONAN_PRESET -DBUILD_EXAMPLE_PLUGINS=TRUE . + cmake --preset $CONAN_PRESET -DBUILD_EXAMPLE_PLUGINS=TRUE -DINSTALLDIR=build/Install . else # VFX ref platforms 2022 & earlier have only cmake 3.19. # Older cmake (<3.23) does not support presets, so invoke with explicit args. @@ -158,7 +158,8 @@ jobs: -DCMAKE_TOOLCHAIN_FILE=$(pwd)/$BUILD_DIR/generators/conan_toolchain.cmake \ -DCMAKE_POLICY_DEFAULT_CMP0091=NEW \ -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_EXAMPLE_PLUGINS=TRUE + -DBUILD_EXAMPLE_PLUGINS=TRUE \ + -DINSTALLDIR=build/Install fi - name: Build with cmake @@ -169,6 +170,14 @@ jobs: cmake --build $BUILD_DIR --parallel fi + - name: Install with cmake + run: | + if [[ ${{ matrix.ostype }} = windows ]]; then + cmake --install $BUILD_DIR --config Release + else + cmake --install $BUILD_DIR + fi + - name: Build with make run: | if [[ ${{ matrix.ostype }} = windows ]]; then @@ -184,7 +193,7 @@ jobs: cp -R Support/include ${{ env.BUILD_DIR }}/Support/include cp -R HostSupport/include ${{ env.BUILD_DIR }}/HostSupport/include - - name: Archive libs, plugins, and header files to artifact + - name: Archive header files and libs to artifact uses: actions/upload-artifact@v3 with: name: openfx-${{ matrix.release_prefix }}-${{ env.BUILDTYPE_LC }}-${{ env.GIT_COMMIT_ID }} @@ -196,9 +205,15 @@ jobs: !${{ env.BUILD_DIR }}/include/*.dtd ${{ env.BUILD_DIR }}/Support/include ${{ env.BUILD_DIR }}/HostSupport/include - ${{ env.BUILD_DIR }}/**/*.ofx ${{ env.BUILD_DIR }}/**/lib* + - name: Archive built/installed plugins + uses: actions/upload-artifact@v3 + with: + name: openfx-plugins-${{ matrix.release_prefix }}-${{ env.BUILDTYPE_LC }}-${{ env.GIT_COMMIT_ID }} + path: | + ${{ env.BUILD_DIR }}/Install + # - name: Archive all build artifacts (for debugging CI) # uses: actions/upload-artifact@v3 # with: From c09d3912f027041d5a0dd52d7fbceb5dee52fc36 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Sun, 3 Mar 2024 10:47:31 -0500 Subject: [PATCH 10/22] Fix CI build Signed-off-by: Gary Oberbrunner --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e7091afc6..73e1e1b14 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -212,7 +212,7 @@ jobs: with: name: openfx-plugins-${{ matrix.release_prefix }}-${{ env.BUILDTYPE_LC }}-${{ env.GIT_COMMIT_ID }} path: | - ${{ env.BUILD_DIR }}/Install + build/Install # - name: Archive all build artifacts (for debugging CI) # uses: actions/upload-artifact@v3 From 8f73a61180731e83afdc23efe4c6db3b126fb070 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Sun, 3 Mar 2024 17:30:18 -0500 Subject: [PATCH 11/22] Fix CI build (?) and install plugins into subdir The CI build issue was an "unsafe" git dir. actions/checkout@v4 is supposed to fix that; see comments in https://github.com/actions/runner/issues/2033. Signed-off-by: Gary Oberbrunner --- .github/workflows/build.yml | 11 +++++++---- scripts/build-cmake.sh | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 73e1e1b14..a20f7179e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -99,7 +99,10 @@ jobs: shell: bash steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 + with: + clean: true + fetch-depth: 0 - name: setup env vars run: | @@ -150,7 +153,7 @@ jobs: run: | if [[ ${{ matrix.has_cmake_presets }} = true ]]; then # Sets up to build in e.g. build/Release - cmake --preset $CONAN_PRESET -DBUILD_EXAMPLE_PLUGINS=TRUE -DINSTALLDIR=build/Install . + cmake --preset $CONAN_PRESET -DBUILD_EXAMPLE_PLUGINS=TRUE -DINSTALLDIR=build/Install/OpenFX-Examples . else # VFX ref platforms 2022 & earlier have only cmake 3.19. # Older cmake (<3.23) does not support presets, so invoke with explicit args. @@ -159,7 +162,7 @@ jobs: -DCMAKE_POLICY_DEFAULT_CMP0091=NEW \ -DCMAKE_BUILD_TYPE=Release \ -DBUILD_EXAMPLE_PLUGINS=TRUE \ - -DINSTALLDIR=build/Install + -DINSTALLDIR=build/Install/OpenFX-Examples fi - name: Build with cmake @@ -210,7 +213,7 @@ jobs: - name: Archive built/installed plugins uses: actions/upload-artifact@v3 with: - name: openfx-plugins-${{ matrix.release_prefix }}-${{ env.BUILDTYPE_LC }}-${{ env.GIT_COMMIT_ID }} + name: openfx_plugins-${{ matrix.release_prefix }}-${{ env.BUILDTYPE_LC }}-${{ env.GIT_COMMIT_ID }} path: | build/Install diff --git a/scripts/build-cmake.sh b/scripts/build-cmake.sh index e66d2c395..3db2490a2 100755 --- a/scripts/build-cmake.sh +++ b/scripts/build-cmake.sh @@ -74,7 +74,7 @@ echo === Running cmake cmake --preset ${PRESET_NAME} -DBUILD_EXAMPLE_PLUGINS=TRUE $ARGS echo === Building plugins and support libs -cmake --build ${CMAKE_BUILD_DIR} --config $BUILDTYPE $VERBOSE +cmake --build ${CMAKE_BUILD_DIR} --parallel --config $BUILDTYPE $VERBOSE set +x echo "=== Build complete." From 6f552d2e8605dfee58f6754792ebc9170502832e Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Sun, 3 Mar 2024 17:41:18 -0500 Subject: [PATCH 12/22] More CI build fixes Signed-off-by: Gary Oberbrunner --- .github/workflows/build.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a20f7179e..57988903b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,6 +29,7 @@ jobs: cxx-compiler: clang++ cc-compiler: clang compiler-desc: Clang + checkout_version: 3 - name_prefix: Linux CentOS 7 VFX CY2022 release_prefix: linux-vfx2022 ostype: linux @@ -43,6 +44,7 @@ jobs: cxx-compiler: clang++ cc-compiler: clang compiler-desc: Clang + checkout_version: 3 - name_prefix: Linux Rocky 8 VFX CY2023 release_prefix: linux-vfx2023 ostype: linux @@ -57,6 +59,7 @@ jobs: cxx-compiler: clang++ cc-compiler: clang compiler-desc: Clang + checkout_version: 4 - name_prefix: Linux Ubuntu release_prefix: linux-ubuntu ostype: linux @@ -70,6 +73,7 @@ jobs: cxx-compiler: clang++ cc-compiler: clang compiler-desc: Clang + checkout_version: 4 - name_prefix: MacOS release_prefix: mac ostype: mac @@ -82,6 +86,7 @@ jobs: cxx-compiler: clang++ cc-compiler: clang compiler-desc: Clang + checkout_version: 4 - name_prefix: Windows release_prefix: windows ostype: windows @@ -94,18 +99,28 @@ jobs: cxx-compiler: clang++ cc-compiler: clang compiler-desc: Clang + checkout_version: 4 defaults: run: shell: bash steps: - - name: Checkout code + - name: Checkout code (v4) uses: actions/checkout@v4 + if: ${{matrix.checkout_version}} == 4 + with: + clean: true + fetch-depth: 0 + + - name: Checkout code (v3) + uses: actions/checkout@v4 + if: ${{matrix.checkout_version}} < 4 with: clean: true fetch-depth: 0 - name: setup env vars run: | + git config --global --add safe.directory $PWD # needed for checkout v3, doesn't hurt anyway BUILDTYPE_LC=$(echo '${{ matrix.buildtype }}'|tr [:upper:] [:lower:]) echo "BUILDTYPE_LC=$BUILDTYPE_LC" >> $GITHUB_ENV echo "OSNAME=$(echo '${{ matrix.os }}'|sed 's/-.*//')" >> $GITHUB_ENV From c0ec33f5d83a6b1e48a4a3b67c24f9ed3c63f3a2 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Sun, 3 Mar 2024 17:46:10 -0500 Subject: [PATCH 13/22] Further CI fixes ("if" syntax) Signed-off-by: Gary Oberbrunner --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 57988903b..bb1a40d34 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -106,14 +106,14 @@ jobs: steps: - name: Checkout code (v4) uses: actions/checkout@v4 - if: ${{matrix.checkout_version}} == 4 + if: matrix.checkout_version == 4 with: clean: true fetch-depth: 0 - name: Checkout code (v3) uses: actions/checkout@v4 - if: ${{matrix.checkout_version}} < 4 + if: matrix.checkout_version < 4 with: clean: true fetch-depth: 0 From 63a70f3070c7f2fad4d1de2791d55b41e230fea6 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Sun, 3 Mar 2024 17:48:43 -0500 Subject: [PATCH 14/22] More CI fixes. "OK Rocky, this time for sure!" Signed-off-by: Gary Oberbrunner --- .github/workflows/build.yml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bb1a40d34..318a853b7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -104,16 +104,8 @@ jobs: run: shell: bash steps: - - name: Checkout code (v4) - uses: actions/checkout@v4 - if: matrix.checkout_version == 4 - with: - clean: true - fetch-depth: 0 - - - name: Checkout code (v3) - uses: actions/checkout@v4 - if: matrix.checkout_version < 4 + - name: Checkout code + uses: actions/checkout@v${{ matrix.checkout_version }} with: clean: true fetch-depth: 0 From 5871167dee793fb98742369c239ac69cf053b8e5 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Sun, 3 Mar 2024 17:52:09 -0500 Subject: [PATCH 15/22] Even more CI Signed-off-by: Gary Oberbrunner --- .github/workflows/build.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 318a853b7..050b5f7c0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -104,8 +104,15 @@ jobs: run: shell: bash steps: - - name: Checkout code - uses: actions/checkout@v${{ matrix.checkout_version }} + - name: Checkout code (v4) + uses: actions/checkout@v4 + if: matrix.checkout_version == 4 + with: + clean: true + fetch-depth: 0 + - name: Checkout code (v3) + uses: actions/checkout@v3 + if: matrix.checkout_version == 3 with: clean: true fetch-depth: 0 From aeddb8caf0f009d88c72d21ac561be8c3e0f8fc4 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Tue, 5 Mar 2024 07:08:51 -0500 Subject: [PATCH 16/22] Fix param order for ChoiceParams examples, per notes from @Guido-assim Signed-off-by: Gary Oberbrunner --- Examples/ChoiceParams/choiceparams.cpp | 7 ++++--- Support/Plugins/ChoiceParams/choiceparams.cpp | 7 +++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Examples/ChoiceParams/choiceparams.cpp b/Examples/ChoiceParams/choiceparams.cpp index 0fa1fff9a..7a4ace01b 100644 --- a/Examples/ChoiceParams/choiceparams.cpp +++ b/Examples/ChoiceParams/choiceparams.cpp @@ -468,10 +468,10 @@ static OfxStatus render( OfxImageEffectHandle instance, if (gChoice == 0) gScale = 0; - if (gChoice == 1) - gScale = 0.5; - if (gChoice == 2) + if (gChoice == 1) // "lots" gScale = 1.0; + if (gChoice == 2) // "some" + gScale = 0.5; // string-valued param if (blueChoice == "blue_0.0") @@ -574,6 +574,7 @@ describeInContext( OfxImageEffectHandle effect, OfxPropertySetHandle inArgs) // Second choice param: using order. // This will produce incorrect results in hosts that don't support order. + // Note that index 1 here is "lots", index 2 is "some" gParamHost->paramDefine(paramSet, kOfxParamTypeChoice, "green_choice", &props); gPropHost->propSetInt(props, kOfxParamPropDefault, 0, 0); gPropHost->propSetString(props, kOfxParamPropScriptName, 0, "green_choice"); diff --git a/Support/Plugins/ChoiceParams/choiceparams.cpp b/Support/Plugins/ChoiceParams/choiceparams.cpp index c44d393bb..684724cfd 100644 --- a/Support/Plugins/ChoiceParams/choiceparams.cpp +++ b/Support/Plugins/ChoiceParams/choiceparams.cpp @@ -293,11 +293,12 @@ ChoiceParamsPlugin::setupAndProcess(ImageScalerBase &processor, const OFX::Rende if (ri == 2) r = 1.0; + // Note that green options are out of order if (gi == 0) g = 0; - if (gi == 1) - g = 0.5; if (gi == 2) + g = 0.5; + if (gi == 1) g = 1.0; if (bi == "blue_0.0") @@ -637,6 +638,8 @@ void ChoiceParamsExamplePluginFactory::describeInContext(OFX::ImageEffectDescrip choice1->appendOption("red: lots"); page->addChild(*choice1); + // Note: index 1 is "lots" (even though UI order is 2), index 2 is "some" + // because options are appended in order. auto *choice2 = desc.defineChoiceParam("green_choice"); choice2->appendOption("green: none", "", 0); choice2->appendOption("green: lots", "", 2); From 60b8b2f8ae98037ec89caed4f0a3cd44255943fb Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Tue, 5 Mar 2024 07:29:56 -0500 Subject: [PATCH 17/22] Clean up "building" doc in the Programming Guide. Signed-off-by: Gary Oberbrunner --- Documentation/sources/Guide/README.txt | 91 ++------------------------ 1 file changed, 4 insertions(+), 87 deletions(-) diff --git a/Documentation/sources/Guide/README.txt b/Documentation/sources/Guide/README.txt index e17579c13..5e44cc310 100644 --- a/Documentation/sources/Guide/README.txt +++ b/Documentation/sources/Guide/README.txt @@ -9,94 +9,11 @@ There are two sub-directories... ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BUILDING THE PLUGINS -For Windows instructions, see below. - - -To build the example plugins you will need, - - a C++ compiler - - gmake (or nmake on Windows) - - the ofx header files. - - -Within Code there is a subdirectory per plugin. - - -The assumption is that you have checked out all the OFX -source code in one lump and so the -OFX header files will be in a standard relative path to -the plugin sources. If this is not the case you will -need to modify the file... - - Code/MakefileCommon - -and change the line - - OFX_INC_DIR = -I../../../include - -to point to the directory where you have put the headers. - - -To build all the examples simply go... - - cd Code - make - -this will compile all the plugins and place them in -a directory called 'built_plugins'. - - -You can build individual plugins by changing into the -relevant subdirectory and simply issuing a 'make' command. - -++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -BUILDING ON WINDOWS - -NMakefiles are included for use with Windows' nmake utility. -This should build on any Visual Studio version (at least 2008 or newer). - -Open a Visual Studio command-line window of the appropriate bitness -that you want (32 for a 32-bit OFX host, 64 for a 64-bit host). From -the Start menu, go to Microsoft Visual Studio XXXX -> Visual Studio -Tools -> Visual Studio XXX Command Prompt (choose the appropriate -bitness here). - -In that window, cd to the openfx/Guide/Code dir, and type: - - nmake /F nmakefile install - -This will build and install the plugins into the standard OFX plugins -dir (c:\Program Files\Common Files\OFX\Plugins). - -To clean up: - - nmake /F nmakefile clean +The plugins and support libs are set up to use cmake to generate and run builds. +See the top-level README.md in the repo, and use `scripts/build-cmake.sh`. ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BUILDING THE DOCUMENTATION -To build the documentation you will need... - - gmake - - asciidoctor - - -The documentation is written in asciidoctor markdown, which can be used to -generate HTML, docbook XML and more. You will need to download and install -asciidoctor to build the doc. Visit... - - http://asciidoctor.org/ - -for installation instructions. - - -There is a gnu Makefile currently configured to generate html files. To build -the documentation simply go... - - cd Doc - make - -this will generate a subdirectory called 'html' which will contain the -guides in html format. - - - -Last Edit 11/11/14 +See the file Documentation/README, and use `Documentation/build.sh`. +The docs are also auto-generated on commit to the main branch on github. From 2a8cbf946bb7f0ff49544dbb4f27f7766010399f Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Tue, 5 Mar 2024 21:58:57 -0500 Subject: [PATCH 18/22] Doc & example: ParamChoice Order: hide negative order Also add Order support for StrChoice Still needs work: add to support lib Signed-off-by: Gary Oberbrunner --- Documentation/sources/Reference/ofxParameter.rst | 14 +++++++++++++- Examples/ChoiceParams/choiceparams.cpp | 12 ++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Documentation/sources/Reference/ofxParameter.rst b/Documentation/sources/Reference/ofxParameter.rst index f174f1018..87a4784d2 100644 --- a/Documentation/sources/Reference/ofxParameter.rst +++ b/Documentation/sources/Reference/ofxParameter.rst @@ -216,7 +216,7 @@ in a new version of the plugin. // add NewOpt at the end of the list, but specify order so it comes one before the end in the UI // Will display OptA / OptB / NewOpt / OptC Option = {"OptA", "OptB", "OptC", "NewOpt"} - Order = {0, 1, 3, 2} // or anything that sorts the same, e.g. {-100, 100, 300, 200} + Order = {0, 1, 3, 2} // or anything that sorts the same, e.g. {1, 100, 300, 200} In this case if the user had selected "OptC" in v1, and then loaded the project in v2, "OptC" will still be selected even though it is now the 4th @@ -230,6 +230,15 @@ Values may be arbitrary 32-bit integers. The same value must not occur more than once in the order list; behavior is undefined if the same value occurs twice in the list. +If an Order value is negative, the host should hide the +corresponding option in the UI. This can be useful for a plugin to +deprecate certain options. When a host loads a project which includes +a hidden option, the host should show that option in that effect +instance. If a host cannot dynamically hide or show options, it may +instead show hidden options (with negative Order values) as grayed out +or inactive. A plugin should not set the choice param's default value +to a hidden option. + Note that :c:macro:`kOfxParamPropChoiceOrder` does not affect project storage or operation; it is only used by the host UI. This way it is 100% backward compatible; even if the plugin sets it and the host doesn't support it, @@ -296,6 +305,9 @@ that the host use the default value in that case. To check for availability of this param type, a plugin may check the host property :c:macro:`kOfxParamHostPropSupportsStrChoice`. +StrChoice parameters may also be reordered and/or hidden using +:c:macro:`kOfxParamPropChoiceOrder`; see the previous section. + Available since 1.5. String Parameters diff --git a/Examples/ChoiceParams/choiceparams.cpp b/Examples/ChoiceParams/choiceparams.cpp index 7a4ace01b..38fb3725b 100644 --- a/Examples/ChoiceParams/choiceparams.cpp +++ b/Examples/ChoiceParams/choiceparams.cpp @@ -472,6 +472,9 @@ static OfxStatus render( OfxImageEffectHandle instance, gScale = 1.0; if (gChoice == 2) // "some" gScale = 0.5; + if (gChoice == 3) // should not happen + gScale = 10; + // string-valued param if (blueChoice == "blue_0.0") @@ -582,11 +585,13 @@ describeInContext( OfxImageEffectHandle effect, OfxPropertySetHandle inArgs) gPropHost->propSetString(props, kOfxParamPropChoiceOption, 0, "Green: none"); gPropHost->propSetString(props, kOfxParamPropChoiceOption, 1, "Green: lots"); gPropHost->propSetString(props, kOfxParamPropChoiceOption, 2, "Green: some"); + gPropHost->propSetString(props, kOfxParamPropChoiceOption, 3, "Green: TOO MUCH (hidden)"); // Order sets the display order: choices will be displayed in this order auto stat = gPropHost->propSetInt(props, kOfxParamPropChoiceOrder, 0, 0); // first if (stat == kOfxStatOK) { gPropHost->propSetInt(props, kOfxParamPropChoiceOrder, 1, 2); // last gPropHost->propSetInt(props, kOfxParamPropChoiceOrder, 2, 1); // middle + gPropHost->propSetInt(props, kOfxParamPropChoiceOrder, 3, -1); // negative order: should be hidden in UI } else { gHostSupportsChoiceOrder = false; std::cout << "Host does not support kOfxParamPropChoiceOrder: green results will look wrong\n"; @@ -601,10 +606,17 @@ describeInContext( OfxImageEffectHandle effect, OfxPropertySetHandle inArgs) gPropHost->propSetString(props, kOfxParamPropChoiceOption, 0, "Blue: none"); gPropHost->propSetString(props, kOfxParamPropChoiceOption, 1, "Blue: some"); gPropHost->propSetString(props, kOfxParamPropChoiceOption, 2, "Blue: lots"); + gPropHost->propSetString(props, kOfxParamPropChoiceOption, 3, "Blue: TOO MUCH"); // host will return and store these values gPropHost->propSetString(props, kOfxParamPropChoiceEnum, 0, "blue_0.0"); gPropHost->propSetString(props, kOfxParamPropChoiceEnum, 1, "blue_0.5"); gPropHost->propSetString(props, kOfxParamPropChoiceEnum, 2, "blue_1.0"); + gPropHost->propSetString(props, kOfxParamPropChoiceEnum, 3, "blue_HIDDEN"); + + gPropHost->propSetInt(props, kOfxParamPropChoiceOrder, 0, 0); + gPropHost->propSetInt(props, kOfxParamPropChoiceOrder, 1, 1); + gPropHost->propSetInt(props, kOfxParamPropChoiceOrder, 2, 2); + gPropHost->propSetInt(props, kOfxParamPropChoiceOrder, 3, -1); // hide this one } // make a page of controls and add my parameters to it From 5c0914f6ee196eae157f9b33e00d70b18c4280ee Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Wed, 6 Mar 2024 10:03:33 -0500 Subject: [PATCH 19/22] Add support for StrChoice param Orders (including negative to hide) Update StrChoice support example to use this, and also check for StrChoice support before creating/using the param. Signed-off-by: Gary Oberbrunner --- Support/Library/ofxsImageEffect.cpp | 5 ++- Support/Library/ofxsParams.cpp | 19 ++++++++--- Support/Plugins/ChoiceParams/choiceparams.cpp | 34 ++++++++++++------- Support/include/ofxsImageEffect.h | 1 + Support/include/ofxsParam.h | 7 ++-- 5 files changed, 46 insertions(+), 20 deletions(-) diff --git a/Support/Library/ofxsImageEffect.cpp b/Support/Library/ofxsImageEffect.cpp index 9e592d5d5..192bf524a 100644 --- a/Support/Library/ofxsImageEffect.cpp +++ b/Support/Library/ofxsImageEffect.cpp @@ -1809,7 +1809,10 @@ namespace OFX { gHostDescription.supportsStringAnimation = hostProps.propGetInt(kOfxParamHostPropSupportsStringAnimation) != 0; gHostDescription.supportsCustomInteract = hostProps.propGetInt(kOfxParamHostPropSupportsCustomInteract) != 0; gHostDescription.supportsChoiceAnimation = hostProps.propGetInt(kOfxParamHostPropSupportsChoiceAnimation) != 0; - gHostDescription.supportsStrChoiceAnimation = hostProps.propGetInt(kOfxParamHostPropSupportsStrChoiceAnimation) != 0; + // As of 1.5 + gHostDescription.supportsStrChoice = hostProps.propGetInt(kOfxParamHostPropSupportsStrChoice, false) != 0; + // As of 1.5 + gHostDescription.supportsStrChoiceAnimation = hostProps.propGetInt(kOfxParamHostPropSupportsStrChoiceAnimation, false) != 0; gHostDescription.supportsBooleanAnimation = hostProps.propGetInt(kOfxParamHostPropSupportsBooleanAnimation) != 0; gHostDescription.supportsCustomAnimation = hostProps.propGetInt(kOfxParamHostPropSupportsCustomAnimation) != 0; gHostDescription.osHandle = hostProps.propGetPointer(kOfxPropHostOSHandle, false); diff --git a/Support/Library/ofxsParams.cpp b/Support/Library/ofxsParams.cpp index b47b0d7d1..45fada146 100644 --- a/Support/Library/ofxsParams.cpp +++ b/Support/Library/ofxsParams.cpp @@ -3,6 +3,7 @@ /** @brief This file contains code that skins the ofx param suite */ +#include #include #include "ofxsSupportPrivate.h" #include "ofxParametricParam.h" @@ -770,9 +771,9 @@ namespace OFX { _paramProps.propSetString(kOfxParamPropHint, hint); } } - if (order >= 0) { + if (order != INT_MIN) { // Host may not support this prop (added in 1.5); continue without it. - _paramProps.propSetInt(kOfxParamPropChoiceOrder, order, false); + _paramProps.propSetInt(kOfxParamPropChoiceOrder, order, nCurrentValues, false); } } @@ -798,13 +799,18 @@ namespace OFX { } /** @brief append an option */ - void StrChoiceParamDescriptor::appendOption(const std::string& p_Enum, const std::string& p_Option) + void StrChoiceParamDescriptor::appendOption(const std::string& p_Enum, const std::string& p_Option, int order) { const int numOptions = _paramProps.propGetDimension(kOfxParamPropChoiceOption); assert(numOptions == _paramProps.propGetDimension(kOfxParamPropChoiceEnum)); _paramProps.propSetString(kOfxParamPropChoiceEnum, p_Enum, numOptions); _paramProps.propSetString(kOfxParamPropChoiceOption, p_Option, numOptions); + + if (order != INT_MIN) { + // Host may not support this prop (added in 1.5); continue without it. + _paramProps.propSetInt(kOfxParamPropChoiceOrder, order, numOptions, false); + } } /** @brief how many options do we have */ @@ -2528,13 +2534,18 @@ namespace OFX { } /** @brief add another option */ - void StrChoiceParam::appendOption(const std::string& p_Enum, const std::string& p_Option) + void StrChoiceParam::appendOption(const std::string& p_Enum, const std::string& p_Option, int order) { const int numOptions = _paramProps.propGetDimension(kOfxParamPropChoiceOption); assert(numOptions == _paramProps.propGetDimension(kOfxParamPropChoiceEnum)); _paramProps.propSetString(kOfxParamPropChoiceEnum, p_Enum, numOptions); _paramProps.propSetString(kOfxParamPropChoiceOption, p_Option, numOptions); + + if (order != INT_MIN) { + // Host may not support this prop (added in 1.5); continue without it. + _paramProps.propSetInt(kOfxParamPropChoiceOrder, order, numOptions, false); + } } /** @brief set the string of a specific option */ diff --git a/Support/Plugins/ChoiceParams/choiceparams.cpp b/Support/Plugins/ChoiceParams/choiceparams.cpp index 684724cfd..d066b7c04 100644 --- a/Support/Plugins/ChoiceParams/choiceparams.cpp +++ b/Support/Plugins/ChoiceParams/choiceparams.cpp @@ -207,7 +207,9 @@ public : maskClip_ = getContext() == OFX::eContextFilter ? NULL : fetchClip(getContext() == OFX::eContextPaint ? "Brush" : "Mask"); red_choice_ = fetchChoiceParam("red_choice"); green_choice_ = fetchChoiceParam("green_choice"); - blue_choice_ = fetchStrChoiceParam("blue_choice"); + if (OFX::getImageEffectHostDescription()->supportsStrChoice) { + blue_choice_ = fetchStrChoiceParam("blue_choice"); + } } /* Override the render */ @@ -284,7 +286,6 @@ ChoiceParamsPlugin::setupAndProcess(ImageScalerBase &processor, const OFX::Rende red_choice_->getValueAtTime(args.time, ri); green_choice_->getValueAtTime(args.time, gi); - blue_choice_->getValueAtTime(args.time, bi); if (ri == 0) r = 0; @@ -301,12 +302,17 @@ ChoiceParamsPlugin::setupAndProcess(ImageScalerBase &processor, const OFX::Rende if (gi == 1) g = 1.0; - if (bi == "blue_0.0") - b = 0; - if (bi == "blue_0.5") - b = 0.5; - if (bi == "blue_1.0") + if (OFX::getImageEffectHostDescription()->supportsStrChoice) { + blue_choice_->getValueAtTime(args.time, bi); + if (bi == "blue_0.0") + b = 0; + if (bi == "blue_0.5") + b = 0.5; + if (bi == "blue_1.0") + b = 1.0; + } else { b = 1.0; + } a = 1.0; // always @@ -644,13 +650,17 @@ void ChoiceParamsExamplePluginFactory::describeInContext(OFX::ImageEffectDescrip choice2->appendOption("green: none", "", 0); choice2->appendOption("green: lots", "", 2); choice2->appendOption("green: some", "", 1); + choice2->appendOption("green: TOO MUCH (hidden)", "", -1); page->addChild(*choice2); - auto *choice3 = desc.defineStrChoiceParam("blue_choice"); - choice3->appendOption("blue_0.0", "blue: none"); - choice3->appendOption("blue_0.5", "blue: some"); - choice3->appendOption("blue_1.0", "blue: lots"); - page->addChild(*choice3); + if (getImageEffectHostDescription()->supportsStrChoice) { + auto *choice3 = desc.defineStrChoiceParam("blue_choice"); + choice3->appendOption("blue_0.0", "blue: none", 0); + choice3->appendOption("blue_0.5", "blue: some", 1); + choice3->appendOption("blue_1.0", "blue: lots", 2); + choice3->appendOption("blue_HIDDEN", "blue: TOO MUCH", -1); // hide this one + page->addChild(*choice3); + } } ImageEffect *ChoiceParamsExamplePluginFactory::createInstance(OfxImageEffectHandle handle, ContextEnum /*context*/) diff --git a/Support/include/ofxsImageEffect.h b/Support/include/ofxsImageEffect.h index 4ea98e6ba..d0ddb6e25 100644 --- a/Support/include/ofxsImageEffect.h +++ b/Support/include/ofxsImageEffect.h @@ -244,6 +244,7 @@ namespace OFX { bool supportsStringAnimation; bool supportsCustomInteract; bool supportsChoiceAnimation; + bool supportsStrChoice; bool supportsStrChoiceAnimation; bool supportsBooleanAnimation; bool supportsCustomAnimation; diff --git a/Support/include/ofxsParam.h b/Support/include/ofxsParam.h index c50075ed0..0e0f80301 100644 --- a/Support/include/ofxsParam.h +++ b/Support/include/ofxsParam.h @@ -17,6 +17,7 @@ each represent the actions that can be carried out on those particular OFX objec */ #include +#include #include "ofxsCore.h" /** @brief Nasty macro used to define empty protected copy ctors and assign ops */ @@ -580,7 +581,7 @@ namespace OFX { void setDefault(int v); /** @brief append an option, default is to have not there */ - void appendOption(const std::string &v, const std::string& label = "", const int order = -1); + void appendOption(const std::string &v, const std::string& label = "", const int order = INT_MIN); /** @brief how many options do we have */ int getNOptions(void); @@ -609,7 +610,7 @@ namespace OFX { void setDefault(const std::string& p_DefaultValue); /** @brief append an option */ - void appendOption(const std::string& p_Enum, const std::string& p_Option); + void appendOption(const std::string& p_Enum, const std::string& p_Option, int order = INT_MIN); /** @brief how many options do we have */ int getNOptions(); @@ -1543,7 +1544,7 @@ namespace OFX { int getNOptions(); /** @brief append an option */ - void appendOption(const std::string& p_Enum, const std::string& p_Option); + void appendOption(const std::string& p_Enum, const std::string& p_Option, int order = INT_MIN); /** @brief set an option */ void setOption(const std::string& p_Index, const std::string& p_Option); From 881f3cae9562b64d66d0a54a1a43bf6af7ff42f6 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Sat, 9 Mar 2024 09:31:09 -0500 Subject: [PATCH 20/22] Remove Choice param option hiding based on negative Order values This turned out to be complicated for some hosts, so we will not mandate it in the spec. The spec now recommends plugins only use non-negative values for Order, thus preserving the option for hosts to hide negative values if they can. Signed-off-by: Gary Oberbrunner --- .../sources/Reference/ofxParameter.rst | 22 +++++++------------ Examples/ChoiceParams/choiceparams.cpp | 8 +------ Support/Plugins/ChoiceParams/choiceparams.cpp | 2 -- cmake/OpenFX.cmake | 6 ++--- scripts/build-cmake.sh | 4 +++- 5 files changed, 15 insertions(+), 27 deletions(-) diff --git a/Documentation/sources/Reference/ofxParameter.rst b/Documentation/sources/Reference/ofxParameter.rst index 87a4784d2..2836742ea 100644 --- a/Documentation/sources/Reference/ofxParameter.rst +++ b/Documentation/sources/Reference/ofxParameter.rst @@ -228,16 +228,9 @@ so the options are displayed in their natural order. Values may be arbitrary 32-bit integers. The same value must not occur more than once in the order list; behavior is undefined if the same -value occurs twice in the list. - -If an Order value is negative, the host should hide the -corresponding option in the UI. This can be useful for a plugin to -deprecate certain options. When a host loads a project which includes -a hidden option, the host should show that option in that effect -instance. If a host cannot dynamically hide or show options, it may -instead show hidden options (with negative Order values) as grayed out -or inactive. A plugin should not set the choice param's default value -to a hidden option. +value occurs twice in the list. Plugins should use non-negative +values; some hosts may choose to hide options with negative Order +values. Note that :c:macro:`kOfxParamPropChoiceOrder` does not affect project storage or operation; it is only used by the host UI. This way it is 100% @@ -251,9 +244,10 @@ property and check the return status. If the host does not support values into the middle of the options list, nor reorder the options, in a new version, otherwise old projects will not load properly. -Note: this property does not help if a plugin wants to *remove* an option. One way to -handle that case is to define a new choice param in v2 and hide the old v1 param, then use some -custom logic to populate the v2 param appropriately. +Note: this property does not help if a plugin wants to *remove* an +option. One way to handle that case is to define a new choice param in +v2 and hide the old v1 param, then use some custom logic to populate +the v2 param appropriately. Also in 1.5, see the new :c:macro:`kOfxParamTypeStrChoice` param type for another way to do this: the plugin specifies a set of string @@ -305,7 +299,7 @@ that the host use the default value in that case. To check for availability of this param type, a plugin may check the host property :c:macro:`kOfxParamHostPropSupportsStrChoice`. -StrChoice parameters may also be reordered and/or hidden using +StrChoice parameters may also be reordered using :c:macro:`kOfxParamPropChoiceOrder`; see the previous section. Available since 1.5. diff --git a/Examples/ChoiceParams/choiceparams.cpp b/Examples/ChoiceParams/choiceparams.cpp index 38fb3725b..3d5d9f06a 100644 --- a/Examples/ChoiceParams/choiceparams.cpp +++ b/Examples/ChoiceParams/choiceparams.cpp @@ -576,7 +576,7 @@ describeInContext( OfxImageEffectHandle effect, OfxPropertySetHandle inArgs) // Second choice param: using order. - // This will produce incorrect results in hosts that don't support order. + // This will produce incorrect ordering in hosts that don't support order. // Note that index 1 here is "lots", index 2 is "some" gParamHost->paramDefine(paramSet, kOfxParamTypeChoice, "green_choice", &props); gPropHost->propSetInt(props, kOfxParamPropDefault, 0, 0); @@ -585,16 +585,13 @@ describeInContext( OfxImageEffectHandle effect, OfxPropertySetHandle inArgs) gPropHost->propSetString(props, kOfxParamPropChoiceOption, 0, "Green: none"); gPropHost->propSetString(props, kOfxParamPropChoiceOption, 1, "Green: lots"); gPropHost->propSetString(props, kOfxParamPropChoiceOption, 2, "Green: some"); - gPropHost->propSetString(props, kOfxParamPropChoiceOption, 3, "Green: TOO MUCH (hidden)"); // Order sets the display order: choices will be displayed in this order auto stat = gPropHost->propSetInt(props, kOfxParamPropChoiceOrder, 0, 0); // first if (stat == kOfxStatOK) { gPropHost->propSetInt(props, kOfxParamPropChoiceOrder, 1, 2); // last gPropHost->propSetInt(props, kOfxParamPropChoiceOrder, 2, 1); // middle - gPropHost->propSetInt(props, kOfxParamPropChoiceOrder, 3, -1); // negative order: should be hidden in UI } else { gHostSupportsChoiceOrder = false; - std::cout << "Host does not support kOfxParamPropChoiceOrder: green results will look wrong\n"; } // Third choice param: using StrChoice, string-valued choice param @@ -606,17 +603,14 @@ describeInContext( OfxImageEffectHandle effect, OfxPropertySetHandle inArgs) gPropHost->propSetString(props, kOfxParamPropChoiceOption, 0, "Blue: none"); gPropHost->propSetString(props, kOfxParamPropChoiceOption, 1, "Blue: some"); gPropHost->propSetString(props, kOfxParamPropChoiceOption, 2, "Blue: lots"); - gPropHost->propSetString(props, kOfxParamPropChoiceOption, 3, "Blue: TOO MUCH"); // host will return and store these values gPropHost->propSetString(props, kOfxParamPropChoiceEnum, 0, "blue_0.0"); gPropHost->propSetString(props, kOfxParamPropChoiceEnum, 1, "blue_0.5"); gPropHost->propSetString(props, kOfxParamPropChoiceEnum, 2, "blue_1.0"); - gPropHost->propSetString(props, kOfxParamPropChoiceEnum, 3, "blue_HIDDEN"); gPropHost->propSetInt(props, kOfxParamPropChoiceOrder, 0, 0); gPropHost->propSetInt(props, kOfxParamPropChoiceOrder, 1, 1); gPropHost->propSetInt(props, kOfxParamPropChoiceOrder, 2, 2); - gPropHost->propSetInt(props, kOfxParamPropChoiceOrder, 3, -1); // hide this one } // make a page of controls and add my parameters to it diff --git a/Support/Plugins/ChoiceParams/choiceparams.cpp b/Support/Plugins/ChoiceParams/choiceparams.cpp index d066b7c04..c91258516 100644 --- a/Support/Plugins/ChoiceParams/choiceparams.cpp +++ b/Support/Plugins/ChoiceParams/choiceparams.cpp @@ -650,7 +650,6 @@ void ChoiceParamsExamplePluginFactory::describeInContext(OFX::ImageEffectDescrip choice2->appendOption("green: none", "", 0); choice2->appendOption("green: lots", "", 2); choice2->appendOption("green: some", "", 1); - choice2->appendOption("green: TOO MUCH (hidden)", "", -1); page->addChild(*choice2); if (getImageEffectHostDescription()->supportsStrChoice) { @@ -658,7 +657,6 @@ void ChoiceParamsExamplePluginFactory::describeInContext(OFX::ImageEffectDescrip choice3->appendOption("blue_0.0", "blue: none", 0); choice3->appendOption("blue_0.5", "blue: some", 1); choice3->appendOption("blue_1.0", "blue: lots", 2); - choice3->appendOption("blue_HIDDEN", "blue: TOO MUCH", -1); // hide this one page->addChild(*choice3); } } diff --git a/cmake/OpenFX.cmake b/cmake/OpenFX.cmake index 1c5a3cec8..0e4112dec 100644 --- a/cmake/OpenFX.cmake +++ b/cmake/OpenFX.cmake @@ -1,11 +1,11 @@ if(APPLE) - set(PLUGINDIR "/Library/OFX/Plugins") + set(PLUGINDIR "/Library/OFX/Plugins/OpenFX Examples") set(ARCHDIR "MacOS") elseif(WIN32) - set(PLUGINDIR "C:/Program Files (x86)/Common Files/OFX/Plugins") + set(PLUGINDIR "C:/Program Files (x86)/Common Files/OFX/Plugins/OpenFX Examples") set(ARCHDIR "Win64") elseif(UNIX) - set(PLUGINDIR "/usr/OFX/Plugins") + set(PLUGINDIR "/usr/OFX/Plugins/OpenFX Examples") set(ARCHDIR "Linux-x86-64") else() set(PLUGINDIR "/unknown-os") diff --git a/scripts/build-cmake.sh b/scripts/build-cmake.sh index 3db2490a2..76de41fe2 100755 --- a/scripts/build-cmake.sh +++ b/scripts/build-cmake.sh @@ -32,7 +32,9 @@ done shift $((OPTIND -1)) # First positional argument as BUILDTYPE -BUILDTYPE=$1; shift +if [[ $# -gt 0 ]]; then + BUILDTYPE=$1; shift +fi ARGS="$@" From 8b8be6ae82d1491c142212408ad5c77005e51ce6 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Sat, 9 Mar 2024 10:14:11 -0500 Subject: [PATCH 21/22] Remove dup option from build script (merge error) Signed-off-by: Gary Oberbrunner --- scripts/build-cmake.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build-cmake.sh b/scripts/build-cmake.sh index 2cfbb9066..9bb6d7788 100755 --- a/scripts/build-cmake.sh +++ b/scripts/build-cmake.sh @@ -75,7 +75,7 @@ echo === Running cmake cmake --preset ${PRESET_NAME} -DBUILD_EXAMPLE_PLUGINS=TRUE $ARGS echo === Building and installing plugins and support libs -cmake --build ${CMAKE_BUILD_DIR} --parallel --target install --config $BUILDTYPE --parallel $VERBOSE +cmake --build ${CMAKE_BUILD_DIR} --target install --config $BUILDTYPE --parallel $VERBOSE set +x echo "=== Build complete." From f0182760d61689e11476d534547d254f81b10d47 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Sat, 9 Mar 2024 10:21:05 -0500 Subject: [PATCH 22/22] Fix CI build of Windows without CUDA Signed-off-by: Gary Oberbrunner --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6d606105c..ff89ff6b1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -118,6 +118,7 @@ jobs: cxx-compiler: clang++ cc-compiler: clang compiler-desc: Clang + checkout_version: 4 cuda: false defaults: run: