diff --git a/CMakeLists.txt b/CMakeLists.txt index bc3ee5f..0a034ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,11 @@ if(APPLE) endif() set_property(GLOBAL PROPERTY USE_FOLDERS ON) -set(TOUCAN_PLUGINS toucan-draw toucan-filter toucan-transform) +set(TOUCAN_PLUGINS + toucan-draw + toucan-filter + toucan-generator + toucan-transform) enable_testing() diff --git a/bin/CMakeLists.txt b/bin/CMakeLists.txt index d6fffc7..3808155 100644 --- a/bin/CMakeLists.txt +++ b/bin/CMakeLists.txt @@ -7,7 +7,7 @@ install( TARGETS toucan-render RUNTIME DESTINATION bin) -foreach(OTIO CompositeTracks Filters Gap LinearTimeWarp Patterns Render Transition Transition2 Transforms) +foreach(OTIO CompositeTracks Draw Filter Gap LinearTimeWarp Pattern Transition Transition2 Transform) add_test( toucan-render-${OTIO} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/toucan-render${CMAKE_EXECUTABLE_SUFFIX} diff --git a/bin/main.cpp b/bin/main.cpp index d44fac7..39465da 100644 --- a/bin/main.cpp +++ b/bin/main.cpp @@ -62,7 +62,7 @@ int main(int argc, char** argv) // Create the timeline graph. const auto graph = std::make_shared(inputPath.parent_path(), timeline); - const IMATH_NAMESPACE::V2d& imageSize = graph->getImageSize(); + const IMATH_NAMESPACE::V2d imageSize = graph->getImageSize(); // Create the image effect host. auto host = std::make_shared(std::vector{ diff --git a/data/Render.otio b/data/Draw.otio similarity index 99% rename from data/Render.otio rename to data/Draw.otio index eb81ad1..63462de 100644 --- a/data/Render.otio +++ b/data/Draw.otio @@ -1,7 +1,7 @@ { "OTIO_SCHEMA": "Timeline.1", "metadata": {}, - "name": "Text", + "name": "Draw", "tracks": { "OTIO_SCHEMA": "Stack.1", "children": [ diff --git a/data/Filters.otio b/data/Filter.otio similarity index 99% rename from data/Filters.otio rename to data/Filter.otio index ae8a96a..3edf75b 100644 --- a/data/Filters.otio +++ b/data/Filter.otio @@ -1,7 +1,7 @@ { "OTIO_SCHEMA": "Timeline.1", "metadata": {}, - "name": "Filters", + "name": "Filter", "tracks": { "OTIO_SCHEMA": "Stack.1", "children": [ diff --git a/data/Patterns.otio b/data/Generator.otio similarity index 82% rename from data/Patterns.otio rename to data/Generator.otio index b879281..eeed30a 100644 --- a/data/Patterns.otio +++ b/data/Generator.otio @@ -1,7 +1,7 @@ { "OTIO_SCHEMA": "Timeline.1", "metadata": {}, - "name": "Patterns", + "name": "Generator", "tracks": { "OTIO_SCHEMA": "Stack.1", "children": [ @@ -11,7 +11,7 @@ { "OTIO_SCHEMA": "Clip.1", "media_reference": { - "OTIO_SCHEMA": "ExternalReference.1", + "OTIO_SCHEMA": "GeneratorReference.1", "available_range": { "OTIO_SCHEMA": "TimeRange.1", "duration": { @@ -25,16 +25,12 @@ "value": 0 } }, - "target_url": "Color_Black.png" - }, - "effects": [ - { - "OTIO_SCHEMA": "FillEffect.1", - "effect_name": "", + "generator_kind": "Fill", + "parameters": { "size": [ 1280, 720 ], "color": [ 1.0, 0.0, 0.0, 1.0 ] } - ], + }, "source_range": { "OTIO_SCHEMA": "TimeRange.1", "duration": { @@ -53,7 +49,7 @@ { "OTIO_SCHEMA": "Clip.1", "media_reference": { - "OTIO_SCHEMA": "ExternalReference.1", + "OTIO_SCHEMA": "GeneratorReference.1", "available_range": { "OTIO_SCHEMA": "TimeRange.1", "duration": { @@ -67,18 +63,14 @@ "value": 0 } }, - "target_url": "Color_Black.png" - }, - "effects": [ - { - "OTIO_SCHEMA": "CheckersEffect.1", - "effect_name": "", + "generator_kind": "Checkers", + "parameters": { "size": [ 1280, 720 ], "checkerSize": [ 100, 100 ], "color1": [ 1.0, 1.0, 1.0, 1.0 ], "color2": [ 0.0, 0.0, 0.0, 1.0 ] } - ], + }, "source_range": { "OTIO_SCHEMA": "TimeRange.1", "duration": { @@ -97,7 +89,7 @@ { "OTIO_SCHEMA": "Clip.1", "media_reference": { - "OTIO_SCHEMA": "ExternalReference.1", + "OTIO_SCHEMA": "GeneratorReference.1", "available_range": { "OTIO_SCHEMA": "TimeRange.1", "duration": { @@ -111,21 +103,16 @@ "value": 0 } }, - "target_url": "Color_Black.png" - }, - "effects": [ - { - "OTIO_SCHEMA": "NoiseEffect.1", - "effect_name": "", - "width": 1280, - "height": 720, + "generator_kind": "Noise", + "parameters": { + "size": [ 1280, 720 ], "type": "gaussian", "a": 0.0, "b": 1.0, "mono": false, "seed": 0 } - ], + }, "source_range": { "OTIO_SCHEMA": "TimeRange.1", "duration": { diff --git a/data/Transforms.otio b/data/Transform.otio similarity index 99% rename from data/Transforms.otio rename to data/Transform.otio index d1b3097..fe6028a 100644 --- a/data/Transforms.otio +++ b/data/Transform.otio @@ -1,7 +1,7 @@ { "OTIO_SCHEMA": "Timeline.1", "metadata": {}, - "name": "Transforms", + "name": "Transform", "tracks": { "OTIO_SCHEMA": "Stack.1", "children": [ diff --git a/lib/toucan/CMakeLists.txt b/lib/toucan/CMakeLists.txt index d126d34..8a39b38 100644 --- a/lib/toucan/CMakeLists.txt +++ b/lib/toucan/CMakeLists.txt @@ -2,17 +2,17 @@ set(HEADERS Comp.h Draw.h Filter.h - Generator.h - TimeWarp.h - Transform.h - Transition.h ImageNode.h ImageEffectHost.h Init.h + Generator.h Plugin.h PropertySet.h Read.h + TimeWarp.h TimelineGraph.h + Transform.h + Transition.h Transition.h Util.h) set(HEADERS_PRIVATE) @@ -20,17 +20,17 @@ set(SOURCE Comp.cpp Draw.cpp Filter.cpp - Generator.cpp - TimeWarp.cpp - Transform.cpp - Transition.cpp ImageNode.cpp ImageEffectHost.cpp Init.cpp + Generator.cpp Plugin.cpp PropertySet.cpp Read.cpp + TimeWarp.cpp TimelineGraph.cpp + Transform.cpp + Transition.cpp Transition.cpp Util.cpp) if(WIN32) diff --git a/lib/toucan/Generator.cpp b/lib/toucan/Generator.cpp index 18d59a2..e71566b 100644 --- a/lib/toucan/Generator.cpp +++ b/lib/toucan/Generator.cpp @@ -34,87 +34,29 @@ namespace toucan const OTIO_NS::RationalTime& time, const std::shared_ptr& host) { - OIIO::ImageBuf buf; - if (!_inputs.empty() && _inputs[0]) + OIIO::ImageBuf buf(OIIO::ImageSpec(_data.size.x, _data.size.y, 4)); + PropertySet propSet; + propSet.setIntN("checkerSize", 2, &_data.checkerSize.x); + const double color1[] = { - OTIO_NS::RationalTime offsetTime = time; - if (!_timeOffset.is_invalid_time()) - { - offsetTime -= _timeOffset; - } - buf = _inputs[0]->exec(offsetTime, host); - OIIO::ImageBufAlgo::checker( - buf, - _data.checkerSize.x, - _data.checkerSize.y, - 1, - { _data.color1.x, _data.color1.y, _data.color1.z, _data.color1.w }, - { _data.color2.x, _data.color2.y, _data.color2.z, _data.color2.w }); - } - else + _data.color1.x, + _data.color1.y, + _data.color1.z, + _data.color1.w + }; + propSet.setDoubleN("color1", 4, color1); + const double color2[] = { - buf = OIIO::ImageBufAlgo::checker( - _data.checkerSize.x, - _data.checkerSize.y, - 1, - { _data.color1.x, _data.color1.y, _data.color1.z, _data.color1.w }, - { _data.color2.x, _data.color2.y, _data.color2.z, _data.color2.w }, - 0, - 0, - 0, - OIIO::ROI(0, _data.size.x, 0, _data.size.y, 0, 1, 0, 4)); - } + _data.color2.x, + _data.color2.y, + _data.color2.z, + _data.color2.w + }; + propSet.setDoubleN("color2", 4, color2); + host->generator("Toucan:Checkers", buf, propSet); return buf; } - CheckersEffect::CheckersEffect( - std::string const& name, - std::string const& effect_name, - OTIO_NS::AnyDictionary const& metadata) : - IEffect(name, effect_name, metadata) - {} - - CheckersEffect::~CheckersEffect() - {} - - std::shared_ptr CheckersEffect::createNode( - const std::vector >& inputs) - { - return std::make_shared(_data, inputs); - } - - bool CheckersEffect::read_from(Reader& reader) - { - //! \todo What is a better way to serialize non-POD types? - OTIO_NS::AnyVector size; - OTIO_NS::AnyVector checkerSize; - OTIO_NS::AnyVector color1; - OTIO_NS::AnyVector color2; - bool out = - reader.read("size", &size) && - reader.read("checkerSize", &checkerSize) && - reader.read("color1", &color1) && - reader.read("color2", &color2) && - IEffect::read_from(reader); - if (out) - { - anyToVec(size, _data.size); - anyToVec(checkerSize, _data.checkerSize); - anyToVec(color1, _data.color1); - anyToVec(color2, _data.color2); - } - return out; - } - - void CheckersEffect::write_to(Writer& writer) const - { - IEffect::write_to(writer); - writer.write("size", vecToAny(_data.size)); - writer.write("checkerSize", vecToAny(_data.checkerSize)); - writer.write("color1", vecToAny(_data.color1)); - writer.write("color2", vecToAny(_data.color2)); - } - FillNode::FillNode( const FillData& data, const std::vector >& inputs) : @@ -139,67 +81,20 @@ namespace toucan const OTIO_NS::RationalTime& time, const std::shared_ptr& host) { - OIIO::ImageBuf buf; - if (!_inputs.empty() && _inputs[0]) - { - OTIO_NS::RationalTime offsetTime = time; - if (!_timeOffset.is_invalid_time()) - { - offsetTime -= _timeOffset; - } - buf = _inputs[0]->exec(offsetTime, host); - OIIO::ImageBufAlgo::fill( - buf, - { _data.color.x, _data.color.y, _data.color.z, _data.color.w }); - } - else + OIIO::ImageBuf buf(OIIO::ImageSpec(_data.size.x, _data.size.y, 4)); + PropertySet propSet; + const double color[] = { - buf = OIIO::ImageBufAlgo::fill( - { _data.color.x, _data.color.y, _data.color.z, _data.color.w }, - OIIO::ROI(0, _data.size.x, 0, _data.size.y, 0, 1, 0, 4)); - } + _data.color.x, + _data.color.y, + _data.color.z, + _data.color.w + }; + propSet.setDoubleN("color", 4, color); + host->generator("Toucan:Fill", buf, propSet); return buf; } - FillEffect::FillEffect( - std::string const& name, - std::string const& effect_name, - OTIO_NS::AnyDictionary const& metadata) : - IEffect(name, effect_name, metadata) - {} - - FillEffect::~FillEffect() - {} - - std::shared_ptr FillEffect::createNode( - const std::vector >& inputs) - { - return std::make_shared(_data, inputs); - } - - bool FillEffect::read_from(Reader& reader) - { - OTIO_NS::AnyVector size; - OTIO_NS::AnyVector color; - bool out = - reader.read("size", &size) && - reader.read("color", &color) && - IEffect::read_from(reader); - if (out) - { - anyToVec(size, _data.size); - anyToVec(color, _data.color); - } - return out; - } - - void FillEffect::write_to(Writer& writer) const - { - IEffect::write_to(writer); - writer.write("size", vecToAny(_data.size)); - writer.write("color", vecToAny(_data.color)); - } - NoiseNode::NoiseNode( const NoiseData& data, const std::vector >& inputs) : @@ -224,91 +119,14 @@ namespace toucan const OTIO_NS::RationalTime& time, const std::shared_ptr& host) { - OIIO::ImageBuf buf; - if (!_inputs.empty() && _inputs[0]) - { - OTIO_NS::RationalTime offsetTime = time; - if (!_timeOffset.is_invalid_time()) - { - offsetTime -= _timeOffset; - } - buf = _inputs[0]->exec(offsetTime, host); - OIIO::ImageBufAlgo::noise( - buf, - _data.type, - _data.a, - _data.b, - _data.mono, - _data.seed); - } - else - { - buf = OIIO::ImageBufAlgo::noise( - _data.type, - _data.a, - _data.b, - _data.mono, - _data.seed, - OIIO::ROI(0, _data.size.x, 0, _data.size.y, 0, 1, 0, 4)); - } + OIIO::ImageBuf buf(OIIO::ImageSpec(_data.size.x, _data.size.y, 4)); + PropertySet propSet; + propSet.setString("type", 0, _data.type.c_str()); + propSet.setDouble("a", 0, _data.a); + propSet.setDouble("b", 0, _data.b); + propSet.setInt("mono", 0, _data.mono); + propSet.setInt("seed", 0, _data.seed); + host->generator("Toucan:Noise", buf, propSet); return buf; } - - NoiseEffect::NoiseEffect( - std::string const& name, - std::string const& effect_name, - OTIO_NS::AnyDictionary const& metadata) : - IEffect(name, effect_name, metadata) - {} - - NoiseEffect::~NoiseEffect() - {} - - std::shared_ptr NoiseEffect::createNode( - const std::vector >& inputs) - { - return std::make_shared(_data, inputs); - } - - bool NoiseEffect::read_from(Reader& reader) - { - int64_t width = 0; - int64_t height = 0; - double a = 0.0; - double b = 0.0; - bool mono = false; - int64_t seed = 0; - IMATH_NAMESPACE::V4d color; - bool out = - reader.read("width", &width) && - reader.read("height", &height) && - reader.read("type", &_data.type) && - reader.read("a", &a) && - reader.read("b", &b) && - reader.read("mono", &mono) && - reader.read("seed", &seed) && - IEffect::read_from(reader); - if (out) - { - _data.size.x = width; - _data.size.y = height; - _data.a = a; - _data.b = b; - _data.mono = mono; - _data.seed = seed; - } - return out; - } - - void NoiseEffect::write_to(Writer& writer) const - { - IEffect::write_to(writer); - writer.write("width", static_cast(_data.size.x)); - writer.write("height", static_cast(_data.size.y)); - writer.write("type", _data.type); - writer.write("a", static_cast(_data.a)); - writer.write("b", static_cast(_data.b)); - writer.write("mono", _data.mono); - writer.write("seed", static_cast(_data.seed)); - } } diff --git a/lib/toucan/Generator.h b/lib/toucan/Generator.h index 05bb521..2d5b23b 100644 --- a/lib/toucan/Generator.h +++ b/lib/toucan/Generator.h @@ -38,34 +38,6 @@ namespace toucan CheckersData _data; }; - //! Checkers OTIO effect. - class CheckersEffect : public IEffect - { - public: - struct Schema - { - static auto constexpr name = "CheckersEffect"; - static int constexpr version = 1; - }; - - CheckersEffect( - std::string const& name = std::string(), - std::string const& effect_name = std::string(), - OTIO_NS::AnyDictionary const& metadata = OTIO_NS::AnyDictionary()); - - std::shared_ptr createNode( - const std::vector >& inputs) override; - - protected: - virtual ~CheckersEffect(); - - bool read_from(Reader&) override; - void write_to(Writer&) const override; - - private: - CheckersData _data; - }; - //! Fill data. struct FillData { @@ -94,34 +66,6 @@ namespace toucan FillData _data; }; - //! Fill OTIO effect. - class FillEffect : public IEffect - { - public: - struct Schema - { - static auto constexpr name = "FillEffect"; - static int constexpr version = 1; - }; - - FillEffect( - std::string const& name = std::string(), - std::string const& effect_name = std::string(), - OTIO_NS::AnyDictionary const& metadata = OTIO_NS::AnyDictionary()); - - std::shared_ptr createNode( - const std::vector >& inputs) override; - - protected: - virtual ~FillEffect(); - - bool read_from(Reader&) override; - void write_to(Writer&) const override; - - private: - FillData _data; - }; - //! Noise data. struct NoiseData { @@ -153,32 +97,4 @@ namespace toucan private: NoiseData _data; }; - - //! Noise OTIO effect. - class NoiseEffect : public IEffect - { - public: - struct Schema - { - static auto constexpr name = "NoiseEffect"; - static int constexpr version = 1; - }; - - NoiseEffect( - std::string const& name = std::string(), - std::string const& effect_name = std::string(), - OTIO_NS::AnyDictionary const& metadata = OTIO_NS::AnyDictionary()); - - std::shared_ptr createNode( - const std::vector >& inputs) override; - - protected: - virtual ~NoiseEffect(); - - bool read_from(Reader&) override; - void write_to(Writer&) const override; - - private: - NoiseData _data; - }; } diff --git a/lib/toucan/ImageEffectHost.cpp b/lib/toucan/ImageEffectHost.cpp index 16e09cd..4cce6e6 100644 --- a/lib/toucan/ImageEffectHost.cpp +++ b/lib/toucan/ImageEffectHost.cpp @@ -34,6 +34,47 @@ namespace toucan } } + void ImageEffectHost::generator( + const std::string& name, + OIIO::ImageBuf& output, + const PropertySet& propSet) + { + for (auto& data : _pluginData) + { + if (name == data.ofxPlugin->pluginIdentifier) + { + OfxStatus ofxStatus = data.ofxPlugin->mainEntry( + kOfxActionCreateInstance, + &data, + nullptr, + nullptr); + + data.images["Output"] = bufToPropSet(output); + PropertySet args = propSet; + args.setDouble(kOfxPropTime, 0, 0.0); + const auto& spec = output.spec(); + OfxRectI bounds; + bounds.x1 = 0; + bounds.x2 = spec.width; + bounds.y1 = 0; + bounds.y2 = spec.height; + args.setIntN(kOfxImageEffectPropRenderWindow, 4, &bounds.x1); + ofxStatus = data.ofxPlugin->mainEntry( + kOfxImageEffectActionRender, + &data, + (OfxPropertySetHandle)&args, + nullptr); + + ofxStatus = data.ofxPlugin->mainEntry( + kOfxActionDestroyInstance, + &data, + nullptr, + nullptr); + break; + } + } + } + void ImageEffectHost::filter( const std::string& name, const OIIO::ImageBuf& source, diff --git a/lib/toucan/ImageEffectHost.h b/lib/toucan/ImageEffectHost.h index 984f290..50676bc 100644 --- a/lib/toucan/ImageEffectHost.h +++ b/lib/toucan/ImageEffectHost.h @@ -24,12 +24,16 @@ namespace toucan ~ImageEffectHost(); - //! Apply a filter effect. + void generator( + const std::string& name, + OIIO::ImageBuf&, + const PropertySet & = PropertySet()); + void filter( const std::string& name, const OIIO::ImageBuf&, OIIO::ImageBuf&, - const PropertySet& = PropertySet()); + const PropertySet & = PropertySet()); private: void _suiteInit(); diff --git a/lib/toucan/ImageNode.h b/lib/toucan/ImageNode.h index a012d76..480d3a5 100644 --- a/lib/toucan/ImageNode.h +++ b/lib/toucan/ImageNode.h @@ -7,6 +7,7 @@ #include #include +#include #include diff --git a/lib/toucan/Init.cpp b/lib/toucan/Init.cpp index 451f50f..1338dd3 100644 --- a/lib/toucan/Init.cpp +++ b/lib/toucan/Init.cpp @@ -7,7 +7,6 @@ #include "Comp.h" #include "Draw.h" #include "Filter.h" -#include "Generator.h" #include "Read.h" #include "TimeWarp.h" #include "Transform.h" @@ -20,14 +19,11 @@ namespace toucan void init() { OTIO_NS::TypeRegistry::instance().register_type(); - OTIO_NS::TypeRegistry::instance().register_type(); OTIO_NS::TypeRegistry::instance().register_type(); - OTIO_NS::TypeRegistry::instance().register_type(); OTIO_NS::TypeRegistry::instance().register_type(); OTIO_NS::TypeRegistry::instance().register_type(); OTIO_NS::TypeRegistry::instance().register_type(); OTIO_NS::TypeRegistry::instance().register_type(); - OTIO_NS::TypeRegistry::instance().register_type(); OTIO_NS::TypeRegistry::instance().register_type(); OTIO_NS::TypeRegistry::instance().register_type(); OTIO_NS::TypeRegistry::instance().register_type(); diff --git a/lib/toucan/TimelineGraph.cpp b/lib/toucan/TimelineGraph.cpp index 749e055..fab33e9 100644 --- a/lib/toucan/TimelineGraph.cpp +++ b/lib/toucan/TimelineGraph.cpp @@ -9,9 +9,11 @@ #include "Read.h" #include "TimeWarp.h" #include "Transition.h" +#include "Util.h" #include #include +#include #include #include @@ -23,7 +25,7 @@ namespace toucan _path(path), _timeline(timeline) { - // Get the image size from the first clip. + // Get the image size from the first item. for (auto clip : _timeline->find_clips()) { if (auto externalRef = dynamic_cast(clip->media_reference())) @@ -57,6 +59,15 @@ namespace toucan break; } } + else if (auto generatorRef = dynamic_cast(clip->media_reference())) + { + auto parameters = generatorRef->parameters(); + auto i = parameters.find("size"); + if (i != parameters.end() && i->second.has_value()) + { + anyToVec(std::any_cast(i->second), _imageSize); + } + } } } @@ -214,6 +225,90 @@ namespace toucan sequenceRef->frame_zero_padding()); out = read; } + else if (auto generatorRef = dynamic_cast(clip->media_reference())) + { + if ("Checkers" == generatorRef->generator_kind()) + { + CheckersData data; + auto parameters = generatorRef->parameters(); + auto i = parameters.find("size"); + if (i != parameters.end() && i->second.has_value()) + { + anyToVec(std::any_cast(i->second), data.size); + } + i = parameters.find("checkerSize"); + if (i != parameters.end() && i->second.has_value()) + { + anyToVec(std::any_cast(i->second), data.checkerSize); + } + i = parameters.find("color1"); + if (i != parameters.end() && i->second.has_value()) + { + anyToVec(std::any_cast(i->second), data.color1); + } + i = parameters.find("color2"); + if (i != parameters.end() && i->second.has_value()) + { + anyToVec(std::any_cast(i->second), data.color2); + } + auto generator = std::make_shared(data); + out = generator; + } + else if ("Fill" == generatorRef->generator_kind()) + { + FillData data; + auto parameters = generatorRef->parameters(); + auto i = parameters.find("size"); + if (i != parameters.end() && i->second.has_value()) + { + anyToVec(std::any_cast(i->second), data.size); + } + i = parameters.find("color"); + if (i != parameters.end() && i->second.has_value()) + { + anyToVec(std::any_cast(i->second), data.color); + } + auto generator = std::make_shared(data); + out = generator; + } + else if ("Noise" == generatorRef->generator_kind()) + { + NoiseData data; + auto parameters = generatorRef->parameters(); + auto i = parameters.find("size"); + if (i != parameters.end() && i->second.has_value()) + { + anyToVec(std::any_cast(i->second), data.size); + } + i = parameters.find("type"); + if (i != parameters.end() && i->second.has_value()) + { + data.type = std::any_cast(i->second); + } + i = parameters.find("a"); + if (i != parameters.end() && i->second.has_value()) + { + data.a = std::any_cast(i->second); + } + i = parameters.find("b"); + if (i != parameters.end() && i->second.has_value()) + { + data.b = std::any_cast(i->second); + } + i = parameters.find("mono"); + if (i != parameters.end() && i->second.has_value()) + { + data.mono = std::any_cast(i->second); + } + i = parameters.find("seed"); + if (i != parameters.end() && i->second.has_value()) + { + data.seed = std::any_cast(i->second); + } + auto generator = std::make_shared(data); + out = generator; + } + } } else if (auto gap = OTIO_NS::dynamic_retainer_cast(item)) { diff --git a/lib/toucan/TimelineGraph.h b/lib/toucan/TimelineGraph.h index cd8e93c..efdee08 100644 --- a/lib/toucan/TimelineGraph.h +++ b/lib/toucan/TimelineGraph.h @@ -43,6 +43,6 @@ namespace toucan std::filesystem::path _path; OTIO_NS::SerializableObject::Retainer _timeline; - IMATH_NAMESPACE::V2d _imageSize = IMATH_NAMESPACE::V2d(0, 0); + IMATH_NAMESPACE::V2i _imageSize = IMATH_NAMESPACE::V2i(0, 0); }; } diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 7d04862..aa7e9d8 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -1,4 +1,4 @@ -foreach(PLUGIN Draw Filter Transform) +foreach(PLUGIN Draw Filter Generator Transform) string(TOLOWER ${PLUGIN} PLUGIN_LOWER) add_library( toucan-${PLUGIN_LOWER} MODULE diff --git a/plugins/DrawPlugin.cpp b/plugins/DrawPlugin.cpp index 1867285..d1cf923 100644 --- a/plugins/DrawPlugin.cpp +++ b/plugins/DrawPlugin.cpp @@ -17,6 +17,19 @@ DrawPlugin::DrawPlugin(const std::string& group, const std::string& name) : DrawPlugin::~DrawPlugin() {} +OfxStatus DrawPlugin::_describeAction(OfxImageEffectHandle descriptor) +{ + Plugin::_describeAction(descriptor); + OfxPropertySetHandle effectProps; + _imageEffectSuite->getPropertySet(descriptor, &effectProps); + _propertySuite->propSetString( + effectProps, + kOfxImageEffectPropSupportedContexts, + 0, + kOfxImageEffectContextFilter); + return kOfxStatOK; +} + OfxStatus DrawPlugin::_describeInContextAction(OfxImageEffectHandle descriptor, OfxPropertySetHandle inArgs) { OfxPropertySetHandle sourceProps; diff --git a/plugins/DrawPlugin.h b/plugins/DrawPlugin.h index 9d16d00..bee751d 100644 --- a/plugins/DrawPlugin.h +++ b/plugins/DrawPlugin.h @@ -22,6 +22,7 @@ class DrawPlugin : public Plugin const OfxRectI& renderWindow, OfxPropertySetHandle inArgs) = 0; + OfxStatus _describeAction(OfxImageEffectHandle) override; OfxStatus _describeInContextAction( OfxImageEffectHandle, OfxPropertySetHandle) override; diff --git a/plugins/FilterPlugin.cpp b/plugins/FilterPlugin.cpp index 39b745d..372fcc8 100644 --- a/plugins/FilterPlugin.cpp +++ b/plugins/FilterPlugin.cpp @@ -15,6 +15,19 @@ FilterPlugin::FilterPlugin(const std::string& group, const std::string& name) : FilterPlugin::~FilterPlugin() {} +OfxStatus FilterPlugin::_describeAction(OfxImageEffectHandle descriptor) +{ + Plugin::_describeAction(descriptor); + OfxPropertySetHandle effectProps; + _imageEffectSuite->getPropertySet(descriptor, &effectProps); + _propertySuite->propSetString( + effectProps, + kOfxImageEffectPropSupportedContexts, + 0, + kOfxImageEffectContextFilter); + return kOfxStatOK; +} + OfxStatus FilterPlugin::_describeInContextAction(OfxImageEffectHandle descriptor, OfxPropertySetHandle inArgs) { OfxPropertySetHandle sourceProps; diff --git a/plugins/FilterPlugin.h b/plugins/FilterPlugin.h index d685c0a..52f914c 100644 --- a/plugins/FilterPlugin.h +++ b/plugins/FilterPlugin.h @@ -22,6 +22,7 @@ class FilterPlugin : public Plugin const OfxRectI& renderWindow, OfxPropertySetHandle inArgs) = 0; + OfxStatus _describeAction(OfxImageEffectHandle) override; OfxStatus _describeInContextAction( OfxImageEffectHandle, OfxPropertySetHandle) override; diff --git a/plugins/GeneratorPlugin.cpp b/plugins/GeneratorPlugin.cpp new file mode 100644 index 0000000..df0e77e --- /dev/null +++ b/plugins/GeneratorPlugin.cpp @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2024 Darby Johnston +// All rights reserved. + +#include "GeneratorPlugin.h" + +#include "Util.h" + +#include + +#include + +GeneratorPlugin::GeneratorPlugin(const std::string& group, const std::string& name) : + Plugin(group, name) +{} + +GeneratorPlugin::~GeneratorPlugin() +{} + +OfxStatus GeneratorPlugin::_describeAction(OfxImageEffectHandle descriptor) +{ + Plugin::_describeAction(descriptor); + OfxPropertySetHandle effectProps; + _imageEffectSuite->getPropertySet(descriptor, &effectProps); + _propertySuite->propSetString( + effectProps, + kOfxImageEffectPropSupportedContexts, + 0, + kOfxImageEffectContextGenerator); + return kOfxStatOK; +} + +OfxStatus GeneratorPlugin::_describeInContextAction(OfxImageEffectHandle descriptor, OfxPropertySetHandle inArgs) +{ + OfxPropertySetHandle outputProps; + _imageEffectSuite->clipDefine(descriptor, "Output", &outputProps); + const std::vector components = + { + kOfxImageComponentAlpha, + kOfxImageComponentRGB, + kOfxImageComponentRGBA + }; + const std::vector pixelDepths = + { + kOfxBitDepthByte, + kOfxBitDepthShort, + kOfxBitDepthFloat + }; + for (int i = 0; i < components.size(); ++i) + { + _propertySuite->propSetString( + outputProps, + kOfxImageEffectPropSupportedComponents, + i, + components[i].c_str()); + } + for (int i = 0; i < pixelDepths.size(); ++i) + { + _propertySuite->propSetString( + outputProps, + kOfxImageEffectPropSupportedPixelDepths, + i, + pixelDepths[i].c_str()); + } + return kOfxStatOK; +} + +OfxStatus GeneratorPlugin::_renderAction( + OfxImageEffectHandle instance, + OfxPropertySetHandle inArgs, + OfxPropertySetHandle outArgs) +{ + OfxTime time; + OfxRectI renderWindow; + _propertySuite->propGetDouble(inArgs, kOfxPropTime, 0, &time); + _propertySuite->propGetIntN(inArgs, kOfxImageEffectPropRenderWindow, 4, &renderWindow.x1); + + OfxImageClipHandle outputClip = nullptr; + OfxPropertySetHandle outputImage = nullptr; + _imageEffectSuite->clipGetHandle(instance, "Output", &outputClip, nullptr); + if (outputClip) + { + _imageEffectSuite->clipGetImage(outputClip, time, nullptr, &outputImage); + if (outputImage) + { + OIIO::ImageBuf outputBuf = propSetToBuf(_propertySuite, outputImage); + _render(outputBuf, renderWindow, inArgs); + } + } + + if (outputImage) + { + _imageEffectSuite->clipReleaseImage(outputImage); + } + return kOfxStatOK; +} + +CheckersPlugin* CheckersPlugin::_instance = nullptr; + +CheckersPlugin::CheckersPlugin() : + GeneratorPlugin("Toucan", "Checkers") +{} + +CheckersPlugin::~CheckersPlugin() +{} + +void CheckersPlugin::setHostFunc(OfxHost* host) +{ + if (!_instance) + { + _instance = new CheckersPlugin; + } + _instance->_host = host; +} + +OfxStatus CheckersPlugin::mainEntryPoint( + const char* action, + const void* handle, + OfxPropertySetHandle inArgs, + OfxPropertySetHandle outArgs) +{ + return _instance->_entryPoint(action, handle, inArgs, outArgs); +} + +OfxStatus CheckersPlugin::_render( + OIIO::ImageBuf& outputBuf, + const OfxRectI& renderWindow, + OfxPropertySetHandle inArgs) +{ + IMATH_NAMESPACE::V2i checkerSize(0, 0); + _propertySuite->propGetIntN(inArgs, "checkerSize", 2, &checkerSize.x); + + double color1[4] = { 0.0, 0.0, 0.0, 0.0 }; + _propertySuite->propGetDoubleN(inArgs, "color1", 4, color1); + + double color2[4] = { 0.0, 0.0, 0.0, 0.0 }; + _propertySuite->propGetDoubleN(inArgs, "color2", 4, color2); + + OIIO::ImageBufAlgo::checker( + outputBuf, + checkerSize.x, + checkerSize.y, + 1, + { + static_cast(color1[0]), + static_cast(color1[1]), + static_cast(color1[2]), + static_cast(color1[3]) + }, + { + static_cast(color2[0]), + static_cast(color2[1]), + static_cast(color2[2]), + static_cast(color2[3]) + }); + + return kOfxStatOK; +} + +FillPlugin* FillPlugin::_instance = nullptr; + +FillPlugin::FillPlugin() : + GeneratorPlugin("Toucan", "Fill") +{} + +FillPlugin::~FillPlugin() +{} + +void FillPlugin::setHostFunc(OfxHost* host) +{ + if (!_instance) + { + _instance = new FillPlugin; + } + _instance->_host = host; +} + +OfxStatus FillPlugin::mainEntryPoint( + const char* action, + const void* handle, + OfxPropertySetHandle inArgs, + OfxPropertySetHandle outArgs) +{ + return _instance->_entryPoint(action, handle, inArgs, outArgs); +} + +OfxStatus FillPlugin::_render( + OIIO::ImageBuf& outputBuf, + const OfxRectI& renderWindow, + OfxPropertySetHandle inArgs) +{ + double color[4] = { 0.0, 0.0, 0.0, 0.0 }; + _propertySuite->propGetDoubleN(inArgs, "color", 4, color); + + OIIO::ImageBufAlgo::fill( + outputBuf, + { + static_cast(color[0]), + static_cast(color[1]), + static_cast(color[2]), + static_cast(color[3]) + }); + + return kOfxStatOK; +} + +NoisePlugin* NoisePlugin::_instance = nullptr; + +NoisePlugin::NoisePlugin() : + GeneratorPlugin("Toucan", "Noise") +{} + +NoisePlugin::~NoisePlugin() +{} + +void NoisePlugin::setHostFunc(OfxHost* host) +{ + if (!_instance) + { + _instance = new NoisePlugin; + } + _instance->_host = host; +} + +OfxStatus NoisePlugin::mainEntryPoint( + const char* action, + const void* handle, + OfxPropertySetHandle inArgs, + OfxPropertySetHandle outArgs) +{ + return _instance->_entryPoint(action, handle, inArgs, outArgs); +} + +OfxStatus NoisePlugin::_render( + OIIO::ImageBuf& outputBuf, + const OfxRectI& renderWindow, + OfxPropertySetHandle inArgs) +{ + std::string type; + char* s = nullptr; + _propertySuite->propGetString(inArgs, "type", 0, &s); + if (s) + { + type = s; + } + + double a = 0.0; + double b = 0.0; + _propertySuite->propGetDouble(inArgs, "a", 0, &a); + _propertySuite->propGetDouble(inArgs, "b", 0, &b); + + int mono = 0; + _propertySuite->propGetInt(inArgs, "mono", 0, &mono); + + int seed = 0; + _propertySuite->propGetInt(inArgs, "seed", 0, &seed); + + OIIO::ImageBufAlgo::noise( + outputBuf, + type, + a, + b, + mono, + seed); + + return kOfxStatOK; +} + +namespace +{ + std::vector plugins = + { + { kOfxImageEffectPluginApi, 1, "Toucan:Checkers", 1, 0, CheckersPlugin::setHostFunc, CheckersPlugin::mainEntryPoint }, + { kOfxImageEffectPluginApi, 1, "Toucan:Fill", 1, 0, FillPlugin::setHostFunc, FillPlugin::mainEntryPoint }, + { kOfxImageEffectPluginApi, 1, "Toucan:Noise", 1, 0, NoisePlugin::setHostFunc, NoisePlugin::mainEntryPoint } + }; +} + +extern "C" +{ + int OfxGetNumberOfPlugins(void) + { + return plugins.size(); + } + + OfxPlugin* OfxGetPlugin(int index) + { + if (index >= 0 && index < plugins.size()) + { + return &plugins[index]; + } + return 0; + } +} diff --git a/plugins/GeneratorPlugin.h b/plugins/GeneratorPlugin.h new file mode 100644 index 0000000..c42f04c --- /dev/null +++ b/plugins/GeneratorPlugin.h @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2024 Darby Johnston +// All rights reserved. + +#pragma once + +#include "Plugin.h" + +#include + +class GeneratorPlugin : public Plugin +{ +public: + GeneratorPlugin(const std::string& group, const std::string& name); + + virtual ~GeneratorPlugin() = 0; + +protected: + virtual OfxStatus _render( + OIIO::ImageBuf&, + const OfxRectI& renderWindow, + OfxPropertySetHandle inArgs) = 0; + + OfxStatus _describeAction(OfxImageEffectHandle) override; + OfxStatus _describeInContextAction( + OfxImageEffectHandle, + OfxPropertySetHandle) override; + OfxStatus _renderAction( + OfxImageEffectHandle, + OfxPropertySetHandle inArgs, + OfxPropertySetHandle outArgs) override; +}; + +class CheckersPlugin : public GeneratorPlugin +{ +public: + CheckersPlugin(); + + virtual ~CheckersPlugin(); + + static void setHostFunc(OfxHost*); + + static OfxStatus mainEntryPoint( + const char* action, + const void* handle, + OfxPropertySetHandle inArgs, + OfxPropertySetHandle outArgs); + +protected: + OfxStatus _render( + OIIO::ImageBuf&, + const OfxRectI& renderWindow, + OfxPropertySetHandle inArgs) override; + +private: + static CheckersPlugin* _instance; +}; + +class FillPlugin : public GeneratorPlugin +{ +public: + FillPlugin(); + + virtual ~FillPlugin(); + + static void setHostFunc(OfxHost*); + + static OfxStatus mainEntryPoint( + const char* action, + const void* handle, + OfxPropertySetHandle inArgs, + OfxPropertySetHandle outArgs); + +protected: + OfxStatus _render( + OIIO::ImageBuf&, + const OfxRectI& renderWindow, + OfxPropertySetHandle inArgs) override; + +private: + static FillPlugin* _instance; +}; + +class NoisePlugin : public GeneratorPlugin +{ +public: + NoisePlugin(); + + virtual ~NoisePlugin(); + + static void setHostFunc(OfxHost*); + + static OfxStatus mainEntryPoint( + const char* action, + const void* handle, + OfxPropertySetHandle inArgs, + OfxPropertySetHandle outArgs); + +protected: + OfxStatus _render( + OIIO::ImageBuf&, + const OfxRectI& renderWindow, + OfxPropertySetHandle inArgs) override; + +private: + static NoisePlugin* _instance; +}; diff --git a/plugins/Plugin.cpp b/plugins/Plugin.cpp index 5b8c01d..fab7227 100644 --- a/plugins/Plugin.cpp +++ b/plugins/Plugin.cpp @@ -77,11 +77,6 @@ OfxStatus Plugin::_describeAction(OfxImageEffectHandle descriptor) kOfxImageEffectPluginPropGrouping, 0, _group.c_str()); - _propertySuite->propSetString( - effectProps, - kOfxImageEffectPropSupportedContexts, - 0, - kOfxImageEffectContextFilter); return kOfxStatOK; } diff --git a/plugins/TransformPlugin.cpp b/plugins/TransformPlugin.cpp index b0e3062..f4da53b 100644 --- a/plugins/TransformPlugin.cpp +++ b/plugins/TransformPlugin.cpp @@ -17,6 +17,19 @@ TransformPlugin::TransformPlugin(const std::string& group, const std::string& na TransformPlugin::~TransformPlugin() {} +OfxStatus TransformPlugin::_describeAction(OfxImageEffectHandle descriptor) +{ + Plugin::_describeAction(descriptor); + OfxPropertySetHandle effectProps; + _imageEffectSuite->getPropertySet(descriptor, &effectProps); + _propertySuite->propSetString( + effectProps, + kOfxImageEffectPropSupportedContexts, + 0, + kOfxImageEffectContextFilter); + return kOfxStatOK; +} + OfxStatus TransformPlugin::_describeInContextAction(OfxImageEffectHandle descriptor, OfxPropertySetHandle inArgs) { OfxPropertySetHandle sourceProps; @@ -136,7 +149,6 @@ OfxStatus FlipPlugin::_render( const OfxRectI& renderWindow, OfxPropertySetHandle inArgs) { - //! \bug This doesn't seem to be working? OIIO::ImageBufAlgo::flip( outputBuf, sourceBuf, @@ -145,9 +157,6 @@ OfxStatus FlipPlugin::_render( renderWindow.x2, renderWindow.y1, renderWindow.y2)); - //OIIO::ImageBufAlgo::copy( - // outputBuf, - // sourceBuf); return kOfxStatOK; } @@ -184,7 +193,6 @@ OfxStatus FlopPlugin::_render( const OfxRectI& renderWindow, OfxPropertySetHandle inArgs) { - //! \bug This doesn't seem to be working? OIIO::ImageBufAlgo::flop( outputBuf, sourceBuf, @@ -243,7 +251,6 @@ OfxStatus ResizePlugin::_render( double filterWidth = 0.0; _propertySuite->propGetDouble(inArgs, "filterWidth", 0, &filterWidth); - //! \bug This doesn't seem to be working? OIIO::ImageBufAlgo::resize( outputBuf, sourceBuf, @@ -301,7 +308,6 @@ OfxStatus RotatePlugin::_render( double filterWidth = 0.0; _propertySuite->propGetDouble(inArgs, "filterWidth", 0, &filterWidth); - //! \bug This doesn't seem to be working? OIIO::ImageBufAlgo::rotate( outputBuf, sourceBuf, diff --git a/plugins/TransformPlugin.h b/plugins/TransformPlugin.h index 25f5bba..383bdec 100644 --- a/plugins/TransformPlugin.h +++ b/plugins/TransformPlugin.h @@ -22,6 +22,7 @@ class TransformPlugin : public Plugin const OfxRectI& renderWindow, OfxPropertySetHandle inArgs) = 0; + OfxStatus _describeAction(OfxImageEffectHandle) override; OfxStatus _describeInContextAction( OfxImageEffectHandle, OfxPropertySetHandle) override; diff --git a/plugins/Util.cpp b/plugins/Util.cpp index a10a9f2..e6e07ff 100644 --- a/plugins/Util.cpp +++ b/plugins/Util.cpp @@ -48,17 +48,19 @@ OIIO::ImageBuf propSetToBuf(OfxPropertySuiteV1* suite, OfxPropertySetHandle hand void* p = nullptr; suite->propGetPointer(handle, kOfxImagePropData, 0, &p); - OIIO::ImageSpec spec; - spec.width = bounds.x2 - bounds.x1; - spec.height = bounds.y2 - bounds.y1; - spec.nchannels = components; + OIIO::TypeDesc format = OIIO::TypeDesc::UNKNOWN; switch (pixelDepth) { - case 1: spec.format = OIIO::TypeDesc::UINT8; break; - case 2: spec.format = OIIO::TypeDesc::UINT16; break; - case 4: spec.format = OIIO::TypeDesc::FLOAT; break; + case 1: format = OIIO::TypeDesc::UINT8; break; + case 2: format = OIIO::TypeDesc::UINT16; break; + case 4: format = OIIO::TypeDesc::FLOAT; break; default: break; } + OIIO::ImageSpec spec( + bounds.x2 - bounds.x1, + bounds.y2 - bounds.y1, + components, + format); return OIIO::ImageBuf( spec, diff --git a/tests/TimelineGraphTest.cpp b/tests/TimelineGraphTest.cpp index de0151b..b096ca2 100644 --- a/tests/TimelineGraphTest.cpp +++ b/tests/TimelineGraphTest.cpp @@ -19,12 +19,12 @@ namespace toucan const std::vector otioFiles = { "CompositeTracks", - "Filters", + "Draw", + "Filter", "Gap", + "Generator", "LinearTimeWarp", - "Patterns", - "Render", - "Transforms", + "Transform", "Transition", "Transition2" };