From 81b2640411a1e86060f07ac9cf9219bb3d0c0a7b Mon Sep 17 00:00:00 2001 From: Mike Solar Date: Fri, 24 Oct 2025 20:20:04 +0800 Subject: [PATCH 1/4] add: some changes to add openfx support --- app/pluginSupport/OliveClip.cpp | 122 ++++++++++++++++++++++++++++ app/pluginSupport/OliveClip.h | 63 ++++++++++++++ app/pluginSupport/OliveInstance.cpp | 45 ++++++++-- ext/KDDockWidgets | 2 +- ext/core | 2 +- 5 files changed, 225 insertions(+), 9 deletions(-) create mode 100644 app/pluginSupport/OliveClip.cpp create mode 100644 app/pluginSupport/OliveClip.h diff --git a/app/pluginSupport/OliveClip.cpp b/app/pluginSupport/OliveClip.cpp new file mode 100644 index 000000000..7edd1e2af --- /dev/null +++ b/app/pluginSupport/OliveClip.cpp @@ -0,0 +1,122 @@ +/* + * Olive Community Edition - Non-Linear Video Editor + * Copyright (C) 2025 Olive CE Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +// +// Created by mikesolar on 25-10-1. +// + +#include "OliveClip.h" + +#include "common/Current.h" +const std::string &olive::plugin::OliveClipInstance::getUnmappedBitDepth() const +{ + switch (params_.format()) { + case PixelFormat::INVALID: + return kOfxBitDepthNone; + case PixelFormat::U8: + return kOfxBitDepthByte; + case PixelFormat::U16: + return kOfxBitDepthShort; + case PixelFormat::F16: + return kOfxBitDepthHalf; + case PixelFormat::F32: + return kOfxBitDepthFloat; + default: + return kOfxBitDepthNone; + } +} +const std::string & +olive::plugin::OliveClipInstance::getUnmappedComponents() const +{ + switch (params_.channel_count()) { + case 1: + return kOfxImageComponentAlpha; + case 3: + return kOfxImageComponentRGB; + case 4: + return kOfxImageComponentRGBA; + default: + return kOfxImageComponentNone; + } +} +const std::string &olive::plugin::OliveClipInstance::getPremult() const +{ + if (params_.premultiplied_alpha()) { + return kOfxImagePreMultiplied; + } else { + return kOfxImageUnPreMultiplied; + } +} +double olive::plugin::OliveClipInstance::getAspectRatio() const +{ + return params_ + .pixel_aspect_ratio() + .toDouble(); +} +double olive::plugin::OliveClipInstance::getFrameRate() const +{ + return params_.frame_rate().toDouble(); +} +void olive::plugin::OliveClipInstance::getFrameRange(double &startFrame, + double &endFrame) const +{ + startFrame = + params_.frame_rate().toDouble() * + params_.start_time(); + endFrame = + startFrame + + params_.frame_rate().toDouble() * + params_.duration(); +} +const std::string &olive::plugin::OliveClipInstance::getFieldOrder() const +{ + switch (params_.interlacing()) { + case VideoParams::kInterlaceNone: + return kOfxImageFieldNone; + case VideoParams::kInterlacedTopFirst: + return kOfxImageFieldUpper; + case VideoParams::kInterlacedBottomFirst: + return kOfxImageFieldLower; + } + return kOfxImageFieldNone; +} +bool olive::plugin::OliveClipInstance::getConnected() const +{ + return params_.format() == + PixelFormat::INVALID; +} +double olive::plugin::OliveClipInstance::getUnmappedFrameRate() const +{ + return getFrameRate(); +} +void olive::plugin::OliveClipInstance::getUnmappedFrameRange( + double &startFrame, double &endFrame) const +{ + getFrameRange(startFrame, endFrame); +} +bool olive::plugin::OliveClipInstance::getContinuousSamples() const +{ + return false; +} +OFX::Host::ImageEffect::Image * +olive::plugin::OliveClipInstance::getImage(OfxTime time, + const OfxRectD *optionalBounds) +{ + +} \ No newline at end of file diff --git a/app/pluginSupport/OliveClip.h b/app/pluginSupport/OliveClip.h new file mode 100644 index 000000000..94ba596e0 --- /dev/null +++ b/app/pluginSupport/OliveClip.h @@ -0,0 +1,63 @@ +/* + * Olive Community Edition - Non-Linear Video Editor + * Copyright (C) 2025 Olive CE Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +// +// Created by mikesolar on 25-10-1. +// + +#ifndef OLIVECLIP_H +#define OLIVECLIP_H +#include "ofxhClip.h" +#include "render/videoparams.h" +namespace olive +{ +namespace plugin +{ +class OliveClipInstance: public OFX::Host::ImageEffect::ClipInstance { +public: + OliveClipInstance(OFX::Host::ImageEffect::Instance* effectInstance, + OFX::Host::ImageEffect::ClipDescriptor& desc,VideoParams params) + : ClipInstance(effectInstance, desc) + { + params_ = params; + } + const std::string &getUnmappedBitDepth() const override; + const std::string &getUnmappedComponents() const override; + const std::string &getPremult() const override; + double getAspectRatio() const override; + double getFrameRate() const override; + void getFrameRange(double &startFrame, double &endFrame) const override; + const std::string &getFieldOrder() const override; + bool getConnected() const override; + double getUnmappedFrameRate() const override; + void getUnmappedFrameRange(double &startFrame, double &endFrame) const override; + bool getContinuousSamples() const override; + OFX::Host::ImageEffect::Image* getImage(OfxTime time, const OfxRectD *optionalBounds) override; + OfxRectD getRegionOfDefinition(OfxTime time) const override; + const std::string &findSupportedComp(const std::string &s) const override; +private: + VideoParams params_; + +}; +} +} + + + +#endif //OLIVECLIP_H diff --git a/app/pluginSupport/OliveInstance.cpp b/app/pluginSupport/OliveInstance.cpp index 8a8fba951..0c0030fe8 100644 --- a/app/pluginSupport/OliveInstance.cpp +++ b/app/pluginSupport/OliveInstance.cpp @@ -16,6 +16,8 @@ * along with this program. If not, see . */ #include "OliveInstance.h" + +#include "OliveClip.h" #include "ofxCore.h" #include "ofxMessage.h" #include "common/Current.h" @@ -101,18 +103,18 @@ OfxStatus OliveInstance::clearPersistentMessage() } void OliveInstance::getProjectSize(double &xSize, double &ySize) const { - xSize = Current::getInstance().currentVideoParams().width(); - ySize = Current::getInstance().currentVideoParams().height(); + xSize =params_.width(); + ySize =params_.height(); } void OliveInstance::getProjectOffset(double &xOffset, double &yOffset) const { - xOffset = Current::getInstance().currentVideoParams().x(); - yOffset = Current::getInstance().currentVideoParams().y(); + xOffset =params_.x(); + yOffset =params_.y(); } void OliveInstance::getProjectExtent(double &xSize, double &ySize) const { - xSize = Current::getInstance().currentVideoParams().width(); - ySize = Current::getInstance().currentVideoParams().height(); + xSize =params_.width(); + ySize =params_.height(); // TODO: Ensure this project does not support this. } double OliveInstance::getProjectPixelAspectRatio() const @@ -124,7 +126,36 @@ double OliveInstance::getProjectPixelAspectRatio() const } double OliveInstance::getFrameRate() const { - return Current::getInstance().currentVideoParams().frame_rate().toDouble(); + return params_.frame_rate().toDouble(); +} + +double OliveInstance::getEffectDuration() const +{ + // Return a default duration value + return 100.0; +} + +double OliveInstance::getFrameRecursive() const +{ + // Return current frame (this would typically be set by the host during rendering) + return 0.0; +} + +void OliveInstance::getRenderScaleRecursive(double &x, double &y) const +{ + // Return default render scale (1.0, 1.0) + x = 1.0; + y = 1.0; +} + +OFX::Host::ImageEffect::ClipInstance *OliveInstance::newClipInstance( + OFX::Host::ImageEffect::Instance *plugin, + OFX::Host::ImageEffect::ClipDescriptor *descriptor, + int index) +{ + // Create a new clip instance + OFX::Host::ImageEffect::ClipInstance* clipInstance = new OliveClipInstance(plugin, *descriptor, params_); + return clipInstance; } } diff --git a/ext/KDDockWidgets b/ext/KDDockWidgets index 8d2d0a576..f24a04252 160000 --- a/ext/KDDockWidgets +++ b/ext/KDDockWidgets @@ -1 +1 @@ -Subproject commit 8d2d0a5764f8393cc148a2296d511276a8ffe559 +Subproject commit f24a04252ba319ef323769e766b3ba5252c15c4c diff --git a/ext/core b/ext/core index 08eeee5fa..25e4152bb 160000 --- a/ext/core +++ b/ext/core @@ -1 +1 @@ -Subproject commit 08eeee5fa90bed55442f2be9e324482f0be0a73c +Subproject commit 25e4152bb70a0f4e30b9249e4f4b31e7936dc84a From 26e6924cdf150b400fdac4f66fce7b88e9aeb56f Mon Sep 17 00:00:00 2001 From: Mike Solar Date: Sat, 25 Oct 2025 17:59:50 +0800 Subject: [PATCH 2/4] feat: Implement OFX plugin support infrastructure This commit adds comprehensive support for OFX plugins in Olive, including: - Add plugin node infrastructure with getPlugin() method - Implement OliveHost for managing OFX plugin instances - Create OliveInstance to handle plugin parameter and timeline interactions - Add OliveClip implementation for plugin clip handling - Implement region of definition (RoD) and region of interest (RoI) functionality - Add interactive mode support in Current class - Integrate plugin rendering into RenderProcessor - Fix include dependencies and remove unused OlivePlugin.cpp reference --- app/common/Current.h | 4 ++ app/main.cpp | 2 +- app/node/node.h | 5 ++ app/pluginSupport/OliveClip.cpp | 23 ++++++++ app/pluginSupport/OliveClip.h | 20 ++++++- app/pluginSupport/OliveHost.cpp | 5 +- app/pluginSupport/OliveInstance.cpp | 6 ++ app/pluginSupport/OliveInstance.h | 60 ++++++++++++++++++- app/render/renderprocessor.cpp | 89 +++++++++++++++++++++++++++++ 9 files changed, 210 insertions(+), 4 deletions(-) diff --git a/app/common/Current.h b/app/common/Current.h index b2076e351..aee702922 100644 --- a/app/common/Current.h +++ b/app/common/Current.h @@ -51,6 +51,10 @@ public: { currentAudioParams_ = params; } + bool interactive() + { + return true; + } private: static Current current; olive::VideoParams currentVideoParams_; diff --git a/app/main.cpp b/app/main.cpp index 90f5e28a2..68c0005a4 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -24,6 +24,7 @@ * Use the navigation above to find documentation on classes or source files. */ +#include "OliveHost.h" extern "C" { #include #include @@ -52,7 +53,6 @@ extern "C" { #ifdef USE_CRASHPAD #include "common/crashpadinterface.h" #endif // USE_CRASHPAD -#include "OlivePlugin.cpp" int decompress_project(const QString &project) { if (project.isEmpty()) { diff --git a/app/node/node.h b/app/node/node.h index c4b356ac2..b2f05d95f 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -1124,7 +1124,12 @@ public: static const QString kEnabledInput; + std::shared_ptr getPlugin(); protected: + + // If is set, this is a plugin node. + std::shared_ptr plugin; + void InsertInput(const QString &id, NodeValue::Type type, const QVariant &default_value, InputFlags flags, int index); diff --git a/app/pluginSupport/OliveClip.cpp b/app/pluginSupport/OliveClip.cpp index 7edd1e2af..b41ec7cc7 100644 --- a/app/pluginSupport/OliveClip.cpp +++ b/app/pluginSupport/OliveClip.cpp @@ -118,5 +118,28 @@ OFX::Host::ImageEffect::Image * olive::plugin::OliveClipInstance::getImage(OfxTime time, const OfxRectD *optionalBounds) { + return &image_; +} +OfxRectD +olive::plugin::OliveClipInstance::getRegionOfDefinition(OfxTime time) const +{ + if (regionOfDefinitions_.contains(time)) { + return regionOfDefinitions_[time]; + } + OfxRectD regionOfDefinition; + regionOfDefinition.x1=regionOfDefinition.y1=0; + regionOfDefinition.x2=params_.width(); + regionOfDefinition.y2=params_.height(); + return regionOfDefinition; +} +void olive::plugin::OliveClipInstance::setRegionOfDefinition( + OfxRectD regionOfDefinition, OfxTime time) +{ + regionOfDefinitions_[time] = regionOfDefinition; +} +void olive::plugin::OliveClipInstance::setDefaultRegionOfDefinition( + OfxRectD regionOfDefinition) +{ + defaultRegionOfDefinitions_ = regionOfDefinition; } \ No newline at end of file diff --git a/app/pluginSupport/OliveClip.h b/app/pluginSupport/OliveClip.h index 94ba596e0..833402257 100644 --- a/app/pluginSupport/OliveClip.h +++ b/app/pluginSupport/OliveClip.h @@ -25,6 +25,8 @@ #define OLIVECLIP_H #include "ofxhClip.h" #include "render/videoparams.h" + +#include namespace olive { namespace plugin @@ -37,6 +39,11 @@ public: { params_ = params; } + OFX::Host::ImageEffect::Image& getOutputImage() + { + return image_; + } + const std::string &getUnmappedBitDepth() const override; const std::string &getUnmappedComponents() const override; const std::string &getPremult() const override; @@ -50,10 +57,21 @@ public: bool getContinuousSamples() const override; OFX::Host::ImageEffect::Image* getImage(OfxTime time, const OfxRectD *optionalBounds) override; OfxRectD getRegionOfDefinition(OfxTime time) const override; - const std::string &findSupportedComp(const std::string &s) const override; + + void setRegionOfDefinition(OfxRectD regionOfDefinition, OfxTime time); + void olive::plugin::OliveClipInstance::setDefaultRegionOfDefinition( + OfxRectD regionOfDefinition); +# ifdef OFX_SUPPORTS_OPENGLRENDER + OFX::Host::ImageEffect::Texture* loadTexture(OfxTime time, const char *format, const OfxRectD *optionalBounds) { return NULL; }; +# endif private: VideoParams params_; + QMap regionOfDefinitions_; + + OfxRectD defaultRegionOfDefinitions_; + + OFX::Host::ImageEffect::Image image_; }; } } diff --git a/app/pluginSupport/OliveHost.cpp b/app/pluginSupport/OliveHost.cpp index 8477cb4fd..d0a0e1355 100644 --- a/app/pluginSupport/OliveHost.cpp +++ b/app/pluginSupport/OliveHost.cpp @@ -25,6 +25,9 @@ #include #include #include "OliveHost.h" + +#include "OliveInstance.h" +#include "common/Current.h" using namespace OFX::Host; using namespace olive::plugin; void loadPlugins(QString path) @@ -79,7 +82,7 @@ OFX::Host::ImageEffect::Instance* OliveHost::newInstance(void* clientData, OFX::Host::ImageEffect::ImageEffectPlugin* plugin, OFX::Host::ImageEffect::Descriptor& desc, const std::string& context){ - //ImageEffect::Instance* instance=new ImageEffect::Instance(clientData,plugin,desc,context); + ImageEffect::Instance* instance=new OliveInstance(plugin,desc,context,Current::getInstance().interactive()); instances_.append(instance); return instance; }; diff --git a/app/pluginSupport/OliveInstance.cpp b/app/pluginSupport/OliveInstance.cpp index 0c0030fe8..f653aeeaf 100644 --- a/app/pluginSupport/OliveInstance.cpp +++ b/app/pluginSupport/OliveInstance.cpp @@ -147,6 +147,12 @@ void OliveInstance::getRenderScaleRecursive(double &x, double &y) const x = 1.0; y = 1.0; } +OFX::Host::Param::Instance * +OliveInstance::newParam(const std::string &name, + OFX::Host::Param::Descriptor &Descriptor) +{ + +} OFX::Host::ImageEffect::ClipInstance *OliveInstance::newClipInstance( OFX::Host::ImageEffect::Instance *plugin, diff --git a/app/pluginSupport/OliveInstance.h b/app/pluginSupport/OliveInstance.h index 30265f56c..5e2e0178d 100644 --- a/app/pluginSupport/OliveInstance.h +++ b/app/pluginSupport/OliveInstance.h @@ -20,6 +20,8 @@ #include "ofxImageEffect.h" #include #include "ofxhImageEffect.h" +#include "render/videoparams.h" + #include #include #include @@ -49,6 +51,10 @@ public: return kOfxImageFieldNone; }; + void setVideoParam(VideoParams params) + { + this->params_=params; + } OFX::Host::ImageEffect::ClipInstance *newClipInstance( OFX::Host::ImageEffect::Instance *plugin, OFX::Host::ImageEffect::ClipDescriptor *descriptor, @@ -88,9 +94,61 @@ public: /// renderScale void getRenderScaleRecursive(double &x, double &y) const override; + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + // overridden for Param::SetInstance + + /// make a parameter instance + /// + /// Client host code needs to implement this + virtual OFX::Host::Param::Instance* newParam(const std::string& name, OFX::Host::Param::Descriptor& Descriptor); + + /// Triggered when the plug-in calls OfxParameterSuiteV1::paramEditBegin + /// + /// Client host code needs to implement this + virtual OfxStatus editBegin(const std::string& name); + + /// Triggered when the plug-in calls OfxParameterSuiteV1::paramEditEnd + /// + /// Client host code needs to implement this + virtual OfxStatus editEnd(); + + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + // overridden for Progress::ProgressI + + /// Start doing progress. + virtual void progressStart(const std::string &message, const std::string &messageid); + + /// finish yer progress + virtual void progressEnd(); + + /// set the progress to some level of completion, returns + /// false if you should abandon processing, true to continue + virtual bool progressUpdate(double t); + + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + // overridden for TimeLine::TimeLineI + + /// get the current time on the timeline. This is not necessarily the same + /// time as being passed to an action (eg render) + virtual double timeLineGetTime(); + + /// set the timeline to a specific time + virtual void timeLineGotoTime(double t); + + /// get the first and last times available on the effect's timeline + virtual void timeLineGetBounds(double &t1, double &t2); private: QList persistentErrors_; - + VideoParams params_; }; } } \ No newline at end of file diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 530a06ccd..bdec1ceed 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -28,6 +28,8 @@ #include "node/block/transition/transition.h" #include "node/project.h" #include "rendermanager.h" +#include "pluginSupport/OliveClip.h" +#include "pluginSupport/OliveHost.h" namespace olive { @@ -157,6 +159,7 @@ void RenderProcessor::Run() SetCancelPointer(ticket_->GetCancelAtom()); + VideoParams params=ticket_->property("vparam").value(); SetCacheVideoParams(ticket_->property("vparam").value()); SetCacheAudioParams(ticket_->property("aparam").value()); @@ -165,6 +168,92 @@ void RenderProcessor::Run() return; } + // if is a plugin + Node *node=ticket_->property("node").value(); + if (node && node->getPlugin()) { + std::shared_ptr plugin + = node->getPlugin(); + std::unique_ptr instance(plugin->createInstance(kOfxImageEffectContextFilter, NULL)); + + OfxStatus stat; + stat = instance->createInstanceAction(); + if(stat != kOfxStatOK && stat != kOfxStatReplyDefault) { + ticket_->Finish(); + return; + } + // now we need to to call getClipPreferences on the instance so that it does the clip component/depth + // logic and caches away the components and depth on each clip. + bool ok = instance->getClipPreferences(); + if (!ok) { + ticket_->Finish(); + return; + } + + // current render scale of 1 + OfxPointD renderScale; + renderScale.x = renderScale.y = 1.0; + + // The render window is in pixel coordinates + // ie: render scale and a PAR of not 1 + OfxRectI renderWindow; + renderWindow.x1 = renderWindow.y1 = 0; + renderWindow.x2 = ticket_->property("size").value().width(); + renderWindow.y2 = ticket_->property("size").value().height(); + + /// RoI is in canonical coords, + OfxRectD regionOfInterest; + regionOfInterest.x1 = regionOfInterest.y1 = 0; + regionOfInterest.x2 = renderWindow.x2 * instance->getProjectPixelAspectRatio(); + regionOfInterest.y2 = renderWindow.y2 * instance->getProjectPixelAspectRatio(); + + OfxRectD regionOfDefinition; + regionOfDefinition.x1 = regionOfDefinition.y1 = 0; + regionOfDefinition.x2 = params.width(); + regionOfDefinition.y2 = params.height(); + + + int numFramesToRender=ticket_->property("time").value().toDouble() + * params.frame_rate().toDouble(); + stat = instance->beginRenderAction(0, numFramesToRender, 1.0, false, renderScale, /*sequential=*/true, /*interactive=*/false + ); + if (stat != kOfxStatOK && stat != kOfxStatReplyDefault) { + ticket_->Finish(); + return; + } + + plugin::OliveClipInstance *clip=dynamic_cast(instance->getClip("Output")); + for(int t = 0; t <= numFramesToRender; ++t) + { + // call get region of interest on each of the inputs + OfxTime frame = t; + + // get the RoI for each input clip + // the regions of interest for each input clip are returned in a std::map + // on a real host, these will be the regions of each input clip that the + // effect needs to render a given frame (clipped to the RoD). + // + // In our example we are doing full frame fetches regardless. + std::map rois; + stat = instance->getRegionOfInterestAction(frame, renderScale, + regionOfInterest, rois); + assert(stat == kOfxStatOK || stat == kOfxStatReplyDefault); + + // render a frame + stat = instance->renderAction(t,kOfxImageFieldBoth,renderWindow, renderScale, /*sequential=*/true, /*interactive=*/false, /*draft=*/false); + assert(stat == kOfxStatOK); + + // get the output image buffer + OFX::Host::ImageEffect::Image *outputImage = clip->getOutputImage(); + + std::ostringstream ss; + ss << "Output." << t << ".ppm"; + exportToPPM(ss.str(), outputImage); + } + + instance->endRenderAction(0, numFramesToRender, 1.0, false, renderScale, /*sequential=*/true, /*interactive=*/false + ); + } + switch (type) { case RenderManager::kTypeVideo: { rational time = ticket_->property("time").value(); From 8a7b6cf8690e16d8a1958a2a943318cb7930134f Mon Sep 17 00:00:00 2001 From: Mike Solar Date: Sun, 9 Nov 2025 17:03:56 +0800 Subject: [PATCH 3/4] Add OFX plugin support and related infrastructure This commit extends the OFX plugin support in Olive by: - Initializing and scanning for OFX plugins - Updating PluginNode to handle OFX plugin parameters and inputs - Adding necessary methods and properties for OFX plugin integration - Enhancing NodeFactory to include OFX plugin nodes - Refactoring and renaming OliveInstance to OlivePluginInstance - Introducing new classes for parameter handling (ParamInstance) - Adding PluginJob for rendering OFX plugins - Adjusting CMakeLists.txt files to include new source files --- app/CMakeLists.txt | 2 + app/common/Current.h | 24 +++ app/node/factory.cpp | 16 ++ app/node/node.h | 6 +- app/node/plugins/Plugin.cpp | 95 ++++++++--- app/node/plugins/Plugin.h | 56 +++++-- app/node/traverser.cpp | 4 + app/node/value.h | 8 +- app/pluginSupport/CMakeLists.txt | 8 +- app/pluginSupport/OliveHost.cpp | 17 +- app/pluginSupport/OliveHost.h | 5 +- ...veInstance.cpp => OlivePluginInstance.cpp} | 29 ++-- ...{OliveInstance.h => OlivePluginInstance.h} | 12 +- app/pluginSupport/paraminstance.cpp | 22 +++ app/pluginSupport/paraminstance.h | 149 ++++++++++++++++++ app/render/job/CMakeLists.txt | 4 +- app/render/job/acceleratedjob.h | 14 +- app/render/job/pluginjob.cpp | 25 +++ app/render/job/pluginjob.h | 50 ++++++ app/render/plugin/CMakeLists.txt | 1 + app/render/plugin/pluginrenderer.cpp | 33 ++++ app/render/plugin/pluginrenderer.h | 46 ++++++ app/render/renderprocessor.cpp | 9 +- 23 files changed, 545 insertions(+), 90 deletions(-) rename app/pluginSupport/{OliveInstance.cpp => OlivePluginInstance.cpp} (79%) rename app/pluginSupport/{OliveInstance.h => OlivePluginInstance.h} (96%) create mode 100644 app/pluginSupport/paraminstance.cpp create mode 100644 app/pluginSupport/paraminstance.h create mode 100644 app/render/job/pluginjob.cpp create mode 100644 app/render/job/pluginjob.h create mode 100644 app/render/plugin/CMakeLists.txt create mode 100644 app/render/plugin/pluginrenderer.cpp create mode 100644 app/render/plugin/pluginrenderer.h diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 37ccd41ed..f3a57ec33 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -55,6 +55,8 @@ configure_file(ts/translations.qrc.in ts/translations.qrc @ONLY) set(OLIVE_RESOURCES ${OLIVE_RESOURCES} ${CMAKE_CURRENT_BINARY_DIR}/ts/translations.qrc + render/job/pluginjob.cpp + render/job/pluginjob.h ) # Add version object diff --git a/app/common/Current.h b/app/common/Current.h index aee702922..44487a2c0 100644 --- a/app/common/Current.h +++ b/app/common/Current.h @@ -19,7 +19,9 @@ #ifndef CURRENT_H #define CURRENT_H +#include "pluginSupport/OliveHost.h" #include "render/videoparams.h" +#include "render/job/pluginjob.h" class Current { public: @@ -55,10 +57,32 @@ public: { return true; } + + std::shared_ptr pluginHost() + { + return myHost; + } + + void setPluginHost(std::shared_ptr host) + { + myHost = host; + } + + std::shared_ptr pluginCache() + { + return plugin_cache_; + } + + void setPluginCache(std::shared_ptr cache) + { + plugin_cache_ = cache; + } private: static Current current; olive::VideoParams currentVideoParams_; olive::AudioParams currentAudioParams_; + std::shared_ptr myHost; + std::shared_ptr plugin_cache_; }; diff --git a/app/node/factory.cpp b/app/node/factory.cpp index 9529137ab..5d0400bba 100644 --- a/app/node/factory.cpp +++ b/app/node/factory.cpp @@ -29,6 +29,7 @@ #include "block/transition/diptocolor/diptocolortransition.h" #include "color/displaytransform/displaytransform.h" #include "color/ociogradingtransformlinear/ociogradingtransformlinear.h" +#include "common/Current.h" #include "distort/cornerpin/cornerpindistortnode.h" #include "distort/crop/cropdistortnode.h" #include "distort/flip/flipdistortnode.h" @@ -62,6 +63,8 @@ #include "math/trigonometry/trigonometry.h" #include "output/track/track.h" #include "output/viewer/viewer.h" +#include "pluginSupport/OliveHost.h" +#include "plugins/Plugin.h" #include "project/folder/folder.h" #include "project/footage/footage.h" #include "project/sequence/sequence.h" @@ -84,6 +87,19 @@ void NodeFactory::Initialize() library_.append(created_node); } + + std::shared_ptr host=std::make_shared(); + Current::getInstance().setPluginHost(host); + std::shared_ptr plugin_cache=std::make_shared(*host); + plugin_cache->registerInCache(*OFX::Host::PluginCache::getPluginCache()); + OFX::Host::PluginCache::getPluginCache()->scanPluginFiles(); + + + for (auto plugin : OFX::Host::PluginCache::getPluginCache()->getPlugins()) { + plugin::PluginNode *plugin_node=new plugin::PluginNode(dynamic_cast(plugin)); + library_.append(plugin_node); + } + } void NodeFactory::Destroy() diff --git a/app/node/node.h b/app/node/node.h index b2f05d95f..f73de0ee2 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -19,6 +19,8 @@ #ifndef NODE_H #define NODE_H +#include "ofxhImageEffectAPI.h" + #include #include #include @@ -1124,11 +1126,11 @@ public: static const QString kEnabledInput; - std::shared_ptr getPlugin(); + OFX::Host::ImageEffect::ImageEffectPlugin* getPlugin(); protected: // If is set, this is a plugin node. - std::shared_ptr plugin; + OFX::Host::ImageEffect::ImageEffectPlugin* plugin= nullptr; void InsertInput(const QString &id, NodeValue::Type type, const QVariant &default_value, InputFlags flags, diff --git a/app/node/plugins/Plugin.cpp b/app/node/plugins/Plugin.cpp index f1b9b68b8..af663eb71 100644 --- a/app/node/plugins/Plugin.cpp +++ b/app/node/plugins/Plugin.cpp @@ -17,44 +17,89 @@ */ #include "Plugin.h" + +#include "render/rendermanager.h" +#include "render/job/pluginjob.h" +olive::plugin::PluginNode::PluginNode( + OFX::Host::ImageEffect::ImageEffectPlugin *plugin) +{ + this->plugin = plugin; + auto params = plugin->getDescriptor().getParams(); + + for (auto param: params) { + + NodeValue::Type type; + + const std::string &ofxType = param.second->getType(); + if (ofxType == kOfxParamTypeInteger) { + type = NodeValue::kInt; + } else if (ofxType == kOfxParamTypeDouble) { + type = NodeValue::kFloat; + } else if (ofxType == kOfxParamTypeBoolean) { + type = NodeValue::kBoolean; + } else if (ofxType == kOfxParamTypeString) { + type = NodeValue::kText; + } else if (ofxType == kOfxParamTypeRGB || + ofxType == kOfxParamTypeRGBA) { + type = NodeValue::kColor; + } else if (ofxType == kOfxParamTypeChoice) { + type = NodeValue::kCombo; + } else if (ofxType == kOfxParamTypeDouble2D || + ofxType == kOfxParamTypeInteger2D){ + type = NodeValue::kVec2;} + else if (ofxType == kOfxParamTypeDouble3D || + ofxType == kOfxParamTypeInteger3D){ + type = NodeValue::kVec3; + } else if (ofxType == kOfxParamTypeStrChoice){ + type = NodeValue::kStrCombo; + }else if (ofxType == kOfxParamTypeBytes + || ofxType == kOfxParamTypeCustom) { + type = NodeValue::kBinary; + } else { + type = NodeValue::kNone; + } + + if (ofxType == kOfxParamTypePushButton) { + // TODO + } else if (ofxType == kOfxParamTypeGroup) { + // TODO + } else if (ofxType == kOfxParamTypePage) { + // TODO + } + + AddInput(param.second->getName().data(), type); + } + +} QString olive::plugin::PluginNode::Name() const { - if (methods.name) { - char buffer[64]={0}; - bool ok=methods.name(buffer, 64); - if (ok) { - return QString(buffer); - } - } - return ""; + return plugin->getDescriptor() + .getProps() + .getStringProperty(kOfxPropLabel) + .data(); + } QString olive::plugin::PluginNode::Description() const { - if (methods.description) { - char buffer[1024] = { 0 }; - bool ok = methods.description(buffer, 1024); - if (ok) { - return QString(buffer); - } - } - return ""; + return plugin->getDescriptor().getProps().getStringProperty(kOfxPropPluginDescription).data(); + } void olive::plugin::PluginNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const { - methods.value(&node_ctx_functions); + TexturePtr tex = value[kTextureInput].toTexture(); + if (tex) { + PluginJob job(,value); + table->Push(NodeValue::kTexture, tex->toJob(job), this); + } + + + } QString olive::plugin::PluginNode::id() const { - if (methods.id) { - char buffer[64]={0}; - bool ok=methods.id(buffer, 64); - if (ok) { - return QString(buffer); - } - } - return ""; + return plugin->getIdentifier().data(); } \ No newline at end of file diff --git a/app/node/plugins/Plugin.h b/app/node/plugins/Plugin.h index 5e69358dc..b2ab6b5fd 100644 --- a/app/node/plugins/Plugin.h +++ b/app/node/plugins/Plugin.h @@ -18,36 +18,60 @@ #ifndef PLUGIN_NODES_H #define PLUGIN_NODES_H +#include "ofxhImageEffectAPI.h" +#include "ofxhPluginCache.h" #include "node/node.h" -#include "pluginSupport/Plugin.h" -#include "pluginSupport/OlivePlugin.cpp" + namespace olive { namespace plugin { +const QString kTextureInput = QStringLiteral("tex_in"); class PluginNode : public olive::Node{ public: - PluginNode():Node() - { - memset(&methods, 0, sizeof(olive_node_methods)); - }; - explicit PluginNode(struct olive_node_methods methods):Node() - { - this->methods=methods; - } - void setMethods(struct olive_node_methods methods) - { - this->methods=methods; - } + PluginNode(OFX::Host::ImageEffect::ImageEffectPlugin* plugin) ; + + QString Name() const override; QString id() const override; QVector Category() const override; QString Description() const override; + + void AddPushButton(); + void AddPage(); + Node *copy() const override; + /** + * @brief The main processing function + * + * The node's main purpose is to take values from inputs to set values in outputs. For whatever subclass node you + * create, this is where the code for that goes. + * + * Note that as a video editor, the node graph has to work across time. Depending on the purpose of your node, it may + * output different values depending on the time, and even if not, it will likely be receiving different input + * depending on the time. Most of the difficult work here is handled by NodeInput::get_value() which you should pass + * the `time` parameter to. It will return its value (at that time, if it's keyframed), or pass the time to a + * corresponding output if it's connected to one. If your node doesn't directly deal with time, the default behavior + * of the NodeParam objects will handle everything related to it automatically. + */ void Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const override; -private: - struct olive_node_methods methods; + + /** + * @brief If Value() pushes a ShaderJob, this is the function that will process them. + */ + virtual void ProcessSamples(const NodeValueRow &values, + const SampleBuffer &input, SampleBuffer &output, + int index) const; + + /** + * @brief If Value() pushes a GenerateJob, override this function for the image to create + * + * @param frame + * + * The destination buffer. It will already be allocated and ready for writing to. + */ + virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const; }; diff --git a/app/node/traverser.cpp b/app/node/traverser.cpp index 401446c85..f31aba4c8 100644 --- a/app/node/traverser.cpp +++ b/app/node/traverser.cpp @@ -387,6 +387,10 @@ QVector2D NodeTraverser::GenerateResolution() const video_params_.height()); } +/** + * Resolve Jobs. I need to add a PluginJob here and move the plugin code here. + * @param val + */ void NodeTraverser::ResolveJobs(NodeValue &val) { if (val.type() == NodeValue::kTexture) { diff --git a/app/node/value.h b/app/node/value.h index 590d5638d..43f370bf4 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -163,7 +163,13 @@ public: * Resolves to `int` - the index currently selected */ kCombo, - + /** + * ComboBox type + * + * Resolves to `QString` - the text of choice currently selected + * This is to support the OpenFX type kOfxParamTypeStrChoice + */ + kStrCombo, /** * Video Parameters type * diff --git a/app/pluginSupport/CMakeLists.txt b/app/pluginSupport/CMakeLists.txt index 19b62b0f8..23adfb8ad 100644 --- a/app/pluginSupport/CMakeLists.txt +++ b/app/pluginSupport/CMakeLists.txt @@ -1,6 +1,10 @@ target_sources(libolive-editor PRIVATE OliveHost.h OliveHost.cpp - OliveInstance.h - OliveInstance.cpp + OlivePluginInstance.h + OlivePluginInstance.cpp + OliveClip.cpp + OliveClip.h + paraminstance.cpp + paraminstance.h ) \ No newline at end of file diff --git a/app/pluginSupport/OliveHost.cpp b/app/pluginSupport/OliveHost.cpp index d0a0e1355..c0a3c95c9 100644 --- a/app/pluginSupport/OliveHost.cpp +++ b/app/pluginSupport/OliveHost.cpp @@ -26,22 +26,15 @@ #include #include "OliveHost.h" -#include "OliveInstance.h" +#include "OlivePluginInstance.h" #include "common/Current.h" using namespace OFX::Host; using namespace olive::plugin; -void loadPlugins(QString path) + +void olive::plugin::loadPlugins(QString path) { - QDir dir=(QCoreApplication::applicationDirPath()+path); - QFileInfoList files=dir.entryInfoList(); - for (auto& file:files) { - if (file.isDir()) { - OFX::Binary binary(file.absoluteFilePath().toStdString()); - - } - } + OFX::Host::ImageEffect::PluginCache imageEffectPluginCache(myHost); } - OliveHost::~OliveHost() { for(auto& descriptor:descriptors_){ @@ -82,7 +75,7 @@ OFX::Host::ImageEffect::Instance* OliveHost::newInstance(void* clientData, OFX::Host::ImageEffect::ImageEffectPlugin* plugin, OFX::Host::ImageEffect::Descriptor& desc, const std::string& context){ - ImageEffect::Instance* instance=new OliveInstance(plugin,desc,context,Current::getInstance().interactive()); + ImageEffect::Instance* instance=new OlivePluginInstance(plugin,desc,context,Current::getInstance().interactive()); instances_.append(instance); return instance; }; diff --git a/app/pluginSupport/OliveHost.h b/app/pluginSupport/OliveHost.h index f0d39d633..4fb5f020a 100644 --- a/app/pluginSupport/OliveHost.h +++ b/app/pluginSupport/OliveHost.h @@ -15,6 +15,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#ifndef OLIVE_HOST_H +#define OLIVE_HOST_H #include "ofxhHost.h" #include "ofxhImageEffectAPI.h" #include "ofxCore.h" @@ -66,4 +68,5 @@ private: QList instances_; }; } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/app/pluginSupport/OliveInstance.cpp b/app/pluginSupport/OlivePluginInstance.cpp similarity index 79% rename from app/pluginSupport/OliveInstance.cpp rename to app/pluginSupport/OlivePluginInstance.cpp index f653aeeaf..f39110443 100644 --- a/app/pluginSupport/OliveInstance.cpp +++ b/app/pluginSupport/OlivePluginInstance.cpp @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "OliveInstance.h" +#include "OlivePluginInstance.h" #include "OliveClip.h" #include "ofxCore.h" @@ -28,11 +28,12 @@ #include #include #include +#include "paraminstance.h" namespace olive { namespace plugin { -OfxStatus OliveInstance::vmessage(const char *type, const char *id, +OfxStatus OlivePluginInstance::vmessage(const char *type, const char *id, const char *format, va_list args) { char *buffer = new char[1024]; @@ -59,7 +60,7 @@ OfxStatus OliveInstance::vmessage(const char *type, const char *id, return kOfxStatOK; } } -OfxStatus OliveInstance::setPersistentMessage(const char *type, const char *id, +OfxStatus OlivePluginInstance::setPersistentMessage(const char *type, const char *id, const char *format, va_list args) { char *buffer = new char[1024]; @@ -95,66 +96,66 @@ OfxStatus OliveInstance::setPersistentMessage(const char *type, const char *id, } return kOfxStatOK; } -OfxStatus OliveInstance::clearPersistentMessage() +OfxStatus OlivePluginInstance::clearPersistentMessage() { persistentErrors_.clear(); // TODO: tell the shell to remove message. return kOfxStatOK; } -void OliveInstance::getProjectSize(double &xSize, double &ySize) const +void OlivePluginInstance::getProjectSize(double &xSize, double &ySize) const { xSize =params_.width(); ySize =params_.height(); } -void OliveInstance::getProjectOffset(double &xOffset, double &yOffset) const +void OlivePluginInstance::getProjectOffset(double &xOffset, double &yOffset) const { xOffset =params_.x(); yOffset =params_.y(); } -void OliveInstance::getProjectExtent(double &xSize, double &ySize) const +void OlivePluginInstance::getProjectExtent(double &xSize, double &ySize) const { xSize =params_.width(); ySize =params_.height(); // TODO: Ensure this project does not support this. } -double OliveInstance::getProjectPixelAspectRatio() const +double OlivePluginInstance::getProjectPixelAspectRatio() const { return Current::getInstance() .currentVideoParams() .pixel_aspect_ratio() .toDouble(); } -double OliveInstance::getFrameRate() const +double OlivePluginInstance::getFrameRate() const { return params_.frame_rate().toDouble(); } -double OliveInstance::getEffectDuration() const +double OlivePluginInstance::getEffectDuration() const { // Return a default duration value return 100.0; } -double OliveInstance::getFrameRecursive() const +double OlivePluginInstance::getFrameRecursive() const { // Return current frame (this would typically be set by the host during rendering) return 0.0; } -void OliveInstance::getRenderScaleRecursive(double &x, double &y) const +void OlivePluginInstance::getRenderScaleRecursive(double &x, double &y) const { // Return default render scale (1.0, 1.0) x = 1.0; y = 1.0; } OFX::Host::Param::Instance * -OliveInstance::newParam(const std::string &name, +OlivePluginInstance::newParam(const std::string &name, OFX::Host::Param::Descriptor &Descriptor) { } -OFX::Host::ImageEffect::ClipInstance *OliveInstance::newClipInstance( +OFX::Host::ImageEffect::ClipInstance *OlivePluginInstance::newClipInstance( OFX::Host::ImageEffect::Instance *plugin, OFX::Host::ImageEffect::ClipDescriptor *descriptor, int index) diff --git a/app/pluginSupport/OliveInstance.h b/app/pluginSupport/OlivePluginInstance.h similarity index 96% rename from app/pluginSupport/OliveInstance.h rename to app/pluginSupport/OlivePluginInstance.h index 5e2e0178d..db59e04c6 100644 --- a/app/pluginSupport/OliveInstance.h +++ b/app/pluginSupport/OlivePluginInstance.h @@ -15,7 +15,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - +#ifndef OLIVE_INSTANCE_H +#define OLIVE_INSTANCE_H #include "ofxCore.h" #include "ofxImageEffect.h" #include @@ -36,9 +37,9 @@ struct PersistentErrors{ ErrorType type; QString message; }; -class OliveInstance : public OFX::Host::ImageEffect::Instance { +class OlivePluginInstance : public OFX::Host::ImageEffect::Instance { public: - OliveInstance( + OlivePluginInstance( OFX::Host::ImageEffect::ImageEffectPlugin* plugin, OFX::Host::ImageEffect::Descriptor& desc, const std::string& context, @@ -46,7 +47,7 @@ public: : OFX::Host::ImageEffect::Instance(plugin, desc, context, interactive) { } - ~OliveInstance() override = default; + ~OlivePluginInstance() override = default; const std::string &getDefaultOutputFielding() const{ return kOfxImageFieldNone; }; @@ -151,4 +152,5 @@ private: VideoParams params_; }; } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/app/pluginSupport/paraminstance.cpp b/app/pluginSupport/paraminstance.cpp new file mode 100644 index 000000000..63b594e04 --- /dev/null +++ b/app/pluginSupport/paraminstance.cpp @@ -0,0 +1,22 @@ +/* + * Olive Community Edition - Non-Linear Video Editor + * Copyright (C) 2025 Olive CE Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +#include "paraminstance.h" + diff --git a/app/pluginSupport/paraminstance.h b/app/pluginSupport/paraminstance.h new file mode 100644 index 000000000..9a05ecaa5 --- /dev/null +++ b/app/pluginSupport/paraminstance.h @@ -0,0 +1,149 @@ +/* + * Olive Community Edition - Non-Linear Video Editor + * Copyright (C) 2025 Olive CE Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +// Copyright OpenFX and contributors to the OpenFX project. +#ifndef PARAM_INSTANCE_H +#define PARAM_INSTANCE_H + +#include "OlivePluginInstance.h" +#include "ofxhParam.h" +namespace olive +{ +namespace plugin +{ +class PushbuttonInstance : public OFX::Host::Param::PushbuttonInstance { +protected: + OlivePluginInstance* _effect; + OFX::Host::Param::Descriptor *_descriptor; +public: + PushbuttonInstance(OlivePluginInstance *effect, const std::string &name, + OFX::Host::Param::Descriptor &descriptor) + : OFX::Host::Param::PushbuttonInstance( descriptor) + { + _descriptor = &descriptor; + }; +}; + +class IntegerInstance : public OFX::Host::Param::IntegerInstance { +protected: + OlivePluginInstance* _effect; + OFX::Host::Param::Descriptor& _descriptor; +public: + IntegerInstance(OlivePluginInstance* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); + OfxStatus get(int&); + OfxStatus get(OfxTime time, int&); + OfxStatus set(int); + OfxStatus set(OfxTime time, int); +}; + +class DoubleInstance : public OFX::Host::Param::DoubleInstance { +protected: + OlivePluginInstance* _effect; + OFX::Host::Param::Descriptor& _descriptor; +public: + DoubleInstance(OlivePluginInstance* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); + OfxStatus get(double&); + OfxStatus get(OfxTime time, double&); + OfxStatus set(double); + OfxStatus set(OfxTime time, double); + OfxStatus derive(OfxTime time, double&); + OfxStatus integrate(OfxTime time1, OfxTime time2, double&); +}; + +class BooleanInstance : public OFX::Host::Param::BooleanInstance { +protected: + OlivePluginInstance* _effect; + OFX::Host::Param::Descriptor& _descriptor; +public: + BooleanInstance(OlivePluginInstance* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); + OfxStatus get(bool&); + OfxStatus get(OfxTime time, bool&); + OfxStatus set(bool); + OfxStatus set(OfxTime time, bool); +}; + +class ChoiceInstance : public OFX::Host::Param::ChoiceInstance { +protected: + OlivePluginInstance* _effect; + OFX::Host::Param::Descriptor& _descriptor; +public: + ChoiceInstance(OlivePluginInstance* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); + OfxStatus get(int&); + OfxStatus get(OfxTime time, int&); + OfxStatus set(int); + OfxStatus set(OfxTime time, int); +}; + +class RGBAInstance : public OFX::Host::Param::RGBAInstance { +protected: + OlivePluginInstance* _effect; + OFX::Host::Param::Descriptor& _descriptor; +public: + RGBAInstance(OlivePluginInstance* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); + OfxStatus get(double&,double&,double&,double&); + OfxStatus get(OfxTime time, double&,double&,double&,double&); + OfxStatus set(double,double,double,double); + OfxStatus set(OfxTime time, double,double,double,double); +}; + + +class RGBInstance : public OFX::Host::Param::RGBInstance { +protected: + OlivePluginInstance* _effect; + OFX::Host::Param::Descriptor& _descriptor; +public: + RGBInstance(OlivePluginInstance* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); + OfxStatus get(double&,double&,double&); + OfxStatus get(OfxTime time, double&,double&,double&); + OfxStatus set(double,double,double); + OfxStatus set(OfxTime time, double,double,double); +}; + +class Double2DInstance : public OFX::Host::Param::Double2DInstance { +protected: + OlivePluginInstance* _effect; + OFX::Host::Param::Descriptor& _descriptor; +public: + Double2DInstance(OlivePluginInstance* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); + OfxStatus get(double&,double&); + OfxStatus get(OfxTime time,double&,double&); + OfxStatus set(double,double); + OfxStatus set(OfxTime time,double,double); +}; + +class Integer2DInstance : public OFX::Host::Param::Integer2DInstance { +protected: + OlivePluginInstance* _effect; + OFX::Host::Param::Descriptor& _descriptor; +public: + Integer2DInstance(OlivePluginInstance* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); + OfxStatus get(int&,int&); + OfxStatus get(OfxTime time,int&,int&); + OfxStatus set(int,int); + OfxStatus set(OfxTime time,int,int); +}; +} +} + + + +#endif // HOST_DEMO_PARAM_INSTANCE_H + + +#endif //PARAMINSTANCE_H diff --git a/app/render/job/CMakeLists.txt b/app/render/job/CMakeLists.txt index 6ad233bbe..350257ee0 100644 --- a/app/render/job/CMakeLists.txt +++ b/app/render/job/CMakeLists.txt @@ -22,5 +22,7 @@ set(OLIVE_SOURCES render/job/generatejob.h render/job/samplejob.h render/job/shaderjob.h - PARENT_SCOPE + render/job/pluginjob.h + render/job/pluginjob.cpp + PARENT_SCOPE ) diff --git a/app/render/job/acceleratedjob.h b/app/render/job/acceleratedjob.h index 1724fa201..1a4f4053c 100644 --- a/app/render/job/acceleratedjob.h +++ b/app/render/job/acceleratedjob.h @@ -33,22 +33,22 @@ public: { } - NodeValue Get(const QString &input) const + virtual NodeValue Get(const QString &input) const { return value_map_.value(input); } - void Insert(const QString &input, const NodeValueRow &row) + virtual void Insert(const QString &input, const NodeValueRow &row) { value_map_.insert(input, row.value(input)); } - void Insert(const QString &input, const NodeValue &value) + virtual void Insert(const QString &input, const NodeValue &value) { value_map_.insert(input, value); } - void Insert(const NodeValueRow &row) + virtual void Insert(const NodeValueRow &row) { #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) value_map_.insert(row); @@ -59,16 +59,16 @@ public: #endif } - const NodeValueRow &GetValues() const + virtual const NodeValueRow &GetValues() const { return value_map_; } - NodeValueRow &GetValues() + virtual NodeValueRow &GetValues() { return value_map_; } -private: +protected: NodeValueRow value_map_; }; diff --git a/app/render/job/pluginjob.cpp b/app/render/job/pluginjob.cpp new file mode 100644 index 000000000..67c4e71d2 --- /dev/null +++ b/app/render/job/pluginjob.cpp @@ -0,0 +1,25 @@ +/* + * Olive Community Edition - Non-Linear Video Editor + * Copyright (C) 2025 Olive CE Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "pluginjob.h" + +namespace olive { +namespace plugin { +} // plugin +} // olive \ No newline at end of file diff --git a/app/render/job/pluginjob.h b/app/render/job/pluginjob.h new file mode 100644 index 000000000..84d945469 --- /dev/null +++ b/app/render/job/pluginjob.h @@ -0,0 +1,50 @@ +/* + * Olive Community Edition - Non-Linear Video Editor + * Copyright (C) 2025 Olive CE Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef PLUGINJOB_H +#define PLUGINJOB_H +#include "acceleratedjob.h" +#include "pluginSupport/OlivePluginInstance.h" + +#include + +namespace olive { +namespace plugin { + +class PluginJob :public AcceleratedJob{ +public: + explicit PluginJob(std::shared_ptr pluginInstance, NodeValueRow row): AcceleratedJob() + { + this->pluginInstance = pluginInstance; + + + } + +private: + std::shared_ptr pluginInstance; + + QHash> paramsOnTime; + + QHash params; +}; + +} // plugin +} // olive + +#endif //PLUGINJOB_H diff --git a/app/render/plugin/CMakeLists.txt b/app/render/plugin/CMakeLists.txt new file mode 100644 index 000000000..89d6f2721 --- /dev/null +++ b/app/render/plugin/CMakeLists.txt @@ -0,0 +1 @@ +target_sources(libolive-editor PRIVATE pluginrenderer.cpp pluginrenderer.h) \ No newline at end of file diff --git a/app/render/plugin/pluginrenderer.cpp b/app/render/plugin/pluginrenderer.cpp new file mode 100644 index 000000000..c6b26de69 --- /dev/null +++ b/app/render/plugin/pluginrenderer.cpp @@ -0,0 +1,33 @@ +/* + * Olive Community Edition - Non-Linear Video Editor + * Copyright (C) 2025 Olive CE Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +// +// Created by mikesolar on 25-10-19. +// + +#include "pluginrenderer.h" + +#include "pluginSupport/OlivePluginInstance.h" +void olive::plugin::PluginRenderer::Blit(QVariant shader, ShaderJob job, + Texture *destination, + VideoParams destination_params, + bool clear_destination) +{ + +} \ No newline at end of file diff --git a/app/render/plugin/pluginrenderer.h b/app/render/plugin/pluginrenderer.h new file mode 100644 index 000000000..09e99f846 --- /dev/null +++ b/app/render/plugin/pluginrenderer.h @@ -0,0 +1,46 @@ +/* + * Olive Community Edition - Non-Linear Video Editor + * Copyright (C) 2025 Olive CE Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +// +// Created by mikesolar on 25-10-19. +// + +#ifndef PLUGINRENDERER_H +#define PLUGINRENDERER_H +#include "render/renderer.h" +namespace olive +{ +namespace plugin{ +class PluginRenderer : public olive::Renderer{ + Q_OBJECT +public: + PluginRenderer(QObject *parent=nullptr):Renderer(parent){}; +protected: + void Blit(QVariant shader, + ShaderJob job, + Texture *destination, + VideoParams destination_params, + bool clear_destination) override; +}; +} +} + + + +#endif //PLUGINRENDERER_H diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index bdec1ceed..73f89d487 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -169,7 +169,7 @@ void RenderProcessor::Run() } // if is a plugin - Node *node=ticket_->property("node").value(); + /*Node *node=ticket_->property("node").value(); if (node && node->getPlugin()) { std::shared_ptr plugin = node->getPlugin(); @@ -214,7 +214,7 @@ void RenderProcessor::Run() int numFramesToRender=ticket_->property("time").value().toDouble() * params.frame_rate().toDouble(); - stat = instance->beginRenderAction(0, numFramesToRender, 1.0, false, renderScale, /*sequential=*/true, /*interactive=*/false + stat = instance->beginRenderAction(0, numFramesToRender, 1.0, false, renderScale, /*sequential=#1#true, /*interactive=#1#false ); if (stat != kOfxStatOK && stat != kOfxStatReplyDefault) { ticket_->Finish(); @@ -239,7 +239,7 @@ void RenderProcessor::Run() assert(stat == kOfxStatOK || stat == kOfxStatReplyDefault); // render a frame - stat = instance->renderAction(t,kOfxImageFieldBoth,renderWindow, renderScale, /*sequential=*/true, /*interactive=*/false, /*draft=*/false); + stat = instance->renderAction(t,kOfxImageFieldBoth,renderWindow, renderScale, /*sequential=#1#true, /*interactive=#1#false, /*draft=#1#false); assert(stat == kOfxStatOK); // get the output image buffer @@ -250,9 +250,10 @@ void RenderProcessor::Run() exportToPPM(ss.str(), outputImage); } - instance->endRenderAction(0, numFramesToRender, 1.0, false, renderScale, /*sequential=*/true, /*interactive=*/false + instance->endRenderAction(0, numFramesToRender, 1.0, false, renderScale, /*sequential=#1#true, /*interactive=#1#false ); } + */ switch (type) { case RenderManager::kTypeVideo: { From 5aaa49c515aa45f6128f6ac25e813ce200288ceb Mon Sep 17 00:00:00 2001 From: Mike Solar Date: Mon, 10 Nov 2025 13:57:04 +0800 Subject: [PATCH 4/4] Add push button support for OFX plugins This commit adds support for push buttons in OFX plugins by: - Adding `kPushButton` to `NodeValue` enum - Implementing `pushButtonClicked` slot in `PluginNode` - Creating `NodeParamButton` widget for UI representation - Updating `paraminstance.h` to handle push button instances - Modifying `nodeparamviewwidgetbridge.cpp` to connect push button signals --- app/CMakeLists.txt | 2 + app/node/plugins/Plugin.cpp | 22 ++--- app/node/plugins/Plugin.h | 4 + app/node/value.h | 4 + app/pluginSupport/paraminstance.h | 88 +++++++++++++------ app/render/job/pluginjob.h | 7 +- app/widget/nodeparamview/CMakeLists.txt | 50 ++++++----- app/widget/nodeparamview/nodeparambutton.cpp | 20 +++++ app/widget/nodeparamview/nodeparambutton.h | 47 ++++++++++ .../nodeparamviewwidgetbridge.cpp | 10 +++ 10 files changed, 188 insertions(+), 66 deletions(-) create mode 100644 app/widget/nodeparamview/nodeparambutton.cpp create mode 100644 app/widget/nodeparamview/nodeparambutton.h diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index f3a57ec33..cb36f0ee5 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -57,6 +57,8 @@ set(OLIVE_RESOURCES ${CMAKE_CURRENT_BINARY_DIR}/ts/translations.qrc render/job/pluginjob.cpp render/job/pluginjob.h + widget/nodeparamview/nodeparambutton.cpp + widget/nodeparamview/nodeparambutton.h ) # Add version object diff --git a/app/node/plugins/Plugin.cpp b/app/node/plugins/Plugin.cpp index af663eb71..e74a86aab 100644 --- a/app/node/plugins/Plugin.cpp +++ b/app/node/plugins/Plugin.cpp @@ -53,20 +53,20 @@ olive::plugin::PluginNode::PluginNode( } else if (ofxType == kOfxParamTypeStrChoice){ type = NodeValue::kStrCombo; }else if (ofxType == kOfxParamTypeBytes - || ofxType == kOfxParamTypeCustom) { + || ofxType == kOfxParamTypeCustom) { type = NodeValue::kBinary; - } else { - type = NodeValue::kNone; - } - - if (ofxType == kOfxParamTypePushButton) { - // TODO + } else if (ofxType == kOfxParamTypePushButton) { + type = NodeValue::kPushButton; } else if (ofxType == kOfxParamTypeGroup) { // TODO } else if (ofxType == kOfxParamTypePage) { // TODO + }else { + type = NodeValue::kNone; } + + AddInput(param.second->getName().data(), type); } @@ -91,12 +91,12 @@ void olive::plugin::PluginNode::Value(const NodeValueRow &value, { TexturePtr tex = value[kTextureInput].toTexture(); if (tex) { - PluginJob job(,value); + PluginJob job(plugin, value); table->Push(NodeValue::kTexture, tex->toJob(job), this); } - - - +} +void olive::plugin::PluginNode::pushButtonClicked(QString name) +{ } QString olive::plugin::PluginNode::id() const diff --git a/app/node/plugins/Plugin.h b/app/node/plugins/Plugin.h index b2ab6b5fd..490a9d97f 100644 --- a/app/node/plugins/Plugin.h +++ b/app/node/plugins/Plugin.h @@ -40,6 +40,7 @@ public: void AddPushButton(); void AddPage(); + Node *copy() const override; /** * @brief The main processing function @@ -73,6 +74,9 @@ public: */ virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const; +public slots: + void pushButtonClicked(QString name); + }; } diff --git a/app/node/value.h b/app/node/value.h index 43f370bf4..5f1ff9d7b 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -197,6 +197,10 @@ public: kBinary, /** + *Push Button + */ + kPushButton, + /** * End of list */ kDataTypeCount diff --git a/app/pluginSupport/paraminstance.h b/app/pluginSupport/paraminstance.h index 9a05ecaa5..e7b746dcd 100644 --- a/app/pluginSupport/paraminstance.h +++ b/app/pluginSupport/paraminstance.h @@ -21,20 +21,21 @@ #ifndef PARAM_INSTANCE_H #define PARAM_INSTANCE_H -#include "OlivePluginInstance.h" #include "ofxhParam.h" +#include "node/plugins/Plugin.h" namespace olive { namespace plugin { class PushbuttonInstance : public OFX::Host::Param::PushbuttonInstance { protected: - OlivePluginInstance* _effect; + PluginNode* node; OFX::Host::Param::Descriptor *_descriptor; public: - PushbuttonInstance(OlivePluginInstance *effect, const std::string &name, + PushbuttonInstance(PluginNode *effect, const std::string &name, OFX::Host::Param::Descriptor &descriptor) - : OFX::Host::Param::PushbuttonInstance( descriptor) + : OFX::Host::Param::PushbuttonInstance(descriptor) + , node(effect) { _descriptor = &descriptor; }; @@ -42,22 +43,58 @@ public: class IntegerInstance : public OFX::Host::Param::IntegerInstance { protected: - OlivePluginInstance* _effect; + PluginNode* _node; OFX::Host::Param::Descriptor& _descriptor; + QString id; public: - IntegerInstance(OlivePluginInstance* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); - OfxStatus get(int&); - OfxStatus get(OfxTime time, int&); - OfxStatus set(int); - OfxStatus set(OfxTime time, int); + IntegerInstance(PluginNode *node, OFX::Host::Param::Descriptor &descriptor) + : _node(node), _descriptor(descriptor), + OFX::Host::Param::IntegerInstance(_descriptor){} + OfxStatus get(int &a) + { + if (id.isEmpty()) { + return kOfxStatErrBadHandle; + } + QVariant variant=_node->GetStandardValue(id); + + if (variant.typeId()==QVariant::Int) { + a=variant.toInt(); + return kOfxStatOK; + } + a=0; + return kOfxStatErrValue; + } + OfxStatus get(OfxTime time, int &data) + { + if (id.isEmpty()) { + return kOfxStatErrBadHandle; + } + QVariant variant=_node->GetValueAtTime(id, rational::fromDouble(time)); + if (variant.typeId()==QVariant::Int) { + data=variant.toInt(); + return kOfxStatOK; + } + data=0; + return kOfxStatErrValue; + } + OfxStatus set(int data) + { + _node->SetStandardValue(_descriptor.getName().c_str(), data); + id=_descriptor.getName().c_str(); + return kOfxStatOK; + } + OfxStatus set(OfxTime time, int) + { + _node->SetValueAtTime() + } }; class DoubleInstance : public OFX::Host::Param::DoubleInstance { protected: - OlivePluginInstance* _effect; + PluginNode* node; OFX::Host::Param::Descriptor& _descriptor; public: - DoubleInstance(OlivePluginInstance* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); + DoubleInstance(PluginNode* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); OfxStatus get(double&); OfxStatus get(OfxTime time, double&); OfxStatus set(double); @@ -68,10 +105,10 @@ public: class BooleanInstance : public OFX::Host::Param::BooleanInstance { protected: - OlivePluginInstance* _effect; + PluginNode* node; OFX::Host::Param::Descriptor& _descriptor; public: - BooleanInstance(OlivePluginInstance* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); + BooleanInstance(PluginNode* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); OfxStatus get(bool&); OfxStatus get(OfxTime time, bool&); OfxStatus set(bool); @@ -80,10 +117,10 @@ public: class ChoiceInstance : public OFX::Host::Param::ChoiceInstance { protected: - OlivePluginInstance* _effect; + PluginNode* node; OFX::Host::Param::Descriptor& _descriptor; public: - ChoiceInstance(OlivePluginInstance* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); + ChoiceInstance(PluginNode* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); OfxStatus get(int&); OfxStatus get(OfxTime time, int&); OfxStatus set(int); @@ -92,10 +129,10 @@ public: class RGBAInstance : public OFX::Host::Param::RGBAInstance { protected: - OlivePluginInstance* _effect; + PluginNode* node; OFX::Host::Param::Descriptor& _descriptor; public: - RGBAInstance(OlivePluginInstance* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); + RGBAInstance(PluginNode* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); OfxStatus get(double&,double&,double&,double&); OfxStatus get(OfxTime time, double&,double&,double&,double&); OfxStatus set(double,double,double,double); @@ -105,10 +142,10 @@ public: class RGBInstance : public OFX::Host::Param::RGBInstance { protected: - OlivePluginInstance* _effect; + PluginNode* node; OFX::Host::Param::Descriptor& _descriptor; public: - RGBInstance(OlivePluginInstance* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); + RGBInstance(PluginNode* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); OfxStatus get(double&,double&,double&); OfxStatus get(OfxTime time, double&,double&,double&); OfxStatus set(double,double,double); @@ -117,10 +154,10 @@ public: class Double2DInstance : public OFX::Host::Param::Double2DInstance { protected: - OlivePluginInstance* _effect; + PluginNode* node; OFX::Host::Param::Descriptor& _descriptor; public: - Double2DInstance(OlivePluginInstance* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); + Double2DInstance(PluginNode* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); OfxStatus get(double&,double&); OfxStatus get(OfxTime time,double&,double&); OfxStatus set(double,double); @@ -129,10 +166,10 @@ public: class Integer2DInstance : public OFX::Host::Param::Integer2DInstance { protected: - OlivePluginInstance* _effect; + PluginNode* node; OFX::Host::Param::Descriptor& _descriptor; public: - Integer2DInstance(OlivePluginInstance* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); + Integer2DInstance(PluginNode* effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor); OfxStatus get(int&,int&); OfxStatus get(OfxTime time,int&,int&); OfxStatus set(int,int); @@ -144,6 +181,3 @@ public: #endif // HOST_DEMO_PARAM_INSTANCE_H - - -#endif //PARAMINSTANCE_H diff --git a/app/render/job/pluginjob.h b/app/render/job/pluginjob.h index 84d945469..757d0cfce 100644 --- a/app/render/job/pluginjob.h +++ b/app/render/job/pluginjob.h @@ -23,21 +23,20 @@ #include "pluginSupport/OlivePluginInstance.h" #include +#include namespace olive { namespace plugin { class PluginJob :public AcceleratedJob{ public: - explicit PluginJob(std::shared_ptr pluginInstance, NodeValueRow row): AcceleratedJob() + explicit PluginJob(OFX::Host::ImageEffect::ImageEffectPlugin* pluginInstance, NodeValueRow row): AcceleratedJob() { this->pluginInstance = pluginInstance; - - } private: - std::shared_ptr pluginInstance; + OFX::Host::ImageEffect::ImageEffectPlugin *pluginInstance=nullptr; QHash> paramsOnTime; diff --git a/app/widget/nodeparamview/CMakeLists.txt b/app/widget/nodeparamview/CMakeLists.txt index 1b80edebc..4f7cf658e 100644 --- a/app/widget/nodeparamview/CMakeLists.txt +++ b/app/widget/nodeparamview/CMakeLists.txt @@ -15,28 +15,30 @@ # along with this program. If not, see . set(OLIVE_SOURCES - ${OLIVE_SOURCES} - widget/nodeparamview/nodeparamview.cpp - widget/nodeparamview/nodeparamview.h - widget/nodeparamview/nodeparamviewarraywidget.cpp - widget/nodeparamview/nodeparamviewarraywidget.h - widget/nodeparamview/nodeparamviewconnectedlabel.cpp - widget/nodeparamview/nodeparamviewconnectedlabel.h - widget/nodeparamview/nodeparamviewcontext.cpp - widget/nodeparamview/nodeparamviewcontext.h - widget/nodeparamview/nodeparamviewdockarea.cpp - widget/nodeparamview/nodeparamviewdockarea.h - widget/nodeparamview/nodeparamviewitem.cpp - widget/nodeparamview/nodeparamviewitem.h - widget/nodeparamview/nodeparamviewitembase.cpp - widget/nodeparamview/nodeparamviewitembase.h - widget/nodeparamview/nodeparamviewitemtitlebar.cpp - widget/nodeparamview/nodeparamviewitemtitlebar.h - widget/nodeparamview/nodeparamviewkeyframecontrol.cpp - widget/nodeparamview/nodeparamviewkeyframecontrol.h - widget/nodeparamview/nodeparamviewtextedit.cpp - widget/nodeparamview/nodeparamviewtextedit.h - widget/nodeparamview/nodeparamviewwidgetbridge.cpp - widget/nodeparamview/nodeparamviewwidgetbridge.h - PARENT_SCOPE + ${OLIVE_SOURCES} + widget/nodeparamview/nodeparamview.cpp + widget/nodeparamview/nodeparamview.h + widget/nodeparamview/nodeparamviewarraywidget.cpp + widget/nodeparamview/nodeparamviewarraywidget.h + widget/nodeparamview/nodeparamviewconnectedlabel.cpp + widget/nodeparamview/nodeparamviewconnectedlabel.h + widget/nodeparamview/nodeparamviewcontext.cpp + widget/nodeparamview/nodeparamviewcontext.h + widget/nodeparamview/nodeparamviewdockarea.cpp + widget/nodeparamview/nodeparamviewdockarea.h + widget/nodeparamview/nodeparamviewitem.cpp + widget/nodeparamview/nodeparamviewitem.h + widget/nodeparamview/nodeparamviewitembase.cpp + widget/nodeparamview/nodeparamviewitembase.h + widget/nodeparamview/nodeparamviewitemtitlebar.cpp + widget/nodeparamview/nodeparamviewitemtitlebar.h + widget/nodeparamview/nodeparamviewkeyframecontrol.cpp + widget/nodeparamview/nodeparamviewkeyframecontrol.h + widget/nodeparamview/nodeparamviewtextedit.cpp + widget/nodeparamview/nodeparamviewtextedit.h + widget/nodeparamview/nodeparamviewwidgetbridge.cpp + widget/nodeparamview/nodeparamviewwidgetbridge.h + widget/nodeparamview/nodeparambutton.h + widget/nodeparamview/nodeparambutton.cpp + PARENT_SCOPE ) diff --git a/app/widget/nodeparamview/nodeparambutton.cpp b/app/widget/nodeparamview/nodeparambutton.cpp new file mode 100644 index 000000000..2696e6d98 --- /dev/null +++ b/app/widget/nodeparamview/nodeparambutton.cpp @@ -0,0 +1,20 @@ +/* + * Olive Community Edition - Non-Linear Video Editor + * Copyright (C) 2025 Olive CE Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "nodeparambutton.h" diff --git a/app/widget/nodeparamview/nodeparambutton.h b/app/widget/nodeparamview/nodeparambutton.h new file mode 100644 index 000000000..cb06ce7f1 --- /dev/null +++ b/app/widget/nodeparamview/nodeparambutton.h @@ -0,0 +1,47 @@ +/* + * Olive Community Edition - Non-Linear Video Editor + * Copyright (C) 2025 Olive CE Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef NODEPARAMBUTTON_H +#define NODEPARAMBUTTON_H +#include "node/plugins/Plugin.h" + +#include + +class NodeParamButton : public QPushButton{ +Q_OBJECT +public: + NodeParamButton(QString name, QWidget *parent = nullptr):QPushButton(parent) + { + this->name_=name; + connect(this, &QPushButton::clicked, this, &NodeParamButton::pressed); + } +signals: + void onPressed(QString name); +private slots: + void pressed(){ + emit onPressed(name_); + } +private: + QString name_; + +}; + + + +#endif //NODEPARAMBUTTON_H diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index a9236698c..9f8eb4399 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -26,6 +26,7 @@ #include "common/qtutils.h" #include "core.h" +#include "nodeparambutton.h" #include "node/group/group.h" #include "node/node.h" #include "node/nodeundo.h" @@ -40,6 +41,8 @@ #include "widget/slider/integerslider.h" #include "widget/slider/rationalslider.h" +#include + namespace olive { @@ -179,6 +182,13 @@ void NodeParamViewWidgetBridge::CreateWidgets() &NodeParamViewWidgetBridge::WidgetCallback); break; } + case NodeValue::kPushButton: { + NodeInput input=GetInnerInput(); + NodeParamButton *button=new NodeParamButton(input.name(),parent); + widgets_.append(button); + plugin::PluginNode* plugin_node=dynamic_cast(input.node()); + connect(button, &NodeParamButton::onPressed, plugin_node, &plugin::PluginNode::pushButtonClicked); + } } // Check all properties