diff --git a/data/CompOver.otio b/data/CompOver.otio index a65ea3f..3d96c23 100644 --- a/data/CompOver.otio +++ b/data/CompOver.otio @@ -17,7 +17,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 72 + "value": 30 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -32,7 +32,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 72 + "value": 30 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -58,7 +58,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -73,7 +73,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -92,7 +92,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -107,7 +107,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -126,7 +126,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -141,7 +141,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", diff --git a/data/Counter.0.png b/data/Counter.0.png new file mode 100644 index 0000000..4aed6e7 Binary files /dev/null and b/data/Counter.0.png differ diff --git a/data/Counter.1.png b/data/Counter.1.png new file mode 100644 index 0000000..429bcc9 Binary files /dev/null and b/data/Counter.1.png differ diff --git a/data/Counter.2.png b/data/Counter.2.png new file mode 100644 index 0000000..b9b5747 Binary files /dev/null and b/data/Counter.2.png differ diff --git a/data/Counter.3.png b/data/Counter.3.png new file mode 100644 index 0000000..fa0d7af Binary files /dev/null and b/data/Counter.3.png differ diff --git a/data/Counter.4.png b/data/Counter.4.png new file mode 100644 index 0000000..d4b17f9 Binary files /dev/null and b/data/Counter.4.png differ diff --git a/data/Counter.5.png b/data/Counter.5.png new file mode 100644 index 0000000..2830099 Binary files /dev/null and b/data/Counter.5.png differ diff --git a/data/Counter.6.png b/data/Counter.6.png new file mode 100644 index 0000000..ebf6ef7 Binary files /dev/null and b/data/Counter.6.png differ diff --git a/data/Counter.7.png b/data/Counter.7.png new file mode 100644 index 0000000..17494d8 Binary files /dev/null and b/data/Counter.7.png differ diff --git a/data/Counter.8.png b/data/Counter.8.png new file mode 100644 index 0000000..574fa09 Binary files /dev/null and b/data/Counter.8.png differ diff --git a/data/Counter.9.png b/data/Counter.9.png new file mode 100644 index 0000000..ef17e9e Binary files /dev/null and b/data/Counter.9.png differ diff --git a/data/Counter.svg b/data/Counter.svg new file mode 100644 index 0000000..6661e88 --- /dev/null +++ b/data/Counter.svg @@ -0,0 +1,72 @@ + + + + + + + + + + 0 + + diff --git a/data/Filters.otio b/data/Filters.otio index 02cfb4f..15b9213 100644 --- a/data/Filters.otio +++ b/data/Filters.otio @@ -17,7 +17,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -38,7 +38,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -57,7 +57,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -79,7 +79,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", diff --git a/data/Gap.otio b/data/Gap.otio index 6d3d40e..32ad617 100644 --- a/data/Gap.otio +++ b/data/Gap.otio @@ -17,7 +17,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -32,7 +32,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -50,7 +50,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -68,7 +68,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -83,7 +83,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", diff --git a/data/Patterns.otio b/data/Patterns.otio index 225409d..5a5682e 100644 --- a/data/Patterns.otio +++ b/data/Patterns.otio @@ -17,7 +17,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -44,7 +44,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -63,7 +63,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -96,7 +96,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -115,7 +115,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -143,7 +143,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", diff --git a/data/Sequence.otio b/data/Sequence.otio new file mode 100644 index 0000000..ce880a5 --- /dev/null +++ b/data/Sequence.otio @@ -0,0 +1,100 @@ +{ + "OTIO_SCHEMA": "Timeline.1", + "metadata": {}, + "name": "SimpleOver", + "tracks": { + "OTIO_SCHEMA": "Stack.1", + "children": [ + { + "OTIO_SCHEMA": "Track.1", + "children": [ + { + "OTIO_SCHEMA": "Clip.1", + "media_reference": { + "OTIO_SCHEMA": "ImageSequenceReference.1", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 10 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 0 + } + }, + "target_url_base": "", + "name_prefix": "Counter.", + "name_suffix": ".png", + "start_frame": 0, + "frame_step": 1, + "rate": 0.0, + "frame_zero_padding": 0, + "missing_frame_policy": "black" + }, + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 10 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 0 + } + }, + "name": "Sequence" + }, + { + "OTIO_SCHEMA": "Clip.1", + "media_reference": { + "OTIO_SCHEMA": "ImageSequenceReference.1", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 10 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 0 + } + }, + "target_url_base": "", + "name_prefix": "Counter.", + "name_suffix": ".png", + "start_frame": 0, + "frame_step": 1, + "rate": 0.0, + "frame_zero_padding": 0, + "missing_frame_policy": "black" + }, + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 10 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 0 + } + }, + "name": "Sequence" + } + ], + "kind": "Video", + "name": "Video" + } + ], + "name": "Stack" + } +} diff --git a/data/Text.otio b/data/Text.otio index 2d0d4a5..56508eb 100644 --- a/data/Text.otio +++ b/data/Text.otio @@ -17,7 +17,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -86,7 +86,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", diff --git a/data/Transforms.otio b/data/Transforms.otio index bb11934..feb1a8b 100644 --- a/data/Transforms.otio +++ b/data/Transforms.otio @@ -17,7 +17,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -42,7 +42,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -61,7 +61,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", @@ -85,7 +85,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 24 + "value": 10 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", diff --git a/lib/toucan/CMakeLists.txt b/lib/toucan/CMakeLists.txt index b91dc84..8524e38 100644 --- a/lib/toucan/CMakeLists.txt +++ b/lib/toucan/CMakeLists.txt @@ -10,6 +10,7 @@ set(HEADERS ResizeOp.h RotateOp.h SaturateOp.h + SequenceReadOp.h TextOp.h TimelineTraverse.h) set(SOURCE @@ -24,6 +25,7 @@ set(SOURCE ResizeOp.cpp RotateOp.cpp SaturateOp.cpp + SequenceReadOp.cpp TextOp.cpp TimelineTraverse.cpp) diff --git a/lib/toucan/CheckersOp.cpp b/lib/toucan/CheckersOp.cpp index 4e8a1e1..9730247 100644 --- a/lib/toucan/CheckersOp.cpp +++ b/lib/toucan/CheckersOp.cpp @@ -8,7 +8,11 @@ namespace toucan { - CheckersOp::CheckersOp(const CheckersData& data) : + CheckersOp::CheckersOp( + const CheckersData& data, + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) : + IImageOp(timeOffset, inputs), _data(data) {} @@ -25,12 +29,12 @@ namespace toucan _data = value; } - OIIO::ImageBuf CheckersOp::exec() + OIIO::ImageBuf CheckersOp::exec(const OTIO_NS::RationalTime& time) { OIIO::ImageBuf buf; if (!_inputs.empty()) { - buf = _inputs[0]->exec(); + buf = _inputs[0]->exec(time); OIIO::ImageBufAlgo::checker( buf, _data.checkerSize.x, @@ -75,9 +79,11 @@ namespace toucan _data = value; } - std::shared_ptr CheckersEffect::createOp() + std::shared_ptr CheckersEffect::createOp( + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) { - return std::make_shared(_data); + return std::make_shared(_data, timeOffset, inputs); } bool CheckersEffect::read_from(Reader& reader) diff --git a/lib/toucan/CheckersOp.h b/lib/toucan/CheckersOp.h index 6eacf27..6651a78 100644 --- a/lib/toucan/CheckersOp.h +++ b/lib/toucan/CheckersOp.h @@ -21,14 +21,17 @@ namespace toucan class CheckersOp : public IImageOp { public: - CheckersOp(const CheckersData& = CheckersData()); + CheckersOp( + const CheckersData& = CheckersData(), + const OTIO_NS::RationalTime& = OTIO_NS::RationalTime(), + const std::vector >& = {}); virtual ~CheckersOp(); const CheckersData& getData() const; void setData(const CheckersData&); - OIIO::ImageBuf exec() override; + OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; private: CheckersData _data; @@ -52,7 +55,9 @@ namespace toucan const CheckersData& getData() const; void setData(const CheckersData&); - std::shared_ptr createOp() override; + std::shared_ptr createOp( + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) override; protected: virtual ~CheckersEffect(); diff --git a/lib/toucan/CompOp.cpp b/lib/toucan/CompOp.cpp index 4c6534c..0bf4d94 100644 --- a/lib/toucan/CompOp.cpp +++ b/lib/toucan/CompOp.cpp @@ -8,6 +8,12 @@ namespace toucan { + CompOp::CompOp( + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) : + IImageOp(timeOffset, inputs) + {} + CompOp::~CompOp() {} @@ -16,22 +22,22 @@ namespace toucan _premult = premult; } - OIIO::ImageBuf CompOp::exec() + OIIO::ImageBuf CompOp::exec(const OTIO_NS::RationalTime& time) { OIIO::ImageBuf buf; - if (_inputs.size() > 1) + if (_inputs.size() > 1 && _inputs[0] && _inputs[1]) { - auto fg = _inputs[0]->exec(); - auto bg = _inputs[1]->exec(); + auto fg = _inputs[0]->exec(time); + auto bg = _inputs[1]->exec(time); if (_premult) { fg = OIIO::ImageBufAlgo::premult(fg); } buf = OIIO::ImageBufAlgo::over(fg, bg); } - else if (1 == _inputs.size()) + else if (1 == _inputs.size() && _inputs[0]) { - auto fg = _inputs[0]->exec(); + auto fg = _inputs[0]->exec(time); if (_premult) { fg = OIIO::ImageBufAlgo::premult(fg); diff --git a/lib/toucan/CompOp.h b/lib/toucan/CompOp.h index f128139..91b697b 100644 --- a/lib/toucan/CompOp.h +++ b/lib/toucan/CompOp.h @@ -12,11 +12,15 @@ namespace toucan class CompOp : public IImageOp { public: + CompOp( + const OTIO_NS::RationalTime& = OTIO_NS::RationalTime(), + const std::vector >& = {}); + virtual ~CompOp(); void setPremult(bool); - OIIO::ImageBuf exec() override; + OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; private: bool _premult = false; diff --git a/lib/toucan/FillOp.cpp b/lib/toucan/FillOp.cpp index 1c67240..38536fa 100644 --- a/lib/toucan/FillOp.cpp +++ b/lib/toucan/FillOp.cpp @@ -8,7 +8,11 @@ namespace toucan { - FillOp::FillOp(const FillData& data) : + FillOp::FillOp( + const FillData& data, + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) : + IImageOp(timeOffset, inputs), _data(data) {} @@ -25,12 +29,12 @@ namespace toucan _data = value; } - OIIO::ImageBuf FillOp::exec() + OIIO::ImageBuf FillOp::exec(const OTIO_NS::RationalTime& time) { OIIO::ImageBuf buf; if (!_inputs.empty()) { - buf = _inputs[0]->exec(); + buf = _inputs[0]->exec(time); OIIO::ImageBufAlgo::fill( buf, { _data.color.x, _data.color.y, _data.color.z, _data.color.w }); @@ -64,9 +68,11 @@ namespace toucan _data = value; } - std::shared_ptr FillEffect::createOp() + std::shared_ptr FillEffect::createOp( + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) { - return std::make_shared(_data); + return std::make_shared(_data, timeOffset, inputs); } bool FillEffect::read_from(Reader& reader) diff --git a/lib/toucan/FillOp.h b/lib/toucan/FillOp.h index 0f1ff68..c0ca966 100644 --- a/lib/toucan/FillOp.h +++ b/lib/toucan/FillOp.h @@ -19,14 +19,17 @@ namespace toucan class FillOp : public IImageOp { public: - FillOp(const FillData& = FillData()); + FillOp( + const FillData& = FillData(), + const OTIO_NS::RationalTime& = OTIO_NS::RationalTime(), + const std::vector >& = {}); virtual ~FillOp(); const FillData& getData() const; void setData(const FillData&); - OIIO::ImageBuf exec() override; + OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; private: FillData _data; @@ -50,7 +53,9 @@ namespace toucan const FillData& getData() const; void setData(const FillData&); - std::shared_ptr createOp() override; + std::shared_ptr createOp( + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) override; protected: virtual ~FillEffect(); diff --git a/lib/toucan/ImageOp.cpp b/lib/toucan/ImageOp.cpp index 7baaabb..8206f20 100644 --- a/lib/toucan/ImageOp.cpp +++ b/lib/toucan/ImageOp.cpp @@ -6,13 +6,15 @@ namespace toucan { - IImageOp::~IImageOp() + IImageOp::IImageOp( + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) : + _timeOffset(timeOffset), + _inputs(inputs) {} - void IImageOp::setInputs(const std::vector >& inputs) - { - _inputs = inputs; - } + IImageOp::~IImageOp() + {} IEffect::IEffect( std::string const& name, diff --git a/lib/toucan/ImageOp.h b/lib/toucan/ImageOp.h index 8e795e6..a58db06 100644 --- a/lib/toucan/ImageOp.h +++ b/lib/toucan/ImageOp.h @@ -17,13 +17,16 @@ namespace toucan class IImageOp : public std::enable_shared_from_this { public: - virtual ~IImageOp() = 0; + IImageOp( + const OTIO_NS::RationalTime& = OTIO_NS::RationalTime(), + const std::vector >& = {}); - virtual void setInputs(const std::vector >&); + virtual ~IImageOp() = 0; - virtual OIIO::ImageBuf exec() = 0; + virtual OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) = 0; protected: + OTIO_NS::RationalTime _timeOffset = OTIO_NS::RationalTime(0.0, 1.0); std::vector > _inputs; }; @@ -36,7 +39,9 @@ namespace toucan std::string const& effect_name = std::string(), OTIO_NS::AnyDictionary const& metadata = OTIO_NS::AnyDictionary()); - virtual std::shared_ptr createOp() = 0; + virtual std::shared_ptr createOp( + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) = 0; protected: virtual ~IEffect() = 0; diff --git a/lib/toucan/InvertOp.cpp b/lib/toucan/InvertOp.cpp index 691464a..aec5bc2 100644 --- a/lib/toucan/InvertOp.cpp +++ b/lib/toucan/InvertOp.cpp @@ -8,18 +8,21 @@ namespace toucan { - InvertOp::InvertOp() + InvertOp::InvertOp( + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) : + IImageOp(timeOffset, inputs) {} InvertOp::~InvertOp() {} - OIIO::ImageBuf InvertOp::exec() + OIIO::ImageBuf InvertOp::exec(const OTIO_NS::RationalTime& time) { OIIO::ImageBuf buf; if (!_inputs.empty()) { - const auto input = _inputs[0]->exec(); + const auto input = _inputs[0]->exec(time); const auto spec = input.spec(); //! \todo The ROI is not working? buf = OIIO::ImageBufAlgo::invert( @@ -39,9 +42,11 @@ namespace toucan InvertEffect::~InvertEffect() {} - std::shared_ptr InvertEffect::createOp() + std::shared_ptr InvertEffect::createOp( + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) { - return std::make_shared(); + return std::make_shared(timeOffset, inputs); } bool InvertEffect::read_from(Reader& reader) diff --git a/lib/toucan/InvertOp.h b/lib/toucan/InvertOp.h index df21b79..cbc2255 100644 --- a/lib/toucan/InvertOp.h +++ b/lib/toucan/InvertOp.h @@ -12,11 +12,13 @@ namespace toucan class InvertOp : public IImageOp { public: - InvertOp(); + InvertOp( + const OTIO_NS::RationalTime& = OTIO_NS::RationalTime(), + const std::vector >& = {}); virtual ~InvertOp(); - OIIO::ImageBuf exec() override; + OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; }; //! Invert OTIO effect. @@ -34,7 +36,9 @@ namespace toucan std::string const& effect_name = std::string(), OTIO_NS::AnyDictionary const& metadata = OTIO_NS::AnyDictionary()); - std::shared_ptr createOp() override; + std::shared_ptr createOp( + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) override; protected: virtual ~InvertEffect(); diff --git a/lib/toucan/NoiseOp.cpp b/lib/toucan/NoiseOp.cpp index d2e3474..539233d 100644 --- a/lib/toucan/NoiseOp.cpp +++ b/lib/toucan/NoiseOp.cpp @@ -8,7 +8,11 @@ namespace toucan { - NoiseOp::NoiseOp(const NoiseData& data) : + NoiseOp::NoiseOp( + const NoiseData& data, + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) : + IImageOp(timeOffset, inputs), _data(data) {} @@ -25,12 +29,12 @@ namespace toucan _data = value; } - OIIO::ImageBuf NoiseOp::exec() + OIIO::ImageBuf NoiseOp::exec(const OTIO_NS::RationalTime& time) { OIIO::ImageBuf buf; if (!_inputs.empty()) { - buf = _inputs[0]->exec(); + buf = _inputs[0]->exec(time); OIIO::ImageBufAlgo::noise( buf, _data.type, @@ -72,9 +76,11 @@ namespace toucan _data = value; } - std::shared_ptr NoiseEffect::createOp() + std::shared_ptr NoiseEffect::createOp( + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) { - return std::make_shared(_data); + return std::make_shared(_data, timeOffset, inputs); } bool NoiseEffect::read_from(Reader& reader) diff --git a/lib/toucan/NoiseOp.h b/lib/toucan/NoiseOp.h index 1c0dab3..8808930 100644 --- a/lib/toucan/NoiseOp.h +++ b/lib/toucan/NoiseOp.h @@ -23,14 +23,17 @@ namespace toucan class NoiseOp : public IImageOp { public: - NoiseOp(const NoiseData& = NoiseData()); + NoiseOp( + const NoiseData& = NoiseData(), + const OTIO_NS::RationalTime& = OTIO_NS::RationalTime(), + const std::vector >& = {}); virtual ~NoiseOp(); const NoiseData& getData() const; void setData(const NoiseData&); - OIIO::ImageBuf exec() override; + OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; private: NoiseData _data; @@ -54,7 +57,9 @@ namespace toucan const NoiseData& getData() const; void setData(const NoiseData&); - std::shared_ptr createOp() override; + std::shared_ptr createOp( + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) override; protected: virtual ~NoiseEffect(); diff --git a/lib/toucan/ReadOp.cpp b/lib/toucan/ReadOp.cpp index a9bcb90..66e6a35 100644 --- a/lib/toucan/ReadOp.cpp +++ b/lib/toucan/ReadOp.cpp @@ -6,20 +6,18 @@ namespace toucan { - ReadOp::~ReadOp() + ReadOp::ReadOp( + const std::filesystem::path& path, + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) : + IImageOp(timeOffset, inputs), + _path(path) {} - void ReadOp::setPath(const std::filesystem::path& path) - { - _path = path; - } - - void ReadOp::setTime(const opentime::RationalTime& time) - { - _time = time; - } + ReadOp::~ReadOp() + {} - OIIO::ImageBuf ReadOp::exec() + OIIO::ImageBuf ReadOp::exec(const OTIO_NS::RationalTime&) { OIIO::ImageBuf buf(_path.string()); return buf; diff --git a/lib/toucan/ReadOp.h b/lib/toucan/ReadOp.h index 77b6b43..4c40863 100644 --- a/lib/toucan/ReadOp.h +++ b/lib/toucan/ReadOp.h @@ -14,16 +14,16 @@ namespace toucan class ReadOp : public IImageOp { public: - virtual ~ReadOp(); - - void setPath(const std::filesystem::path&); + ReadOp( + const std::filesystem::path&, + const OTIO_NS::RationalTime& = OTIO_NS::RationalTime(), + const std::vector >& = {}); - void setTime(const OTIO_NS::RationalTime&); + virtual ~ReadOp(); - OIIO::ImageBuf exec() override; + OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; private: std::filesystem::path _path; - opentime::RationalTime _time; }; } diff --git a/lib/toucan/ResizeOp.cpp b/lib/toucan/ResizeOp.cpp index 72ecf4c..1e3141d 100644 --- a/lib/toucan/ResizeOp.cpp +++ b/lib/toucan/ResizeOp.cpp @@ -8,7 +8,11 @@ namespace toucan { - ResizeOp::ResizeOp(const ResizeData& data) : + ResizeOp::ResizeOp( + const ResizeData& data, + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) : + IImageOp(timeOffset, inputs), _data(data) {} @@ -25,13 +29,13 @@ namespace toucan _data = value; } - OIIO::ImageBuf ResizeOp::exec() + OIIO::ImageBuf ResizeOp::exec(const OTIO_NS::RationalTime& time) { OIIO::ImageBuf buf; if (!_inputs.empty()) { buf = OIIO::ImageBufAlgo::resize( - _inputs[0]->exec(), + _inputs[0]->exec(time), _data.filterName, _data.filterWidth, OIIO::ROI(0, _data.size.x, 0, _data.size.y, 0, 1, 0, 4)); @@ -59,9 +63,11 @@ namespace toucan _data = value; } - std::shared_ptr ResizeEffect::createOp() + std::shared_ptr ResizeEffect::createOp( + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) { - return std::make_shared(_data); + return std::make_shared(_data, timeOffset, inputs); } bool ResizeEffect::read_from(Reader& reader) diff --git a/lib/toucan/ResizeOp.h b/lib/toucan/ResizeOp.h index 392ffe1..fd5a903 100644 --- a/lib/toucan/ResizeOp.h +++ b/lib/toucan/ResizeOp.h @@ -20,14 +20,17 @@ namespace toucan class ResizeOp : public IImageOp { public: - ResizeOp(const ResizeData& = ResizeData()); + ResizeOp( + const ResizeData& = ResizeData(), + const OTIO_NS::RationalTime& = OTIO_NS::RationalTime(), + const std::vector >& = {}); virtual ~ResizeOp(); const ResizeData& getData() const; void setData(const ResizeData&); - OIIO::ImageBuf exec() override; + OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; private: ResizeData _data; @@ -51,7 +54,9 @@ namespace toucan const ResizeData& getData() const; void setData(const ResizeData&); - std::shared_ptr createOp() override; + std::shared_ptr createOp( + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) override; protected: virtual ~ResizeEffect(); diff --git a/lib/toucan/RotateOp.cpp b/lib/toucan/RotateOp.cpp index 61be738..03bd85f 100644 --- a/lib/toucan/RotateOp.cpp +++ b/lib/toucan/RotateOp.cpp @@ -8,7 +8,11 @@ namespace toucan { - RotateOp::RotateOp(const RotateData& data) : + RotateOp::RotateOp( + const RotateData& data, + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) : + IImageOp(timeOffset, inputs), _data(data) {} @@ -25,13 +29,13 @@ namespace toucan _data = value; } - OIIO::ImageBuf RotateOp::exec() + OIIO::ImageBuf RotateOp::exec(const OTIO_NS::RationalTime& time) { OIIO::ImageBuf buf; if (!_inputs.empty()) { buf = OIIO::ImageBufAlgo::rotate( - _inputs[0]->exec(), + _inputs[0]->exec(time), _data.angle / 360.F * 2.F * M_PI, _data.filterName, _data.filterWidth); @@ -59,9 +63,11 @@ namespace toucan _data = value; } - std::shared_ptr RotateEffect::createOp() + std::shared_ptr RotateEffect::createOp( + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) { - return std::make_shared(_data); + return std::make_shared(_data, timeOffset, inputs); } bool RotateEffect::read_from(Reader& reader) diff --git a/lib/toucan/RotateOp.h b/lib/toucan/RotateOp.h index 34395bb..ca6f0f9 100644 --- a/lib/toucan/RotateOp.h +++ b/lib/toucan/RotateOp.h @@ -20,14 +20,17 @@ namespace toucan class RotateOp : public IImageOp { public: - RotateOp(const RotateData& = RotateData()); + RotateOp( + const RotateData& = RotateData(), + const OTIO_NS::RationalTime& = OTIO_NS::RationalTime(), + const std::vector >& = {}); virtual ~RotateOp(); const RotateData& getData() const; void setData(const RotateData&); - OIIO::ImageBuf exec() override; + OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; private: RotateData _data; @@ -51,7 +54,9 @@ namespace toucan const RotateData& getData() const; void setData(const RotateData&); - std::shared_ptr createOp() override; + std::shared_ptr createOp( + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) override; protected: virtual ~RotateEffect(); diff --git a/lib/toucan/SaturateOp.cpp b/lib/toucan/SaturateOp.cpp index 2e8f375..a0e8132 100644 --- a/lib/toucan/SaturateOp.cpp +++ b/lib/toucan/SaturateOp.cpp @@ -8,7 +8,11 @@ namespace toucan { - SaturateOp::SaturateOp(const SaturateData& data) : + SaturateOp::SaturateOp( + const SaturateData& data, + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) : + IImageOp(timeOffset, inputs), _data(data) {} @@ -25,12 +29,12 @@ namespace toucan _data = value; } - OIIO::ImageBuf SaturateOp::exec() + OIIO::ImageBuf SaturateOp::exec(const OTIO_NS::RationalTime& time) { OIIO::ImageBuf buf; if (!_inputs.empty()) { - const auto input = _inputs[0]->exec(); + const auto input = _inputs[0]->exec(time); const auto spec = input.spec(); //! \todo The ROI is not working? buf = OIIO::ImageBufAlgo::saturate( @@ -62,9 +66,11 @@ namespace toucan _data = value; } - std::shared_ptr SaturateEffect::createOp() + std::shared_ptr SaturateEffect::createOp( + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) { - return std::make_shared(_data); + return std::make_shared(_data, timeOffset, inputs); } bool SaturateEffect::read_from(Reader& reader) diff --git a/lib/toucan/SaturateOp.h b/lib/toucan/SaturateOp.h index faacabd..4fef8f0 100644 --- a/lib/toucan/SaturateOp.h +++ b/lib/toucan/SaturateOp.h @@ -18,14 +18,17 @@ namespace toucan class SaturateOp : public IImageOp { public: - SaturateOp(const SaturateData& = SaturateData()); + SaturateOp( + const SaturateData& = SaturateData(), + const OTIO_NS::RationalTime& = OTIO_NS::RationalTime(), + const std::vector >& = {}); virtual ~SaturateOp(); const SaturateData& getData() const; void setData(const SaturateData&); - OIIO::ImageBuf exec() override; + OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; private: SaturateData _data; @@ -49,7 +52,9 @@ namespace toucan const SaturateData& getData() const; void setData(const SaturateData&); - std::shared_ptr createOp() override; + std::shared_ptr createOp( + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) override; protected: virtual ~SaturateEffect(); diff --git a/lib/toucan/SequenceReadOp.cpp b/lib/toucan/SequenceReadOp.cpp new file mode 100644 index 0000000..5f15ab4 --- /dev/null +++ b/lib/toucan/SequenceReadOp.cpp @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2024 Darby Johnston +// All rights reserved. + +#include "SequenceReadOp.h" + +#include +#include + +namespace toucan +{ + SequenceReadOp::SequenceReadOp( + const std::filesystem::path& base, + const std::string& namePrefix, + const std::string& nameSuffix, + int startFrame, + int frameStep, + double rate, + int frameZeroPadding, + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) : + IImageOp(timeOffset, inputs), + _base(base), + _namePrefix(namePrefix), + _nameSuffix(nameSuffix), + _startFrame(startFrame), + _frameStep(frameStep), + _rate(rate), + _frameZeroPadding(frameZeroPadding) + {} + + SequenceReadOp::~SequenceReadOp() + {} + + OIIO::ImageBuf SequenceReadOp::exec(const OTIO_NS::RationalTime& time) + { + OTIO_NS::RationalTime offsetTime = time; + if (!_timeOffset.is_invalid_time()) + { + offsetTime -= _timeOffset; + } + std::stringstream ss; + ss << _base.string() << _namePrefix << + std::setw(_frameZeroPadding) << std::setfill('0') << offsetTime.to_frames() << + _nameSuffix; + OIIO::ImageBuf buf(ss.str()); + return buf; + } +} diff --git a/lib/toucan/SequenceReadOp.h b/lib/toucan/SequenceReadOp.h new file mode 100644 index 0000000..98d94fb --- /dev/null +++ b/lib/toucan/SequenceReadOp.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2024 Darby Johnston +// All rights reserved. + +#pragma once + +#include + +#include + +namespace toucan +{ + //! Sequence read operation. + class SequenceReadOp : public IImageOp + { + public: + SequenceReadOp( + const std::filesystem::path& base, + const std::string& namePrefix, + const std::string& nameSuffix, + int startFrame, + int frameStep, + double rate, + int frameZeroPadding, + const OTIO_NS::RationalTime& = OTIO_NS::RationalTime(), + const std::vector >& = {}); + + virtual ~SequenceReadOp(); + + OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; + + private: + std::filesystem::path _base; + std::string _namePrefix; + std::string _nameSuffix; + int _startFrame = 1; + int _frameStep = 1; + double _rate = 1.0; + int _frameZeroPadding = 0; + }; +} diff --git a/lib/toucan/TextOp.cpp b/lib/toucan/TextOp.cpp index 67d4473..3f05d24 100644 --- a/lib/toucan/TextOp.cpp +++ b/lib/toucan/TextOp.cpp @@ -8,7 +8,11 @@ namespace toucan { - TextOp::TextOp(const TextData& data) : + TextOp::TextOp( + const TextData& data, + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) : + IImageOp(timeOffset, inputs), _data(data) {} @@ -25,12 +29,12 @@ namespace toucan _data = value; } - OIIO::ImageBuf TextOp::exec() + OIIO::ImageBuf TextOp::exec(const OTIO_NS::RationalTime& time) { OIIO::ImageBuf buf; if (!_inputs.empty()) { - buf = _inputs[0]->exec(); + buf = _inputs[0]->exec(time); OIIO::ImageBufAlgo::render_text( buf, _data.pos.x, @@ -63,9 +67,11 @@ namespace toucan _data = value; } - std::shared_ptr TextEffect::createOp() + std::shared_ptr TextEffect::createOp( + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) { - return std::make_shared(_data); + return std::make_shared(_data, timeOffset, inputs); } bool TextEffect::read_from(Reader& reader) diff --git a/lib/toucan/TextOp.h b/lib/toucan/TextOp.h index 002ac59..693e23a 100644 --- a/lib/toucan/TextOp.h +++ b/lib/toucan/TextOp.h @@ -22,14 +22,17 @@ namespace toucan class TextOp : public IImageOp { public: - TextOp(const TextData& = TextData()); + TextOp( + const TextData& = TextData(), + const OTIO_NS::RationalTime& = OTIO_NS::RationalTime(), + const std::vector >& = {}); virtual ~TextOp(); const TextData& getData() const; void setData(const TextData&); - OIIO::ImageBuf exec() override; + OIIO::ImageBuf exec(const OTIO_NS::RationalTime&) override; private: TextData _data; @@ -53,7 +56,9 @@ namespace toucan const TextData& getData() const; void setData(const TextData&); - std::shared_ptr createOp() override; + std::shared_ptr createOp( + const OTIO_NS::RationalTime& timeOffset, + const std::vector >& inputs) override; protected: virtual ~TextEffect(); diff --git a/lib/toucan/TimelineTraverse.cpp b/lib/toucan/TimelineTraverse.cpp index 88dfb59..ae49841 100644 --- a/lib/toucan/TimelineTraverse.cpp +++ b/lib/toucan/TimelineTraverse.cpp @@ -7,8 +7,10 @@ #include "CompOp.h" #include "FillOp.h" #include "ReadOp.h" +#include "SequenceReadOp.h" #include +#include namespace toucan { @@ -24,7 +26,29 @@ namespace toucan { const std::string url = externalRef->target_url(); const std::filesystem::path path = _path / url; - OIIO::ImageBuf buf(path.string()); + auto read = std::make_shared(path); + const auto buf = read->exec(OTIO_NS::RationalTime(0.0, 1.0)); + const auto& spec = buf.spec(); + if (spec.width > 0) + { + _size.x = spec.width; + _size.y = spec.height; + break; + } + } + else if (auto sequenceRef = dynamic_cast(clip->media_reference())) + { + 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)); const auto& spec = buf.spec(); if (spec.width > 0) { @@ -69,32 +93,53 @@ namespace toucan const OTIO_NS::RationalTime& time, const OTIO_NS::SerializableObject::Retainer& clip) { - const auto availableRange = clip->available_range(); - if (availableRange.contains(time)) + const OTIO_NS::TimeRange trimmedRange = clip->trimmed_range(); + const auto trimmedRangeInParent = clip->trimmed_range_in_parent(); + if (trimmedRange.contains(time) && trimmedRangeInParent.has_value()) { + const OTIO_NS::RationalTime timeOffset = trimmedRangeInParent.value().start_time(); + + // Get the media reference. + std::shared_ptr op; if (auto externalRef = dynamic_cast(clip->media_reference())) { const std::string url = externalRef->target_url(); const std::filesystem::path path = _path / url; - auto read = std::make_shared(); - read->setPath(path); + auto read = std::make_shared(path, timeOffset); + op = read; + } + else if (auto sequenceRef = dynamic_cast(clip->media_reference())) + { + 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(), + timeOffset); + op = read; + } - std::shared_ptr op = read; - for (const auto& effect : clip->effects()) + // Create the effects. + for (const auto& effect : clip->effects()) + { + if (auto iEffect = dynamic_cast(effect.value)) { - if (auto iEffect = dynamic_cast(effect.value)) - { - auto effectOp = iEffect->createOp(); - effectOp->setInputs({ op }); - op = effectOp; - } + auto effectOp = iEffect->createOp(timeOffset, { op }); + op = effectOp; } - - auto comp = std::make_shared(); - comp->setPremult(true); - comp->setInputs({ op, _op }); - _op = comp; } + + // Composite. + auto comp = std::make_shared( + timeOffset, + std::vector >{ op, _op }); + comp->setPremult(true); + _op = comp; } } } diff --git a/tests/CompOpTest.cpp b/tests/CompOpTest.cpp index 48f44cf..fe41d7f 100644 --- a/tests/CompOpTest.cpp +++ b/tests/CompOpTest.cpp @@ -12,14 +12,13 @@ namespace toucan void compOpTest(const std::filesystem::path& path) { std::cout << "compOpTest" << std::endl; - auto fg = std::make_shared(); - fg->setPath(path / "Letter_A.png"); - auto bg = std::make_shared(); - bg->setPath(path / "Color_Blue.png"); - auto comp = std::make_shared(); - comp->setInputs({ fg, bg }); + auto fg = std::make_shared(path / "Letter_A.png"); + auto bg = std::make_shared(path / "Color_Blue.png"); + auto comp = std::make_shared( + OTIO_NS::RationalTime(), + std::vector >{ fg, bg }); comp->setPremult(true); - auto buf = comp->exec(); + auto buf = comp->exec(OTIO_NS::RationalTime()); buf.write("compOpTest.png"); } } diff --git a/tests/ReadOpTest.cpp b/tests/ReadOpTest.cpp index e85e632..35bd12e 100644 --- a/tests/ReadOpTest.cpp +++ b/tests/ReadOpTest.cpp @@ -11,9 +11,8 @@ namespace toucan void readOpTest(const std::filesystem::path& path) { std::cout << "readOpTest" << std::endl; - auto read = std::make_shared(); - read->setPath(path / "Letter_A.png"); - auto buf = read->exec(); + auto read = std::make_shared(path / "Letter_A.png"); + auto buf = read->exec(OTIO_NS::RationalTime()); const auto& spec = buf.spec(); assert(spec.width > 0); } diff --git a/tests/TimelineTraverseTest.cpp b/tests/TimelineTraverseTest.cpp index d5e1b4b..4e6fd71 100644 --- a/tests/TimelineTraverseTest.cpp +++ b/tests/TimelineTraverseTest.cpp @@ -16,12 +16,13 @@ namespace toucan std::cout << "timelineTraverseTest" << std::endl; const std::vector otioFiles = { - /*"CompOver", + "CompOver", "Gap", "Patterns", "Text", - "Transforms",*/ - "Filters" + "Transforms", + "Filters", + "Sequence" }; for (const auto& otioFile : otioFiles) { @@ -50,7 +51,7 @@ namespace toucan timeRange.duration().value() << std::endl; if (auto op = traverse->exec(time)) { - auto buf = op->exec(); + auto buf = op->exec(time); std::stringstream ss; ss << "timelineTraverseTest_" << otioFile << "." << std::setw(6) << std::setfill('0') << time.to_frames() <<