diff --git a/README.md b/README.md index 188acf2..3916052 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,12 @@ Color space conversion: ![Color Space](images/ColorSpace.png) +Multiple effects on clips, tracks, and stacks: + +![Track Effects](images/MultipleEffects.png) + +![Multiple Effects Graph](images/MultipleEffectsGraph.svg) + Building ======== diff --git a/data/Generator.otio b/data/Generator.otio index 06b9457..20a7954 100644 --- a/data/Generator.otio +++ b/data/Generator.otio @@ -46,6 +46,82 @@ }, "name": "Fill" }, + { + "OTIO_SCHEMA": "Clip.1", + "media_reference": { + "OTIO_SCHEMA": "GeneratorReference.1", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 1 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 0 + } + }, + "generator_kind": "Fill", + "parameters": { + "size": [ 1280, 720 ], + "color": [ 0.0, 1.0, 0.0, 1.0 ] + } + }, + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 1 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 0 + } + }, + "name": "Fill" + }, + { + "OTIO_SCHEMA": "Clip.1", + "media_reference": { + "OTIO_SCHEMA": "GeneratorReference.1", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 1 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 0 + } + }, + "generator_kind": "Fill", + "parameters": { + "size": [ 1280, 720 ], + "color": [ 0.0, 0.0, 1.0, 1.0 ] + } + }, + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 1 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 0 + } + }, + "name": "Fill" + }, { "OTIO_SCHEMA": "Clip.1", "media_reference": { @@ -103,6 +179,86 @@ "value": 0 } }, + "generator_kind": "Checkers", + "parameters": { + "size": [ 1280, 720 ], + "checkerSize": [ 200, 200 ], + "color1": [ 1.0, 1.0, 1.0, 1.0 ], + "color2": [ 0.0, 0.0, 0.0, 1.0 ] + } + }, + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 1 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 0 + } + }, + "name": "Checkers" + }, + { + "OTIO_SCHEMA": "Clip.1", + "media_reference": { + "OTIO_SCHEMA": "GeneratorReference.1", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 1 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 0 + } + }, + "generator_kind": "Checkers", + "parameters": { + "size": [ 1280, 720 ], + "checkerSize": [ 300, 300 ], + "color1": [ 1.0, 1.0, 1.0, 1.0 ], + "color2": [ 0.0, 0.0, 0.0, 1.0 ] + } + }, + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 1 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 0 + } + }, + "name": "Checkers" + }, + { + "OTIO_SCHEMA": "Clip.1", + "media_reference": { + "OTIO_SCHEMA": "GeneratorReference.1", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 3 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 0 + } + }, "generator_kind": "Noise", "parameters": { "size": [ 1280, 720 ], @@ -118,7 +274,7 @@ "duration": { "OTIO_SCHEMA": "RationalTime.1", "rate": 24, - "value": 1 + "value": 3 }, "start_time": { "OTIO_SCHEMA": "RationalTime.1", diff --git a/data/MultipleEffects.otio b/data/MultipleEffects.otio new file mode 100644 index 0000000..565599a --- /dev/null +++ b/data/MultipleEffects.otio @@ -0,0 +1,122 @@ +{ + "OTIO_SCHEMA": "Timeline.1", + "metadata": {}, + "name": "MultipleEffects", + "tracks": { + "OTIO_SCHEMA": "Stack.1", + "effects": [ + { + "OTIO_SCHEMA": "PowEffect.1", + "effect_name": "", + "value": 2 + } + ], + "children": [ + { + "OTIO_SCHEMA": "Track.1", + "effects": [ + { + "OTIO_SCHEMA": "InvertEffect.1", + "effect_name": "", + "value": 0.0 + } + ], + "children": [ + { + "OTIO_SCHEMA": "Clip.1", + "media_reference": { + "OTIO_SCHEMA": "ExternalReference.1", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 5 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 0 + } + }, + "target_url": "Charlie.jpg" + }, + "effects": [ + { + "OTIO_SCHEMA": "FlipEffect.1", + "effect_name": "" + }, + { + "OTIO_SCHEMA": "BlurEffect.1", + "effect_name": "", + "radius": 30 + } + ], + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 5 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 0 + } + }, + "name": "Charlie" + }, + { + "OTIO_SCHEMA": "Clip.1", + "media_reference": { + "OTIO_SCHEMA": "ExternalReference.1", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 5 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 0 + } + }, + "target_url": "Charlie.jpg" + }, + "effects": [ + { + "OTIO_SCHEMA": "FlopEffect.1", + "effect_name": "" + }, + { + "OTIO_SCHEMA": "ColorMapEffect.1", + "effect_name": "", + "map_name": "plasma" + } + ], + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 5 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24, + "value": 0 + } + }, + "name": "Charlie" + } + ], + "kind": "Video", + "name": "Video" + } + ], + "name": "Stack" + } +} diff --git a/images/Generator.png b/images/Generator.png index 2046116..61f3809 100644 Binary files a/images/Generator.png and b/images/Generator.png differ diff --git a/images/MultipleEffects.png b/images/MultipleEffects.png new file mode 100644 index 0000000..317ca13 Binary files /dev/null and b/images/MultipleEffects.png differ diff --git a/images/MultipleEffectsGraph.dot b/images/MultipleEffectsGraph.dot new file mode 100644 index 0000000..d882637 --- /dev/null +++ b/images/MultipleEffectsGraph.dot @@ -0,0 +1,16 @@ +digraph MultipleEffects { + node [shape=box, fontsize=12, margin=0.05, width=0, height=0]; + Pow_2560849241088 [label="Pow"] + Comp_2560849246288 -> Pow_2560849241088 + Comp_2560849246288 [label="Comp"] + Invert_2560932117488 -> Comp_2560849246288 + Invert_2560932117488 [label="Invert"] + Blur_2560849246080 -> Invert_2560932117488 + Blur_2560849246080 [label="Blur"] + Flip_2560932119216 -> Blur_2560849246080 + Flip_2560932119216 [label="Flip"] + Read_2560840968656 -> Flip_2560932119216 + Read_2560840968656 [label="Read: Charlie.jpg"] + Fill_2560840970288 -> Comp_2560849246288 + Fill_2560840970288 [label="Fill: 0, 0, 0, 1"] +} \ No newline at end of file diff --git a/images/MultipleEffectsGraph.svg b/images/MultipleEffectsGraph.svg new file mode 100644 index 0000000..5f0026c --- /dev/null +++ b/images/MultipleEffectsGraph.svg @@ -0,0 +1,91 @@ + + + + + + +MultipleEffects + + + +Pow_2560849241088 + +Pow + + + +Comp_2560849246288 + +Comp + + + +Comp_2560849246288->Pow_2560849241088 + + + + + +Invert_2560932117488 + +Invert + + + +Invert_2560932117488->Comp_2560849246288 + + + + + +Blur_2560849246080 + +Blur + + + +Blur_2560849246080->Invert_2560932117488 + + + + + +Flip_2560932119216 + +Flip + + + +Flip_2560932119216->Blur_2560849246080 + + + + + +Read_2560840968656 + +Read: Charlie.jpg + + + +Read_2560840968656->Flip_2560932119216 + + + + + +Fill_2560840970288 + +Fill: 0, 0, 0, 1 + + + +Fill_2560840970288->Comp_2560849246288 + + + + + diff --git a/lib/toucan/TimelineGraph.cpp b/lib/toucan/TimelineGraph.cpp index f571580..b4bdf25 100644 --- a/lib/toucan/TimelineGraph.cpp +++ b/lib/toucan/TimelineGraph.cpp @@ -95,13 +95,21 @@ namespace toucan FillData{ _imageSize, IMATH_NAMESPACE::V4f(0.F, 0.F, 0.F, 1.F )}); // Loop over the tracks. - for (const auto& i : _timeline->tracks()->children()) + auto stack = _timeline->tracks(); + for (const auto& i : stack->children()) { if (auto track = OTIO_NS::dynamic_retainer_cast(i)) { // Process this track. auto trackNode = _track(time, track); + // Get the track effects. + const auto& effects = track->effects(); + if (!effects.empty()) + { + trackNode = _effects(effects, trackNode); + } + // Composite this track over the previous track. std::vector > nodes; if (trackNode) @@ -118,6 +126,13 @@ namespace toucan } } + // Get the stack effects. + const auto& effects = stack->effects(); + if (!effects.empty()) + { + node = _effects(effects, node); + } + return node; } @@ -326,20 +341,10 @@ namespace toucan } // Get the effects. - for (const auto& effect : item->effects()) + const auto& effects = item->effects(); + if (!effects.empty()) { - if (auto iEffect = dynamic_cast(effect.value)) - { - auto effectNode = iEffect->createNode({ out }); - out = effectNode; - } - else if (auto linearTimeWarp = dynamic_cast(effect.value)) - { - auto linearTimeWarpNode = std::make_shared( - static_cast(linearTimeWarp->time_scalar()), - std::vector >{ out }); - out = linearTimeWarpNode; - } + out = _effects(effects, out); } if (out) { @@ -375,4 +380,27 @@ namespace toucan } return out; } + + std::shared_ptr TimelineGraph::_effects( + const std::vector >& effects, + const std::shared_ptr& input) const + { + std::shared_ptr out = input; + for (const auto& effect : effects) + { + if (auto iEffect = dynamic_cast(effect.value)) + { + auto effectNode = iEffect->createNode({ out }); + out = effectNode; + } + else if (auto linearTimeWarp = dynamic_cast(effect.value)) + { + auto linearTimeWarpNode = std::make_shared( + static_cast(linearTimeWarp->time_scalar()), + std::vector >{ out }); + out = linearTimeWarpNode; + } + } + return out; + } } diff --git a/lib/toucan/TimelineGraph.h b/lib/toucan/TimelineGraph.h index c1bed8e..37c7d22 100644 --- a/lib/toucan/TimelineGraph.h +++ b/lib/toucan/TimelineGraph.h @@ -51,6 +51,9 @@ namespace toucan const OTIO_NS::SerializableObject::Retainer&, const OTIO_NS::TimeRange& trimmedRangeInParent, const std::vector >&) const; + std::shared_ptr _effects( + const std::vector >&, + const std::shared_ptr&) const; std::filesystem::path _path; OTIO_NS::SerializableObject::Retainer _timeline; diff --git a/tests/TimelineGraphTest.cpp b/tests/TimelineGraphTest.cpp index 3fe5e4b..f14e8e3 100644 --- a/tests/TimelineGraphTest.cpp +++ b/tests/TimelineGraphTest.cpp @@ -25,6 +25,7 @@ namespace toucan "Gap", "Generator", "LinearTimeWarp", + "MultipleEffects", "Transform", "Transition", "Transition2"