diff --git a/bin/main.cpp b/bin/main.cpp index 4bf6b48..3054b54 100644 --- a/bin/main.cpp +++ b/bin/main.cpp @@ -13,12 +13,6 @@ using namespace toucan; int main(int argc, char** argv) { - std::filesystem::path parentPath = std::filesystem::path(argv[0]).parent_path(); - auto host = std::make_shared(std::vector{ - parentPath, - parentPath / ".." / ".."}); - return 0; - // Command line arguments. if (argc < 3) { @@ -33,6 +27,7 @@ int main(int argc, char** argv) std::cout << std::endl; return 1; } + const std::filesystem::path parentPath = std::filesystem::path(argv[0]).parent_path(); const std::filesystem::path inputPath(argv[1]); const std::filesystem::path outputPath(argv[2]); const auto outputSplit = splitFileNameNumber(outputPath.stem().string()); @@ -69,6 +64,11 @@ int main(int argc, char** argv) const auto traverse = std::make_shared(inputPath.parent_path(), timeline); const IMATH_NAMESPACE::V2d& imageSize = traverse->getImageSize(); + // Create the host. + auto host = std::make_shared(std::vector{ + parentPath, + parentPath / ".." / ".."}); + // Initialize the filmstrip. OIIO::ImageBuf filmstripBuf; const int thumbnailWidth = 120; @@ -97,7 +97,7 @@ int main(int argc, char** argv) if (auto op = traverse->exec(time)) { // Execute the image operation graph. - const auto buf = op->exec(time); + const auto buf = op->exec(time, host); // Save the image. if (!filmstrip) diff --git a/lib/toucan/BoxOp.cpp b/lib/toucan/BoxOp.cpp index ddca04a..6da107c 100644 --- a/lib/toucan/BoxOp.cpp +++ b/lib/toucan/BoxOp.cpp @@ -30,7 +30,9 @@ namespace toucan _data = value; } - OIIO::ImageBuf BoxOp::exec(const OTIO_NS::RationalTime& time) + OIIO::ImageBuf BoxOp::exec( + const OTIO_NS::RationalTime& time, + const std::shared_ptr& host) { OIIO::ImageBuf buf; if (!_inputs.empty() && _inputs[0]) @@ -40,7 +42,7 @@ namespace toucan { offsetTime -= _timeOffset; } - buf = _inputs[0]->exec(offsetTime); + buf = _inputs[0]->exec(offsetTime, host); OIIO::ImageBufAlgo::render_box( buf, _data.pos1.x, diff --git a/lib/toucan/BoxOp.h b/lib/toucan/BoxOp.h index 637e16b..1806042 100644 --- a/lib/toucan/BoxOp.h +++ b/lib/toucan/BoxOp.h @@ -30,7 +30,9 @@ namespace toucan const BoxData& getData() const; void setData(const BoxData&); - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec( + const OTIO_NS::RationalTime&, + const std::shared_ptr&) override; private: BoxData _data; diff --git a/lib/toucan/CMakeLists.txt b/lib/toucan/CMakeLists.txt index 4ce1442..1e885e9 100644 --- a/lib/toucan/CMakeLists.txt +++ b/lib/toucan/CMakeLists.txt @@ -54,7 +54,9 @@ set(SOURCE TransitionOp.cpp Util.cpp) if(WIN32) - list(APPEND SOURCE UtilWin32.cpp) + list(APPEND SOURCE + PluginWin32.cpp + UtilWin32.cpp) endif() add_library(toucan ${HEADERS} ${HEADERS_PRIVATE} ${SOURCE}) diff --git a/lib/toucan/CheckersOp.cpp b/lib/toucan/CheckersOp.cpp index 55192ee..076318c 100644 --- a/lib/toucan/CheckersOp.cpp +++ b/lib/toucan/CheckersOp.cpp @@ -30,7 +30,9 @@ namespace toucan _data = value; } - OIIO::ImageBuf CheckersOp::exec(const OTIO_NS::RationalTime& time) + OIIO::ImageBuf CheckersOp::exec( + const OTIO_NS::RationalTime& time, + const std::shared_ptr& host) { OIIO::ImageBuf buf; if (!_inputs.empty() && _inputs[0]) @@ -40,7 +42,7 @@ namespace toucan { offsetTime -= _timeOffset; } - buf = _inputs[0]->exec(offsetTime); + buf = _inputs[0]->exec(offsetTime, host); OIIO::ImageBufAlgo::checker( buf, _data.checkerSize.x, diff --git a/lib/toucan/CheckersOp.h b/lib/toucan/CheckersOp.h index 286828d..a4f1a1d 100644 --- a/lib/toucan/CheckersOp.h +++ b/lib/toucan/CheckersOp.h @@ -30,7 +30,9 @@ namespace toucan const CheckersData& getData() const; void setData(const CheckersData&); - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec( + const OTIO_NS::RationalTime&, + const std::shared_ptr&) override; private: CheckersData _data; diff --git a/lib/toucan/ColorMapOp.cpp b/lib/toucan/ColorMapOp.cpp index f24dd43..24f6e5f 100644 --- a/lib/toucan/ColorMapOp.cpp +++ b/lib/toucan/ColorMapOp.cpp @@ -28,7 +28,9 @@ namespace toucan _data = value; } - OIIO::ImageBuf ColorMapOp::exec(const OTIO_NS::RationalTime& time) + OIIO::ImageBuf ColorMapOp::exec( + const OTIO_NS::RationalTime& time, + const std::shared_ptr& host) { OIIO::ImageBuf buf; if (!_inputs.empty() && _inputs[0]) @@ -39,7 +41,7 @@ namespace toucan offsetTime -= _timeOffset; } buf = OIIO::ImageBufAlgo::color_map( - _inputs[0]->exec(offsetTime), + _inputs[0]->exec(offsetTime, host), -1, _data.mapName); diff --git a/lib/toucan/ColorMapOp.h b/lib/toucan/ColorMapOp.h index 020fd05..b83d9a3 100644 --- a/lib/toucan/ColorMapOp.h +++ b/lib/toucan/ColorMapOp.h @@ -27,7 +27,9 @@ namespace toucan const ColorMapData& getData() const; void setData(const ColorMapData&); - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec( + const OTIO_NS::RationalTime&, + const std::shared_ptr&) override; private: ColorMapData _data; diff --git a/lib/toucan/CompOp.cpp b/lib/toucan/CompOp.cpp index 29ca2a2..a3ce1f8 100644 --- a/lib/toucan/CompOp.cpp +++ b/lib/toucan/CompOp.cpp @@ -20,7 +20,9 @@ namespace toucan _premult = premult; } - OIIO::ImageBuf CompOp::exec(const OTIO_NS::RationalTime& time) + OIIO::ImageBuf CompOp::exec( + const OTIO_NS::RationalTime& time, + const std::shared_ptr& host) { OIIO::ImageBuf buf; OTIO_NS::RationalTime offsetTime = time; @@ -30,8 +32,8 @@ namespace toucan } if (_inputs.size() > 1 && _inputs[0] && _inputs[1]) { - auto fg = _inputs[0]->exec(offsetTime); - auto bg = _inputs[1]->exec(offsetTime); + auto fg = _inputs[0]->exec(offsetTime, host); + auto bg = _inputs[1]->exec(offsetTime, host); if (_premult) { fg = OIIO::ImageBufAlgo::premult(fg); @@ -40,7 +42,7 @@ namespace toucan } else if (1 == _inputs.size() && _inputs[0]) { - auto fg = _inputs[0]->exec(offsetTime); + auto fg = _inputs[0]->exec(offsetTime, host); if (_premult) { fg = OIIO::ImageBufAlgo::premult(fg); diff --git a/lib/toucan/CompOp.h b/lib/toucan/CompOp.h index f16dcac..948f94d 100644 --- a/lib/toucan/CompOp.h +++ b/lib/toucan/CompOp.h @@ -19,7 +19,9 @@ namespace toucan //! Set whether images are pre-multiplied before compositing. void setPremult(bool); - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec( + const OTIO_NS::RationalTime&, + const std::shared_ptr&) override; private: bool _premult = false; diff --git a/lib/toucan/FillOp.cpp b/lib/toucan/FillOp.cpp index e295d56..99bc91a 100644 --- a/lib/toucan/FillOp.cpp +++ b/lib/toucan/FillOp.cpp @@ -30,7 +30,9 @@ namespace toucan _data = value; } - OIIO::ImageBuf FillOp::exec(const OTIO_NS::RationalTime& time) + OIIO::ImageBuf FillOp::exec( + const OTIO_NS::RationalTime& time, + const std::shared_ptr& host) { OIIO::ImageBuf buf; if (!_inputs.empty() && _inputs[0]) @@ -40,7 +42,7 @@ namespace toucan { offsetTime -= _timeOffset; } - buf = _inputs[0]->exec(offsetTime); + buf = _inputs[0]->exec(offsetTime, host); OIIO::ImageBufAlgo::fill( buf, { _data.color.x, _data.color.y, _data.color.z, _data.color.w }); diff --git a/lib/toucan/FillOp.h b/lib/toucan/FillOp.h index fa62c7a..5e010bd 100644 --- a/lib/toucan/FillOp.h +++ b/lib/toucan/FillOp.h @@ -28,7 +28,9 @@ namespace toucan const FillData& getData() const; void setData(const FillData&); - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec( + const OTIO_NS::RationalTime&, + const std::shared_ptr&) override; private: FillData _data; diff --git a/lib/toucan/FlipOp.cpp b/lib/toucan/FlipOp.cpp index 837646b..5aed882 100644 --- a/lib/toucan/FlipOp.cpp +++ b/lib/toucan/FlipOp.cpp @@ -16,7 +16,9 @@ namespace toucan FlipOp::~FlipOp() {} - OIIO::ImageBuf FlipOp::exec(const OTIO_NS::RationalTime& time) + OIIO::ImageBuf FlipOp::exec( + const OTIO_NS::RationalTime& time, + const std::shared_ptr& host) { OIIO::ImageBuf buf; if (!_inputs.empty() && _inputs[0]) @@ -26,7 +28,7 @@ namespace toucan { offsetTime -= _timeOffset; } - const auto input = _inputs[0]->exec(offsetTime); + const auto input = _inputs[0]->exec(offsetTime, host); buf = OIIO::ImageBufAlgo::flip(input); } return buf; diff --git a/lib/toucan/FlipOp.h b/lib/toucan/FlipOp.h index 50ca070..49eda16 100644 --- a/lib/toucan/FlipOp.h +++ b/lib/toucan/FlipOp.h @@ -16,7 +16,9 @@ namespace toucan virtual ~FlipOp(); - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec( + const OTIO_NS::RationalTime&, + const std::shared_ptr&) override; }; //! Flip OTIO effect. diff --git a/lib/toucan/FlopOp.cpp b/lib/toucan/FlopOp.cpp index fb56fd1..e87d9cd 100644 --- a/lib/toucan/FlopOp.cpp +++ b/lib/toucan/FlopOp.cpp @@ -16,7 +16,9 @@ namespace toucan FlopOp::~FlopOp() {} - OIIO::ImageBuf FlopOp::exec(const OTIO_NS::RationalTime& time) + OIIO::ImageBuf FlopOp::exec( + const OTIO_NS::RationalTime& time, + const std::shared_ptr& host) { OIIO::ImageBuf buf; if (!_inputs.empty() && _inputs[0]) @@ -26,7 +28,7 @@ namespace toucan { offsetTime -= _timeOffset; } - const auto input = _inputs[0]->exec(offsetTime); + const auto input = _inputs[0]->exec(offsetTime, host); buf = OIIO::ImageBufAlgo::flop(input); } return buf; diff --git a/lib/toucan/FlopOp.h b/lib/toucan/FlopOp.h index d17a011..94d2ffc 100644 --- a/lib/toucan/FlopOp.h +++ b/lib/toucan/FlopOp.h @@ -16,7 +16,9 @@ namespace toucan virtual ~FlopOp(); - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec( + const OTIO_NS::RationalTime&, + const std::shared_ptr&) override; }; //! Flop OTIO effect. diff --git a/lib/toucan/Host.cpp b/lib/toucan/Host.cpp index 8844d5b..35681ec 100644 --- a/lib/toucan/Host.cpp +++ b/lib/toucan/Host.cpp @@ -15,6 +15,70 @@ namespace toucan { _propertySet.setPointer("host", 0, this); + _host.host = (OfxPropertySetHandle)&_propertySet; + _host.fetchSuite = &_fetchSuite; + + _suiteInit(); + _pluginInit(searchPath); + } + + Host::~Host() + { + for (const auto& data : _pluginData) + { + OfxStatus ofxStatus = data.ofxPlugin->mainEntry( + kOfxActionUnload, + nullptr, + nullptr, + nullptr); + } + } + + void Host::filter( + const std::string& name, + const OIIO::ImageBuf& source, + 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["Source"] = bufToPropSet(source); + data.images["Output"] = bufToPropSet(output); + PropertySet args; + args.setDouble(kOfxPropTime, 0, 0.0); + const auto& spec = source.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 Host::_suiteInit() + { _propertySuite.propSetPointer = &PropertySet::setPointer; _propertySuite.propSetString = &PropertySet::setString; _propertySuite.propSetDouble = &PropertySet::setDouble; @@ -35,49 +99,57 @@ namespace toucan _propertySuite.propGetDimension = &PropertySet::getDimension; _effectSuite.getPropertySet = &_getPropertySet; + _effectSuite.clipDefine = &_clipDefine; + _effectSuite.clipGetHandle = &_clipGetHandle; + _effectSuite.clipGetImage = &_clipGetImage; + _effectSuite.clipReleaseImage = &_clipReleaseImage; + } - _host.host = reinterpret_cast(&_propertySet); - _host.fetchSuite = &_fetchSuite; - + void Host::_pluginInit(const std::vector& searchPath) + { + // Find the plugins. std::vector pluginPaths; for (const auto& path : searchPath) { findPlugins(path, pluginPaths); } - for (auto const& path : pluginPaths) + + // Load plugins. + for (const auto& path : pluginPaths) { //std::cout << path.string() << std::endl; try { auto plugin = std::make_shared(path); const int pluginCount = plugin->getCount(); - //std::cout << "plugin count: " << pluginCount << std::endl; - if (pluginCount > 0) + for (int i = 0; i < pluginCount; ++i) { - OfxPlugin* ofxPlugin = plugin->getPlugin(0); - //std::cout << " plugin: " << ofxPlugin->pluginIdentifier << std::endl; - ofxPlugin->setHost(&_host); - OfxStatus ofxStatus = ofxPlugin->mainEntry( - kOfxActionLoad, - nullptr, - nullptr, - nullptr); - switch (ofxStatus) - { - case kOfxStatOK: - case kOfxStatReplyDefault: - _plugins.push_back(plugin); - break; - case kOfxStatErrFatal: + OfxPlugin* ofxPlugin = plugin->getPlugin(i); + if (strcmp(ofxPlugin->pluginApi, kOfxImageEffectPluginApi) == 0) { - std::stringstream ss; - ss << "Fatal error in plugin: " << path.string(); - throw std::runtime_error(ss.str()); - break; - } - case kOfxStatFailed: - default: - break; + ofxPlugin->setHost(&_host); + OfxStatus ofxStatus = ofxPlugin->mainEntry( + kOfxActionLoad, + nullptr, + nullptr, + nullptr); + switch (ofxStatus) + { + case kOfxStatOK: + case kOfxStatReplyDefault: + _pluginData.push_back({ plugin, ofxPlugin }); + break; + case kOfxStatErrFatal: + { + std::stringstream ss; + ss << "Fatal error in plugin: " << path.string(); + throw std::runtime_error(ss.str()); + break; + } + case kOfxStatFailed: + default: + break; + } } } } @@ -87,32 +159,31 @@ namespace toucan } } - for (const auto& plugin : _plugins) + // Initialize plugins. + for (auto& data : _pluginData) { - OfxStatus ofxStatus = plugin->getPlugin(0)->mainEntry( + OfxStatus ofxStatus = data.ofxPlugin->mainEntry( kOfxActionDescribe, - plugin.get(), - nullptr, - nullptr); - char* s = nullptr; - plugin->getPropertySet(0)->getString(kOfxPropLabel, 0, &s); - std::cout << "plugin: " << s << std::endl; - plugin->getPropertySet(0)->getString(kOfxImageEffectPluginPropGrouping, 0, &s); - std::cout << " group: " << s << std::endl; - plugin->getPropertySet(0)->getString(kOfxImageEffectPropSupportedContexts, 0, &s); - std::cout << " context: " << s << std::endl; - } - } - - Host::~Host() - { - for (const auto& plugin : _plugins) - { - OfxStatus ofxStatus = plugin->getPlugin(0)->mainEntry( - kOfxActionUnload, - nullptr, + &data, nullptr, nullptr); + int contextCount = 0; + data.effectPropertySet.getDimension(kOfxImageEffectPropSupportedContexts, &contextCount); + for (int i = 0; i < contextCount; ++i) + { + char* context = nullptr; + data.effectPropertySet.getString(kOfxImageEffectPropSupportedContexts, i, &context); + if (context) + { + PropertySet propertySet; + propertySet.setString(kOfxImageEffectPropContext, 0, context); + ofxStatus = data.ofxPlugin->mainEntry( + kOfxImageEffectActionDescribeInContext, + &data, + (OfxPropertySetHandle)&propertySet, + nullptr); + } + } } } @@ -138,8 +209,37 @@ namespace toucan OfxStatus Host::_getPropertySet(OfxImageEffectHandle handle, OfxPropertySetHandle* propHandle) { - Plugin* plugin = reinterpret_cast(handle); - *propHandle = reinterpret_cast(plugin->getPropertySet(0)); + PluginData* data = reinterpret_cast(handle); + *propHandle = (OfxPropertySetHandle)&data->effectPropertySet; + return kOfxStatOK; + } + + OfxStatus Host::_clipDefine(OfxImageEffectHandle handle, const char* name, OfxPropertySetHandle* propHandle) + { + PluginData* data = reinterpret_cast(handle); + *propHandle = (OfxPropertySetHandle)&data->clipPropertySets[name]; + return kOfxStatOK; + } + + OfxStatus Host::_clipGetHandle(OfxImageEffectHandle handle, const char* name, OfxImageClipHandle* clip, OfxPropertySetHandle* propHandle) + { + PluginData* data = reinterpret_cast(handle); + *clip = (OfxImageClipHandle)&data->images[name]; + if (propHandle) + { + *propHandle = (OfxPropertySetHandle)&data->clipPropertySets[name]; + } + return kOfxStatOK; + } + + OfxStatus Host::_clipGetImage(OfxImageClipHandle handle, OfxTime, const OfxRectD*, OfxPropertySetHandle* propHandle) + { + *propHandle = (OfxPropertySetHandle)handle; + return kOfxStatOK; + } + + OfxStatus Host::_clipReleaseImage(OfxPropertySetHandle handle) + { return kOfxStatOK; } } diff --git a/lib/toucan/Host.h b/lib/toucan/Host.h index 666dcb1..c0b9e7a 100644 --- a/lib/toucan/Host.h +++ b/lib/toucan/Host.h @@ -9,6 +9,8 @@ #include +#include + #include #include @@ -21,15 +23,35 @@ namespace toucan ~Host(); + void filter( + const std::string& name, + const OIIO::ImageBuf&, + OIIO::ImageBuf&, + const PropertySet& = PropertySet()); + private: - static const void* _fetchSuite(OfxPropertySetHandle host, const char* suiteName, int suiteVersion); + void _suiteInit(); + void _pluginInit(const std::vector& searchPath); - static OfxStatus _getPropertySet(OfxImageEffectHandle, OfxPropertySetHandle* propHandle); + static const void* _fetchSuite(OfxPropertySetHandle host, const char* suiteName, int suiteVersion); + static OfxStatus _getPropertySet(OfxImageEffectHandle, OfxPropertySetHandle*); + static OfxStatus _clipDefine(OfxImageEffectHandle, const char* name, OfxPropertySetHandle*); + static OfxStatus _clipGetHandle(OfxImageEffectHandle, const char* name, OfxImageClipHandle*, OfxPropertySetHandle*); + static OfxStatus _clipGetImage(OfxImageClipHandle, OfxTime, const OfxRectD*, OfxPropertySetHandle*); + static OfxStatus _clipReleaseImage(OfxPropertySetHandle); PropertySet _propertySet; + OfxHost _host; OfxPropertySuiteV1 _propertySuite; OfxImageEffectSuiteV1 _effectSuite; - OfxHost _host; - std::vector > _plugins; + struct PluginData + { + std::shared_ptr plugin; + OfxPlugin* ofxPlugin = nullptr; + PropertySet effectPropertySet; + std::map clipPropertySets; + std::map images; + }; + std::vector _pluginData; }; } diff --git a/lib/toucan/ImageOp.h b/lib/toucan/ImageOp.h index 71f482c..6f42e00 100644 --- a/lib/toucan/ImageOp.h +++ b/lib/toucan/ImageOp.h @@ -4,6 +4,8 @@ #pragma once +#include + #include #include @@ -27,7 +29,9 @@ namespace toucan void setTimeOffset(const OTIO_NS::RationalTime&); //! Execute the image operation for the given time. - virtual OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) = 0; + virtual OIIO::ImageBuf exec( + const OTIO_NS::RationalTime&, + const std::shared_ptr&) = 0; protected: OTIO_NS::RationalTime _timeOffset; diff --git a/lib/toucan/InvertOp.cpp b/lib/toucan/InvertOp.cpp index 1c21061..8c754b2 100644 --- a/lib/toucan/InvertOp.cpp +++ b/lib/toucan/InvertOp.cpp @@ -4,6 +4,8 @@ #include "InvertOp.h" +#include "PropertySet.h" + #include namespace toucan @@ -16,7 +18,9 @@ namespace toucan InvertOp::~InvertOp() {} - OIIO::ImageBuf InvertOp::exec(const OTIO_NS::RationalTime& time) + OIIO::ImageBuf InvertOp::exec( + const OTIO_NS::RationalTime& time, + const std::shared_ptr& host) { OIIO::ImageBuf buf; if (!_inputs.empty() && _inputs[0]) @@ -26,12 +30,10 @@ namespace toucan { offsetTime -= _timeOffset; } - const auto input = _inputs[0]->exec(offsetTime); - auto roi = input.roi(); - roi.chend = 3; - buf = OIIO::ImageBufAlgo::unpremult(input); - OIIO::ImageBufAlgo::invert(buf, buf, roi); - OIIO::ImageBufAlgo::repremult(buf, buf); + const auto input = _inputs[0]->exec(offsetTime, host); + const auto& spec = input.spec(); + buf = OIIO::ImageBuf(spec); + host->filter("Toucan:Invert", input, buf); } return buf; } diff --git a/lib/toucan/InvertOp.h b/lib/toucan/InvertOp.h index dcdee06..b21052b 100644 --- a/lib/toucan/InvertOp.h +++ b/lib/toucan/InvertOp.h @@ -16,7 +16,9 @@ namespace toucan virtual ~InvertOp(); - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec( + const OTIO_NS::RationalTime&, + const std::shared_ptr&) override; }; //! Invert OTIO effect. diff --git a/lib/toucan/LineOp.cpp b/lib/toucan/LineOp.cpp index 08dcfd4..5ff59b5 100644 --- a/lib/toucan/LineOp.cpp +++ b/lib/toucan/LineOp.cpp @@ -30,7 +30,9 @@ namespace toucan _data = value; } - OIIO::ImageBuf LineOp::exec(const OTIO_NS::RationalTime& time) + OIIO::ImageBuf LineOp::exec( + const OTIO_NS::RationalTime& time, + const std::shared_ptr& host) { OIIO::ImageBuf buf; if (!_inputs.empty() && _inputs[0]) @@ -40,7 +42,7 @@ namespace toucan { offsetTime -= _timeOffset; } - buf = _inputs[0]->exec(offsetTime); + buf = _inputs[0]->exec(offsetTime, host); OIIO::ImageBufAlgo::render_line( buf, _data.pos1.x, diff --git a/lib/toucan/LineOp.h b/lib/toucan/LineOp.h index 4314919..bcbc323 100644 --- a/lib/toucan/LineOp.h +++ b/lib/toucan/LineOp.h @@ -30,7 +30,9 @@ namespace toucan const LineData& getData() const; void setData(const LineData&); - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec( + const OTIO_NS::RationalTime&, + const std::shared_ptr&) override; private: LineData _data; diff --git a/lib/toucan/LinearTimeWarpOp.cpp b/lib/toucan/LinearTimeWarpOp.cpp index 304a350..2a29802 100644 --- a/lib/toucan/LinearTimeWarpOp.cpp +++ b/lib/toucan/LinearTimeWarpOp.cpp @@ -16,7 +16,9 @@ namespace toucan LinearTimeWarpOp::~LinearTimeWarpOp() {} - OIIO::ImageBuf LinearTimeWarpOp::exec(const OTIO_NS::RationalTime& time) + OIIO::ImageBuf LinearTimeWarpOp::exec( + const OTIO_NS::RationalTime& time, + const std::shared_ptr& host) { OIIO::ImageBuf buf; if (!_inputs.empty()) @@ -27,7 +29,7 @@ namespace toucan offsetTime -= _timeOffset; } const OTIO_NS::RationalTime scaledTime = OTIO_NS::RationalTime(offsetTime.value() * _timeScalar, offsetTime.rate()).floor(); - buf = _inputs[0]->exec(scaledTime); + buf = _inputs[0]->exec(scaledTime, host); } return buf; } diff --git a/lib/toucan/LinearTimeWarpOp.h b/lib/toucan/LinearTimeWarpOp.h index e421264..2f44eed 100644 --- a/lib/toucan/LinearTimeWarpOp.h +++ b/lib/toucan/LinearTimeWarpOp.h @@ -18,7 +18,9 @@ namespace toucan virtual ~LinearTimeWarpOp(); - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec( + const OTIO_NS::RationalTime&, + const std::shared_ptr&) override; private: float _timeScalar = 1.F; diff --git a/lib/toucan/NoiseOp.cpp b/lib/toucan/NoiseOp.cpp index edae31d..da1086e 100644 --- a/lib/toucan/NoiseOp.cpp +++ b/lib/toucan/NoiseOp.cpp @@ -28,7 +28,9 @@ namespace toucan _data = value; } - OIIO::ImageBuf NoiseOp::exec(const OTIO_NS::RationalTime& time) + OIIO::ImageBuf NoiseOp::exec( + const OTIO_NS::RationalTime& time, + const std::shared_ptr& host) { OIIO::ImageBuf buf; if (!_inputs.empty() && _inputs[0]) @@ -38,7 +40,7 @@ namespace toucan { offsetTime -= _timeOffset; } - buf = _inputs[0]->exec(offsetTime); + buf = _inputs[0]->exec(offsetTime, host); OIIO::ImageBufAlgo::noise( buf, _data.type, diff --git a/lib/toucan/NoiseOp.h b/lib/toucan/NoiseOp.h index 6596c91..c11eec5 100644 --- a/lib/toucan/NoiseOp.h +++ b/lib/toucan/NoiseOp.h @@ -32,7 +32,9 @@ namespace toucan const NoiseData& getData() const; void setData(const NoiseData&); - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec( + const OTIO_NS::RationalTime&, + const std::shared_ptr&) override; private: NoiseData _data; diff --git a/lib/toucan/Plugin.cpp b/lib/toucan/Plugin.cpp index ee8e6ae..e163184 100644 --- a/lib/toucan/Plugin.cpp +++ b/lib/toucan/Plugin.cpp @@ -4,85 +4,15 @@ #include "Plugin.h" -#include "Util.h" - -#include - -#include -#include - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif // WIN32_LEAN_AND_MEAN -#ifndef NOMINMAX -#define NOMINMAX -#endif // NOMINMAX -#include - namespace toucan { - namespace - { - typedef int(__cdecl* GetNumberOfPluginsFunc)(void); - typedef OfxPlugin* (__cdecl* GetPluginFunc)(int); - typedef void(__cdecl* SetHostFunc)(OfxHost*); - typedef OfxStatus (__cdecl* MainEntryPointFunc)( - const char*, - const void*, - OfxPropertySetHandle, - OfxPropertySetHandle); - } - - struct Plugin::Private - { - HINSTANCE pluginInstance = nullptr; - GetNumberOfPluginsFunc getNumberOfPlugins = nullptr; - GetPluginFunc getPlugin = nullptr; - }; - - Plugin::Plugin(const std::filesystem::path& path) : - _p(new Private) - { - _p->pluginInstance = LoadLibrary(path.string().c_str()); - if (!_p->pluginInstance) - { - std::stringstream ss; - ss << "Cannot load library: " << path.string(); - throw std::runtime_error(ss.str()); - } - - _p->getNumberOfPlugins = (GetNumberOfPluginsFunc)GetProcAddress( - _p->pluginInstance, - "OfxGetNumberOfPlugins"); - _p->getPlugin = (GetPluginFunc)GetProcAddress( - _p->pluginInstance, - "OfxGetPlugin"); - - if (_p->getNumberOfPlugins) - { - _effectPropertySets.resize(_p->getNumberOfPlugins()); - } - } - - Plugin::~Plugin() - { - FreeLibrary(_p->pluginInstance); - } - int Plugin::getCount() { - return _p->getNumberOfPlugins ? _p->getNumberOfPlugins() : 0; + return _getNumberOfPlugins ? _getNumberOfPlugins() : 0; } OfxPlugin* Plugin::getPlugin(int index) { - return _p->getPlugin ? _p->getPlugin(index) : nullptr; - } - - PropertySet* Plugin::getPropertySet(int index) - { - return index >= 0 && index < _effectPropertySets.size() ? - &_effectPropertySets[index] : - nullptr; + return _getPlugin ? _getPlugin(index) : nullptr; } } diff --git a/lib/toucan/Plugin.h b/lib/toucan/Plugin.h index 3d2d2e3..21fedb2 100644 --- a/lib/toucan/Plugin.h +++ b/lib/toucan/Plugin.h @@ -24,10 +24,13 @@ namespace toucan OfxPlugin* getPlugin(int); - PropertySet* getPropertySet(int); - private: - std::vector _effectPropertySets; + typedef int(__cdecl* GetNumberOfPluginsFunc)(void); + GetNumberOfPluginsFunc _getNumberOfPlugins = nullptr; + + typedef OfxPlugin* (__cdecl* GetPluginFunc)(int); + GetPluginFunc _getPlugin = nullptr; + struct Private; std::unique_ptr _p; }; diff --git a/lib/toucan/PluginWin32.cpp b/lib/toucan/PluginWin32.cpp new file mode 100644 index 0000000..9e63db5 --- /dev/null +++ b/lib/toucan/PluginWin32.cpp @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2024 Darby Johnston +// All rights reserved. + +#include "Plugin.h" + +#include "Util.h" + +#include +#include + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif // WIN32_LEAN_AND_MEAN +#ifndef NOMINMAX +#define NOMINMAX +#endif // NOMINMAX +#include + +namespace toucan +{ + struct Plugin::Private + { + HINSTANCE pluginInstance = nullptr; + }; + + Plugin::Plugin(const std::filesystem::path& path) : + _p(new Private) + { + _p->pluginInstance = LoadLibrary(path.string().c_str()); + if (!_p->pluginInstance) + { + std::stringstream ss; + ss << "Cannot load library: " << path.string(); + throw std::runtime_error(ss.str()); + } + + _getNumberOfPlugins = (GetNumberOfPluginsFunc)GetProcAddress( + _p->pluginInstance, + "OfxGetNumberOfPlugins"); + _getPlugin = (GetPluginFunc)GetProcAddress( + _p->pluginInstance, + "OfxGetPlugin"); + } + + Plugin::~Plugin() + { + FreeLibrary(_p->pluginInstance); + } +} diff --git a/lib/toucan/PowOp.cpp b/lib/toucan/PowOp.cpp index 9fda367..86d9d79 100644 --- a/lib/toucan/PowOp.cpp +++ b/lib/toucan/PowOp.cpp @@ -28,7 +28,9 @@ namespace toucan _data = value; } - OIIO::ImageBuf PowOp::exec(const OTIO_NS::RationalTime& time) + OIIO::ImageBuf PowOp::exec( + const OTIO_NS::RationalTime& time, + const std::shared_ptr& host) { OIIO::ImageBuf buf; if (!_inputs.empty() && _inputs[0]) @@ -39,7 +41,7 @@ namespace toucan offsetTime -= _timeOffset; } buf = OIIO::ImageBufAlgo::pow( - _inputs[0]->exec(offsetTime), + _inputs[0]->exec(offsetTime, host), _data.value); } return buf; diff --git a/lib/toucan/PowOp.h b/lib/toucan/PowOp.h index 9704c25..e4a51df 100644 --- a/lib/toucan/PowOp.h +++ b/lib/toucan/PowOp.h @@ -27,7 +27,9 @@ namespace toucan const PowData& getData() const; void setData(const PowData&); - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec( + const OTIO_NS::RationalTime&, + const std::shared_ptr&) override; private: PowData _data; diff --git a/lib/toucan/PropertySet.cpp b/lib/toucan/PropertySet.cpp index 29cad7a..b9063d0 100644 --- a/lib/toucan/PropertySet.cpp +++ b/lib/toucan/PropertySet.cpp @@ -4,6 +4,8 @@ #include "PropertySet.h" +#include + namespace toucan { OfxStatus PropertySet::setPointer(const char* property, int index, void* value) @@ -52,21 +54,45 @@ namespace toucan OfxStatus PropertySet::setPointerN(const char* property, int count, void* const* value) { + auto& v = _p[property]; + v.resize(count); + for (int i = 0; i < count; ++i) + { + v[i] = value[i]; + } return kOfxStatOK; } OfxStatus PropertySet::setStringN(const char* property, int count, const char* const* value) { + auto& v = _s[property]; + v.resize(count); + for (int i = 0; i < count; ++i) + { + v[i] = value[i]; + } return kOfxStatOK; } OfxStatus PropertySet::setDoubleN(const char* property, int count, const double* value) { + auto& v = _d[property]; + v.resize(count); + for (int i = 0; i < count; ++i) + { + v[i] = value[i]; + } return kOfxStatOK; } OfxStatus PropertySet::setIntN(const char* property, int count, const int* value) { + auto& v = _i[property]; + v.resize(count); + for (int i = 0; i < count; ++i) + { + v[i] = value[i]; + } return kOfxStatOK; } @@ -95,10 +121,11 @@ namespace toucan { const auto& string = v[index]; const size_t size = string.size(); - _sBuf.resize(size + 1); - memcpy(_sBuf.data(), string.c_str(), size); - _sBuf[size] = 0; - *value = _sBuf.data(); + _buf.resize(1); + _buf[0].resize(size + 1); + memcpy(_buf[0].data(), string.c_str(), size); + _buf[0][size] = 0; + *value = _buf[0].data(); return kOfxStatOK; } } @@ -107,32 +134,109 @@ namespace toucan OfxStatus PropertySet::getDouble(const char* property, int index, double* value) { - return kOfxStatOK; + auto d = _d.find(property); + if (d != _d.end()) + { + const auto& v = d->second; + if (index < v.size()) + { + *value = v[index]; + return kOfxStatOK; + } + } + return kOfxStatFailed; } OfxStatus PropertySet::getInt(const char* property, int index, int* value) { - return kOfxStatOK; + auto i = _i.find(property); + if (i != _i.end()) + { + const auto& v = i->second; + if (index < v.size()) + { + *value = v[index]; + return kOfxStatOK; + } + } + return kOfxStatFailed; } OfxStatus PropertySet::getPointerN(const char* property, int count, void** value) { - return kOfxStatOK; + auto p = _p.find(property); + if (p != _p.end()) + { + const auto& v = p->second; + if (count == v.size()) + { + for (int i = 0; i < count; ++i) + { + value[i] = v[i]; + } + return kOfxStatOK; + } + } + return kOfxStatFailed; } OfxStatus PropertySet::getStringN(const char* property, int count, char** value) { - return kOfxStatOK; + auto s = _s.find(property); + if (s != _s.end()) + { + const auto& v = s->second; + if (count == v.size()) + { + _buf.resize(count); + for (int i = 0; i < count; ++i) + { + const size_t size = v[i].size(); + _buf[i].resize(size + 1); + memcpy(_buf[i].data(), v[i].c_str(), size); + _buf[i][size] = 0; + value[i] = _buf[i].data(); + } + return kOfxStatOK; + } + } + return kOfxStatFailed; } OfxStatus PropertySet::getDoubleN(const char* property, int count, double* value) { - return kOfxStatOK; + auto d = _d.find(property); + if (d != _d.end()) + { + const auto& v = d->second; + if (count == v.size()) + { + for (int i = 0; i < count; ++i) + { + value[i] = v[i]; + } + return kOfxStatOK; + } + } + return kOfxStatFailed; } OfxStatus PropertySet::getIntN(const char* property, int count, int* value) { - return kOfxStatOK; + auto i = _i.find(property); + if (i != _i.end()) + { + const auto& v = i->second; + if (count == v.size()) + { + for (int i = 0; i < count; ++i) + { + value[i] = v[i]; + } + return kOfxStatOK; + } + } + return kOfxStatFailed; } OfxStatus PropertySet::reset(const char* property) @@ -284,4 +388,46 @@ namespace toucan { return reinterpret_cast(handle)->getDimension(property, count); } + + PropertySet bufToPropSet(const OIIO::ImageBuf& buf) + { + PropertySet out; + + const auto& spec = buf.spec(); + OfxRectI bounds; + bounds.x1 = 0; + bounds.x2 = spec.width; + bounds.y1 = 0; + bounds.y2 = spec.height; + out.setIntN(kOfxImagePropBounds, 4, &bounds.x1); + + std::string components; + switch (spec.nchannels) + { + case 1: components = kOfxImageComponentAlpha; break; + case 3: components = kOfxImageComponentRGB; break; + case 4: components = kOfxImageComponentRGBA; break; + } + out.setString(kOfxImageEffectPropComponents, 0, components.c_str()); + + std::string pixelDepth; + if (OIIO::TypeDesc::UINT8 == spec.format) + { + pixelDepth = kOfxBitDepthByte; + } + else if (OIIO::TypeDesc::UINT16 == spec.format) + { + pixelDepth = kOfxBitDepthShort; + } + else if (OIIO::TypeDesc::FLOAT == spec.format) + { + pixelDepth = kOfxBitDepthFloat; + } + + out.setString(kOfxImageEffectPropPixelDepth, 0, pixelDepth.c_str()); + out.setInt(kOfxImagePropRowBytes, 0, spec.scanline_bytes()); + out.setPointer(kOfxImagePropData, 0, (void*)buf.localpixels()); + + return out; + } } diff --git a/lib/toucan/PropertySet.h b/lib/toucan/PropertySet.h index 9caf92c..88ea2f8 100644 --- a/lib/toucan/PropertySet.h +++ b/lib/toucan/PropertySet.h @@ -6,12 +6,15 @@ #include +#include + #include #include #include namespace toucan { + //! Container class for OpenFX values. class PropertySet { public: @@ -56,9 +59,11 @@ namespace toucan private: std::map > _p; std::map > _s; - std::vector _sBuf; + std::vector > _buf; std::map > _d; std::map > _i; }; + //! Convert to a property set. + PropertySet bufToPropSet(const OIIO::ImageBuf&); } diff --git a/lib/toucan/ReadOp.cpp b/lib/toucan/ReadOp.cpp index 3d50464..7cfcde3 100644 --- a/lib/toucan/ReadOp.cpp +++ b/lib/toucan/ReadOp.cpp @@ -18,7 +18,9 @@ namespace toucan ReadOp::~ReadOp() {} - OIIO::ImageBuf ReadOp::exec(const OTIO_NS::RationalTime&) + OIIO::ImageBuf ReadOp::exec( + const OTIO_NS::RationalTime&, + const std::shared_ptr& host) { OIIO::ImageBuf buf(_path.string()); const auto& spec = buf.spec(); diff --git a/lib/toucan/ReadOp.h b/lib/toucan/ReadOp.h index 3b17a2c..346c7a9 100644 --- a/lib/toucan/ReadOp.h +++ b/lib/toucan/ReadOp.h @@ -20,7 +20,9 @@ namespace toucan virtual ~ReadOp(); - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec( + const OTIO_NS::RationalTime&, + const std::shared_ptr&) override; private: std::filesystem::path _path; diff --git a/lib/toucan/ResizeOp.cpp b/lib/toucan/ResizeOp.cpp index dee5c00..378630a 100644 --- a/lib/toucan/ResizeOp.cpp +++ b/lib/toucan/ResizeOp.cpp @@ -28,7 +28,9 @@ namespace toucan _data = value; } - OIIO::ImageBuf ResizeOp::exec(const OTIO_NS::RationalTime& time) + OIIO::ImageBuf ResizeOp::exec( + const OTIO_NS::RationalTime& time, + const std::shared_ptr& host) { OIIO::ImageBuf buf; if (!_inputs.empty() && _inputs[0]) @@ -39,7 +41,7 @@ namespace toucan offsetTime -= _timeOffset; } buf = OIIO::ImageBufAlgo::resize( - _inputs[0]->exec(offsetTime), + _inputs[0]->exec(offsetTime, host), _data.filterName, _data.filterWidth, OIIO::ROI(0, _data.size.x, 0, _data.size.y, 0, 1, 0, 4)); diff --git a/lib/toucan/ResizeOp.h b/lib/toucan/ResizeOp.h index d76cefa..506d36f 100644 --- a/lib/toucan/ResizeOp.h +++ b/lib/toucan/ResizeOp.h @@ -29,7 +29,9 @@ namespace toucan const ResizeData& getData() const; void setData(const ResizeData&); - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec( + const OTIO_NS::RationalTime&, + const std::shared_ptr&) override; private: ResizeData _data; diff --git a/lib/toucan/RotateOp.cpp b/lib/toucan/RotateOp.cpp index 990203a..edef7b8 100644 --- a/lib/toucan/RotateOp.cpp +++ b/lib/toucan/RotateOp.cpp @@ -28,7 +28,9 @@ namespace toucan _data = value; } - OIIO::ImageBuf RotateOp::exec(const OTIO_NS::RationalTime& time) + OIIO::ImageBuf RotateOp::exec( + const OTIO_NS::RationalTime& time, + const std::shared_ptr& host) { OIIO::ImageBuf buf; if (!_inputs.empty() && _inputs[0]) @@ -39,7 +41,7 @@ namespace toucan offsetTime -= _timeOffset; } buf = OIIO::ImageBufAlgo::rotate( - _inputs[0]->exec(offsetTime), + _inputs[0]->exec(offsetTime, host), _data.angle / 360.F * 2.F * M_PI, _data.filterName, _data.filterWidth); diff --git a/lib/toucan/RotateOp.h b/lib/toucan/RotateOp.h index 35e17c5..dfd1a21 100644 --- a/lib/toucan/RotateOp.h +++ b/lib/toucan/RotateOp.h @@ -29,7 +29,9 @@ namespace toucan const RotateData& getData() const; void setData(const RotateData&); - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec( + const OTIO_NS::RationalTime&, + const std::shared_ptr&) override; private: RotateData _data; diff --git a/lib/toucan/SaturateOp.cpp b/lib/toucan/SaturateOp.cpp index cb6b7ab..23fecdf 100644 --- a/lib/toucan/SaturateOp.cpp +++ b/lib/toucan/SaturateOp.cpp @@ -28,7 +28,9 @@ namespace toucan _data = value; } - OIIO::ImageBuf SaturateOp::exec(const OTIO_NS::RationalTime& time) + OIIO::ImageBuf SaturateOp::exec( + const OTIO_NS::RationalTime& time, + const std::shared_ptr& host) { OIIO::ImageBuf buf; if (!_inputs.empty() && _inputs[0]) @@ -39,7 +41,7 @@ namespace toucan offsetTime -= _timeOffset; } buf = OIIO::ImageBufAlgo::saturate( - _inputs[0]->exec(offsetTime), + _inputs[0]->exec(offsetTime, host), _data.value); } return buf; diff --git a/lib/toucan/SaturateOp.h b/lib/toucan/SaturateOp.h index 1bf62a5..9d6f8f9 100644 --- a/lib/toucan/SaturateOp.h +++ b/lib/toucan/SaturateOp.h @@ -27,7 +27,9 @@ namespace toucan const SaturateData& getData() const; void setData(const SaturateData&); - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec( + const OTIO_NS::RationalTime&, + const std::shared_ptr&) override; private: SaturateData _data; diff --git a/lib/toucan/SequenceReadOp.cpp b/lib/toucan/SequenceReadOp.cpp index 618d299..0be58a6 100644 --- a/lib/toucan/SequenceReadOp.cpp +++ b/lib/toucan/SequenceReadOp.cpp @@ -33,7 +33,9 @@ namespace toucan SequenceReadOp::~SequenceReadOp() {} - OIIO::ImageBuf SequenceReadOp::exec(const OTIO_NS::RationalTime& time) + OIIO::ImageBuf SequenceReadOp::exec( + const OTIO_NS::RationalTime& time, + const std::shared_ptr& host) { OTIO_NS::RationalTime offsetTime = time; if (!_timeOffset.is_invalid_time()) diff --git a/lib/toucan/SequenceReadOp.h b/lib/toucan/SequenceReadOp.h index fa1bb5f..db4d6d4 100644 --- a/lib/toucan/SequenceReadOp.h +++ b/lib/toucan/SequenceReadOp.h @@ -26,7 +26,9 @@ namespace toucan virtual ~SequenceReadOp(); - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec( + const OTIO_NS::RationalTime&, + const std::shared_ptr&) override; private: std::filesystem::path _base; diff --git a/lib/toucan/TextOp.cpp b/lib/toucan/TextOp.cpp index ff4bc50..6f86677 100644 --- a/lib/toucan/TextOp.cpp +++ b/lib/toucan/TextOp.cpp @@ -30,7 +30,9 @@ namespace toucan _data = value; } - OIIO::ImageBuf TextOp::exec(const OTIO_NS::RationalTime& time) + OIIO::ImageBuf TextOp::exec( + const OTIO_NS::RationalTime& time, + const std::shared_ptr& host) { OIIO::ImageBuf buf; if (!_inputs.empty() && _inputs[0]) @@ -40,7 +42,7 @@ namespace toucan { offsetTime -= _timeOffset; } - buf = _inputs[0]->exec(offsetTime); + buf = _inputs[0]->exec(offsetTime, host); OIIO::ImageBufAlgo::render_text( buf, _data.pos.x, diff --git a/lib/toucan/TextOp.h b/lib/toucan/TextOp.h index 7b7716c..55e82c7 100644 --- a/lib/toucan/TextOp.h +++ b/lib/toucan/TextOp.h @@ -31,7 +31,9 @@ namespace toucan const TextData& getData() const; void setData(const TextData&); - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec( + const OTIO_NS::RationalTime&, + const std::shared_ptr&) override; private: TextData _data; diff --git a/lib/toucan/TimelineTraverse.cpp b/lib/toucan/TimelineTraverse.cpp index 0c37cdf..3ca021b 100644 --- a/lib/toucan/TimelineTraverse.cpp +++ b/lib/toucan/TimelineTraverse.cpp @@ -31,8 +31,7 @@ namespace toucan { const std::string url = externalRef->target_url(); const std::filesystem::path path = _path / url; - auto read = std::make_shared(path); - const auto buf = read->exec(OTIO_NS::RationalTime(0.0, 1.0)); + const OIIO::ImageBuf buf(path.string()); const auto& spec = buf.spec(); if (spec.width > 0) { @@ -45,15 +44,12 @@ namespace toucan { const std::string url = sequenceRef->target_url_base(); const std::filesystem::path path = _path / url; - auto read = std::make_shared( - path.string(), - sequenceRef->name_prefix(), - sequenceRef->name_suffix(), - sequenceRef->start_frame(), - sequenceRef->frame_step(), - sequenceRef->rate(), - sequenceRef->frame_zero_padding()); - const auto buf = read->exec(OTIO_NS::RationalTime(0.0, 1.0)); + std::stringstream ss; + ss << path.string() << + sequenceRef->name_prefix() << + std::setw(sequenceRef->frame_zero_padding()) << std::setfill('0') << sequenceRef->start_frame() << + sequenceRef->name_suffix(); + const OIIO::ImageBuf buf(ss.str()); const auto& spec = buf.spec(); if (spec.width > 0) { diff --git a/lib/toucan/TransitionOp.cpp b/lib/toucan/TransitionOp.cpp index 729c16c..16f8e7a 100644 --- a/lib/toucan/TransitionOp.cpp +++ b/lib/toucan/TransitionOp.cpp @@ -20,7 +20,9 @@ namespace toucan TransitionOp::~TransitionOp() {} - OIIO::ImageBuf TransitionOp::exec(const OTIO_NS::RationalTime& time) + OIIO::ImageBuf TransitionOp::exec( + const OTIO_NS::RationalTime& time, + const std::shared_ptr& host) { OIIO::ImageBuf buf; if (_inputs.size() > 1 && _inputs[0] && _inputs[1]) @@ -31,8 +33,8 @@ namespace toucan offsetTime -= _timeOffset; } - const auto a = _inputs[0]->exec(offsetTime); - const auto b = _inputs[1]->exec(offsetTime); + const auto a = _inputs[0]->exec(offsetTime, host); + const auto b = _inputs[1]->exec(offsetTime, host); const float v = (offsetTime.rescaled_to(_range.duration().rate()) - _range.start_time()).value() / diff --git a/lib/toucan/TransitionOp.h b/lib/toucan/TransitionOp.h index 61302f0..60545ca 100644 --- a/lib/toucan/TransitionOp.h +++ b/lib/toucan/TransitionOp.h @@ -18,7 +18,9 @@ namespace toucan virtual ~TransitionOp(); - OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + OIIO::ImageBuf exec( + const OTIO_NS::RationalTime&, + const std::shared_ptr&) override; private: OTIO_NS::TimeRange _range; diff --git a/lib/toucan/Util.cpp b/lib/toucan/Util.cpp index 9866b9f..de1dbab 100644 --- a/lib/toucan/Util.cpp +++ b/lib/toucan/Util.cpp @@ -4,6 +4,8 @@ #include "Util.h" +#include + namespace toucan { namespace diff --git a/lib/toucan/Util.h b/lib/toucan/Util.h index 22d04e0..d83afa6 100644 --- a/lib/toucan/Util.h +++ b/lib/toucan/Util.h @@ -4,6 +4,10 @@ #pragma once +#include + +#include + #include #include diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 08ec719..a1927a7 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -1,7 +1,9 @@ set(HEADERS - InvertPlugin.h) + InvertPlugin.h + Util.h) set(SOURCE - InvertPlugin.cpp) + InvertPlugin.cpp + Util.cpp) add_library(toucan-invert MODULE ${HEADERS} ${SOURCE}) target_link_libraries(toucan-invert PUBLIC OpenImageIO::OpenImageIO) diff --git a/plugins/InvertPlugin.cpp b/plugins/InvertPlugin.cpp index 2d3bcb5..1fa3b69 100644 --- a/plugins/InvertPlugin.cpp +++ b/plugins/InvertPlugin.cpp @@ -4,8 +4,12 @@ #include "InvertPlugin.h" +#include "Util.h" + #include +#include + #include namespace @@ -17,7 +21,7 @@ namespace { kOfxImageEffectPluginApi, 1, - "toucan:invert", + "Toucan:Invert", 1, 0, SetHostFunc, @@ -75,12 +79,12 @@ extern "C" effectProps, kOfxPropLabel, 0, - "invert"); + "Invert"); propertySuite->propSetString( effectProps, kOfxImageEffectPluginPropGrouping, 0, - "toucan"); + "Toucan"); propertySuite->propSetString( effectProps, kOfxImageEffectPropSupportedContexts, @@ -89,6 +93,116 @@ extern "C" return kOfxStatOK; } + OfxStatus DescribeInContextAction(OfxImageEffectHandle descriptor, OfxPropertySetHandle inArgs) + { + OfxPropertySetHandle sourceProps; + OfxPropertySetHandle outputProps; + imageEffectSuite->clipDefine(descriptor, "Source", &sourceProps); + 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( + sourceProps, + kOfxImageEffectPropSupportedComponents, + i, + components[i].c_str()); + propertySuite->propSetString( + outputProps, + kOfxImageEffectPropSupportedComponents, + i, + components[i].c_str()); + } + for (int i = 0; i < pixelDepths.size(); ++i) + { + propertySuite->propSetString( + sourceProps, + kOfxImageEffectPropSupportedPixelDepths, + i, + pixelDepths[i].c_str()); + propertySuite->propSetString( + outputProps, + kOfxImageEffectPropSupportedPixelDepths, + i, + pixelDepths[i].c_str()); + } + return kOfxStatOK; + } + + OfxStatus 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 sourceClip = nullptr; + OfxImageClipHandle outputClip = nullptr; + OfxPropertySetHandle sourceImage = nullptr; + OfxPropertySetHandle outputImage = nullptr; + imageEffectSuite->clipGetHandle(instance, "Source", &sourceClip, nullptr); + imageEffectSuite->clipGetHandle(instance, "Output", &outputClip, nullptr); + if (sourceClip && outputClip) + { + imageEffectSuite->clipGetImage(sourceClip, time, nullptr, &sourceImage); + imageEffectSuite->clipGetImage(outputClip, time, nullptr, &outputImage); + if (sourceImage && outputImage) + { + const OIIO::ImageBuf sourceBuf = propSetToBuf(propertySuite, sourceImage); + OIIO::ImageBuf outputBuf = propSetToBuf(propertySuite, outputImage); + OIIO::ImageBufAlgo::invert( + outputBuf, + sourceBuf, + OIIO::ROI( + renderWindow.x1, + renderWindow.x2, + renderWindow.y1, + renderWindow.y2, + 0, + 1, + 0, + 3)); + OIIO::ImageBufAlgo::copy( + outputBuf, + sourceBuf, + OIIO::TypeUnknown, + OIIO::ROI( + renderWindow.x1, + renderWindow.x2, + renderWindow.y1, + renderWindow.y2, + 0, + 1, + 3, + 4)); + } + } + + if (sourceImage) + { + imageEffectSuite->clipReleaseImage(sourceImage); + } + if (outputImage) + { + imageEffectSuite->clipReleaseImage(outputImage); + } + return kOfxStatOK; + } + OfxStatus MainEntryPoint( const char* action, const void* handle, @@ -97,6 +211,7 @@ extern "C" { OfxStatus out = kOfxStatReplyDefault; //std::cout << "MainEntryPoint: " << action << std::endl; + OfxImageEffectHandle effectHandle = (OfxImageEffectHandle)handle; if (strcmp(action, kOfxActionLoad) == 0) { out = LoadAction(); @@ -107,7 +222,15 @@ extern "C" } else if (strcmp(action, kOfxActionDescribe) == 0) { - out = DescribeAction((OfxImageEffectHandle)handle); + out = DescribeAction(effectHandle); + } + else if (strcmp(action, kOfxImageEffectActionDescribeInContext) == 0) + { + out = DescribeInContextAction(effectHandle, inArgs); + } + else if (strcmp(action, kOfxImageEffectActionRender) == 0) + { + out = RenderAction(effectHandle, inArgs, outArgs); } return out; } diff --git a/plugins/Util.cpp b/plugins/Util.cpp new file mode 100644 index 0000000..a10a9f2 --- /dev/null +++ b/plugins/Util.cpp @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2024 Darby Johnston +// All rights reserved. + +#include "Util.h" + +#include + +OIIO::ImageBuf propSetToBuf(OfxPropertySuiteV1* suite, OfxPropertySetHandle handle) +{ + OfxRectI bounds; + suite->propGetIntN(handle, kOfxImagePropBounds, 4, &bounds.x1); + + int components = 0; + char* s = nullptr; + suite->propGetString(handle, kOfxImageEffectPropComponents, 0, &s); + if (strcmp(s, kOfxImageComponentAlpha) == 0) + { + components = 1; + } + else if (strcmp(s, kOfxImageComponentRGB) == 0) + { + components = 3; + } + else if (strcmp(s, kOfxImageComponentRGBA) == 0) + { + components = 4; + } + + int pixelDepth = 0; + suite->propGetString(handle, kOfxImageEffectPropPixelDepth, 0, &s); + if (strcmp(s, kOfxBitDepthByte) == 0) + { + pixelDepth = 1; + } + else if (strcmp(s, kOfxBitDepthShort) == 0) + { + pixelDepth = 2; + } + else if (strcmp(s, kOfxBitDepthFloat) == 0) + { + pixelDepth = 4; + } + + int rowBytes = 0; + suite->propGetInt(handle, kOfxImagePropRowBytes, 0, &rowBytes); + + 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; + 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; + default: break; + } + + return OIIO::ImageBuf( + spec, + p, + components * pixelDepth, + rowBytes, + 0); +} diff --git a/plugins/Util.h b/plugins/Util.h new file mode 100644 index 0000000..f00bf97 --- /dev/null +++ b/plugins/Util.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2024 Darby Johnston +// All rights reserved. + +#pragma once + +#include + +#include + +//! Convert from a property set. +OIIO::ImageBuf propSetToBuf(OfxPropertySuiteV1*, OfxPropertySetHandle); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2a7b487..7013428 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,11 +1,13 @@ set(HEADERS CompOpTest.h + PropertySetTest.h ReadOpTest.h TimelineTraverseTest.h UtilTest.h) set(SOURCE CompOpTest.cpp + PropertySetTest.cpp ReadOpTest.cpp TimelineTraverseTest.cpp UtilTest.cpp diff --git a/tests/CompOpTest.cpp b/tests/CompOpTest.cpp index ec8c4c3..93a46a3 100644 --- a/tests/CompOpTest.cpp +++ b/tests/CompOpTest.cpp @@ -9,7 +9,9 @@ namespace toucan { - void compOpTest(const std::filesystem::path& path) + void compOpTest( + const std::filesystem::path& path, + const std::shared_ptr& host) { std::cout << "compOpTest" << std::endl; auto fg = std::make_shared(path / "Letter_A.png"); @@ -17,7 +19,7 @@ namespace toucan auto comp = std::make_shared( std::vector >{ fg, bg }); comp->setPremult(true); - auto buf = comp->exec(OTIO_NS::RationalTime()); + auto buf = comp->exec(OTIO_NS::RationalTime(), host); buf.write("compOpTest.png"); } } diff --git a/tests/CompOpTest.h b/tests/CompOpTest.h index c11a6ad..3d23ed2 100644 --- a/tests/CompOpTest.h +++ b/tests/CompOpTest.h @@ -4,9 +4,11 @@ #pragma once -#include +#include namespace toucan { - void compOpTest(const std::filesystem::path&); + void compOpTest( + const std::filesystem::path&, + const std::shared_ptr&); } diff --git a/tests/PropertySetTest.cpp b/tests/PropertySetTest.cpp new file mode 100644 index 0000000..5def72e --- /dev/null +++ b/tests/PropertySetTest.cpp @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2024 Darby Johnston +// All rights reserved. + +#include "PropertySetTest.h" + +#include + +#include + +namespace toucan +{ + void propertySetTest() + { + { + int a = 1; + PropertySet p; + p.setPointer("p", 0, &a); + int* b = nullptr; + p.getPointer("p", 0, (void**)&b); + assert(b); + assert(b == &a); + } + { + std::string a = "a"; + PropertySet p; + p.setString("s", 0, a.c_str()); + char* b = nullptr; + p.getString("s", 0, &b); + assert(b); + assert(b == a); + } + { + double a = 1.0; + PropertySet p; + p.setDouble("d", 0, a); + double b = 0.0; + p.getDouble("d", 0, &b); + assert(b); + assert(b == a); + } + { + int a = 1; + PropertySet p; + p.setInt("i", 0, a); + int b = 0.0; + p.getInt("i", 0, &b); + assert(b); + assert(b == a); + } + { + int v0 = 0; + int v1 = 1; + void* a[] = { &v0, &v1 }; + PropertySet p; + p.setPointerN("p", 2, a); + void* b[2] = { nullptr, nullptr }; + p.getPointerN("p", 2, b); + assert(b); + assert(b[0] == a[0]); + assert(b[1] == a[1]); + } + { + std::string v[2] = { "v0", "v1" }; + const char* a[] = { v[0].c_str(), v[1].c_str() }; + PropertySet p; + p.setStringN("s", 2, a); + char* b[2] = { nullptr, nullptr }; + p.getStringN("s", 2, b); + assert(b); + assert(b[0] == v[0]); + assert(b[1] == v[1]); + } + { + double a[2] = { 1.0, 2.0 }; + PropertySet p; + p.setDoubleN("d", 2, a); + double b[2] = { 0.0, 0.0 }; + p.getDoubleN("d", 2, b); + assert(b[0] == a[0]); + assert(b[1] == a[1]); + } + { + int a[2] = { 1, 2 }; + PropertySet p; + p.setIntN("i", 2, a); + int b[2] = { 0, 0 }; + p.getIntN("i", 2, b); + assert(b[0] == a[0]); + assert(b[1] == a[1]); + } + { + PropertySet p; + int dim = 0; + p.getDimension("i", &dim); + assert(0 == dim); + p.setInt("i", 0, 1); + dim = 0; + p.getDimension("i", &dim); + assert(1 == dim); + p.setInt("i", 1, 2); + dim = 0; + p.getDimension("i", &dim); + assert(2 == dim); + } + { + PropertySet p; + OfxStatus status = p.reset("i"); + assert(status != kOfxStatOK); + p.setInt("i", 0, 1); + status = p.reset("i"); + assert(kOfxStatOK == status); + } + } +} diff --git a/tests/PropertySetTest.h b/tests/PropertySetTest.h new file mode 100644 index 0000000..8d29bed --- /dev/null +++ b/tests/PropertySetTest.h @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2024 Darby Johnston +// All rights reserved. + +#pragma once + +namespace toucan +{ + void propertySetTest(); +} diff --git a/tests/ReadOpTest.cpp b/tests/ReadOpTest.cpp index 697bd16..f86123f 100644 --- a/tests/ReadOpTest.cpp +++ b/tests/ReadOpTest.cpp @@ -10,11 +10,13 @@ namespace toucan { - void readOpTest(const std::filesystem::path& path) + void readOpTest( + const std::filesystem::path& path, + const std::shared_ptr& host) { std::cout << "readOpTest" << std::endl; auto read = std::make_shared(path / "Letter_A.png"); - auto buf = read->exec(OTIO_NS::RationalTime()); + auto buf = read->exec(OTIO_NS::RationalTime(), host); const auto& spec = buf.spec(); assert(spec.width > 0); } diff --git a/tests/ReadOpTest.h b/tests/ReadOpTest.h index 1179e13..9b4ea26 100644 --- a/tests/ReadOpTest.h +++ b/tests/ReadOpTest.h @@ -4,9 +4,11 @@ #pragma once -#include +#include namespace toucan { - void readOpTest(const std::filesystem::path&); + void readOpTest( + const std::filesystem::path&, + const std::shared_ptr&); } diff --git a/tests/TimelineTraverseTest.cpp b/tests/TimelineTraverseTest.cpp index 5b13180..dbd8749 100644 --- a/tests/TimelineTraverseTest.cpp +++ b/tests/TimelineTraverseTest.cpp @@ -11,7 +11,9 @@ namespace toucan { - void timelineTraverseTest(const std::filesystem::path& path) + void timelineTraverseTest( + const std::filesystem::path& path, + const std::shared_ptr& host) { std::cout << "timelineTraverseTest" << std::endl; const std::vector otioFiles = @@ -57,7 +59,7 @@ namespace toucan if (auto op = traverse->exec(time)) { // Execute the image operation graph. - const auto buf = op->exec(time); + const auto buf = op->exec(time, host); // Save the image. std::stringstream ss; diff --git a/tests/TimelineTraverseTest.h b/tests/TimelineTraverseTest.h index a3149ba..37806f5 100644 --- a/tests/TimelineTraverseTest.h +++ b/tests/TimelineTraverseTest.h @@ -4,9 +4,11 @@ #pragma once -#include +#include namespace toucan { - void timelineTraverseTest(const std::filesystem::path&); + void timelineTraverseTest( + const std::filesystem::path&, + const std::shared_ptr&); } diff --git a/tests/main.cpp b/tests/main.cpp index 8d5fe0c..9aae40a 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -3,11 +3,13 @@ // All rights reserved. #include "CompOpTest.h" +#include "PropertySetTest.h" #include "ReadOpTest.h" #include "TimelineTraverseTest.h" #include "UtilTest.h" #include +#include #include @@ -20,11 +22,16 @@ int main(int argc, char** argv) std::cout << "Usage: toucan-test (path to test data)" << std::endl; return 1; } + const std::filesystem::path parentPath = std::filesystem::path(argv[0]).parent_path(); const std::filesystem::path path(argv[1]); init(); - compOpTest(path); - readOpTest(path); - timelineTraverseTest(path); + auto host = std::make_shared(std::vector{ + parentPath, + parentPath / ".." / ".."}); + compOpTest(path, host); + propertySetTest(); + readOpTest(path, host); + timelineTraverseTest(path, host); utilTest(path); return 0; }