From 7c775d7be93afd876da98b52fd369686eb09e797 Mon Sep 17 00:00:00 2001 From: Paul Manias Date: Tue, 23 Apr 2024 21:53:16 +0100 Subject: [PATCH] [SVG] Fixed issues with the synchronisation of animation timers. --- src/svg/animation.cpp | 48 +++++++++++++++++++++++++++++++++++- src/svg/animation.h | 36 +++------------------------ src/svg/animation_timing.cpp | 45 ++++++++++++++++----------------- src/svg/parser.cpp | 4 +-- src/svg/svg.cpp | 6 ++++- 5 files changed, 80 insertions(+), 59 deletions(-) diff --git a/src/svg/animation.cpp b/src/svg/animation.cpp index bf1a2af13..93020fec9 100644 --- a/src/svg/animation.cpp +++ b/src/svg/animation.cpp @@ -3,7 +3,50 @@ // https://www.w3.org/TR/2001/REC-smil-animation-20010904 //******************************************************************************************************************** -// Set common animation properties + +void anim_base::activate(extSVG *SVG) +{ + // Reset all the variables that control time management and the animation will start from scratch. + begin_offset = (double(PreciseTime()) / 1000000.0) - SVG->AnimEpoch; + repeat_index = 0; + start_time = SVG->AnimEpoch + begin_offset; + end_time = 0; + + // Test: w3-animate-elem-21-t.svg + + for (auto &other : start_on_begin) { + other->activate(SVG); + other->start_time = start_time; // Ensure that times match exactly + } +} + +//******************************************************************************************************************** + +void anim_base::stop(extSVG *SVG, double Time) +{ + if (!begin_series.empty()) { + // Check if there's a serialised begin offset following the one that's completed. + LONG i; + for (i=0; i < std::ssize(begin_series)-1; i++) { + if (begin_offset IS begin_series[i]) { + begin_offset = begin_series[i+1]; + start_time = 0; + return; + } + } + } + + end_time = Time; + seek = 1.0; // Necessary in case the seek range calculation has overflowed + + // Start animations that are to be triggered from our ending. + for (auto &other : start_on_end) { + other->activate(SVG); + other->start_time = Time; + } +} + +//******************************************************************************************************************** static ERR parse_spline(APTR Path, LONG Index, LONG Command, double X, double Y, anim_base::SPLINE_POINTS &Meta) { @@ -17,6 +60,9 @@ static ERR parse_spline(APTR Path, LONG Index, LONG Command, double X, double Y, return ERR::Okay; } +//******************************************************************************************************************** +// Set common animation properties + static ERR set_anim_property(extSVG *Self, anim_base &Anim, XMLTag &Tag, ULONG Hash, const std::string_view Value) { switch (Hash) { diff --git a/src/svg/animation.h b/src/svg/animation.h index 1efcc3c4b..95d6d5704 100644 --- a/src/svg/animation.h +++ b/src/svg/animation.h @@ -120,7 +120,6 @@ class anim_base { double min_duration = 0; // The minimum value of the active duration. If zero, the active duration is not constrained. double max_duration = 0; // The maximum value of the active duration. double duration = 0; // Measured in seconds, anything < 0 means infinite. - double first_time = 0; // Time-stamp of the first iteration double start_time = 0; // This is time-stamped once the animation has started (the first begin event is hit) double end_time = 0; // This is time-stamped once the animation has finished all of its cycles (including repetitions) double end = 0; @@ -145,40 +144,11 @@ class anim_base { double get_numeric_value(objVector &, FIELD); std::string get_string(); FRGB get_colour_value(objVector &, FIELD); - bool started(double); + bool started(extSVG *, double); bool next_frame(double); void set_orig_value(); - - void activate(void) { - // Reset all the variables that control time management and the animation will start from scratch. - begin_offset = 0; - repeat_index = 0; - start_time = 0; - end_time = 0; - } - - void stop(double Time) { - if (!begin_series.empty()) { - // Check if there's a serialised begin offset following the one that's completed. - LONG i; - for (i=0; i < std::ssize(begin_series)-1; i++) { - if (begin_offset IS begin_series[i]) { - begin_offset = begin_series[i+1]; - start_time = 0; - return; - } - } - } - - end_time = Time; - seek = 1.0; // Necessary in case the seek range calculation has overflowed - - // Start animations that are to be triggered from our ending. - for (auto &other : start_on_end) { - other->activate(); - other->start_time = Time; - } - } + void activate(extSVG *); + void stop(extSVG *, double); virtual void perform(class extSVG &) = 0; diff --git a/src/svg/animation_timing.cpp b/src/svg/animation_timing.cpp index 881371678..a3e8f3b28 100644 --- a/src/svg/animation_timing.cpp +++ b/src/svg/animation_timing.cpp @@ -1,27 +1,26 @@ //******************************************************************************************************************** -// Return true if the animation has started +// Return true if the animation has started. For absolute consistency, animations start 'at the time they should have +// started', which we can strictly calculate from begin and duration timing values. -bool anim_base::started(double CurrentTime) +bool anim_base::started(extSVG *SVG, double CurrentTime) { - if (not first_time) first_time = CurrentTime; - + if (end_time) return false; if (start_time) return true; if (repeat_index > 0) return true; - if (begin_offset) { - // Check if one of the animation's begin triggers has been tripped. - const double elapsed = CurrentTime - start_time; - if (elapsed < begin_offset) return false; - } + // Check if the begin trigger has been tripped. If begin_offset is 0 then we start immediately. + + if (CurrentTime < SVG->AnimEpoch + begin_offset) return false; + start_time = SVG->AnimEpoch + begin_offset; + + // Start/Reset linked animations that are to start when we do - // Start/Reset linked animations for (auto &other : start_on_begin) { - other->activate(); - other->start_time = CurrentTime; + other->activate(SVG); + other->start_time = start_time; // Ensure that times match exactly } - start_time = CurrentTime; return true; } @@ -71,17 +70,19 @@ static ERR animation_timer(extSVG *SVG, LARGE TimeElapsed, LARGE CurrentTime) matrix.second.transforms.clear(); } - for (auto &record : SVG->Animations) { - std::visit([SVG](auto &&anim) { - double current_time = double(PreciseTime()) / 1000000.0; + if (!SVG->AnimEpoch) { + SVG->AnimEpoch = double(CurrentTime) / 1000000.0; + } - if (not anim.started(current_time)) return; + double current_time = double(CurrentTime) / 1000000.0; - if (anim.next_frame(current_time)) { - anim.perform(*SVG); - anim.stop(current_time); - } - else anim.perform(*SVG); + for (auto &record : SVG->Animations) { + std::visit([ SVG, current_time ](auto &&anim) { + if (not anim.started(SVG, current_time)) return; + + bool stop = anim.next_frame(current_time); + anim.perform(*SVG); + if (stop) anim.stop(SVG, current_time); }, record); } diff --git a/src/svg/parser.cpp b/src/svg/parser.cpp index 2cea646c5..dbb702bfe 100644 --- a/src/svg/parser.cpp +++ b/src/svg/parser.cpp @@ -2371,8 +2371,8 @@ static ERR link_event(objVector *Vector, const InputEvent *Events, svgLink *Link // The link activates a document node, like an animation. if (find_href_tag(Self, Link->ref)) { for (auto &record : Self->Animations) { - std::visit([ Link ](auto &&anim) { - if (anim.id IS Link->ref.substr(1)) anim.activate(); + std::visit([ Link, Self ](auto &&anim) { + if (anim.id IS Link->ref.substr(1)) anim.activate(Self); }, record); } } diff --git a/src/svg/svg.cpp b/src/svg/svg.cpp index f22405e36..83b0a12c1 100644 --- a/src/svg/svg.cpp +++ b/src/svg/svg.cpp @@ -29,7 +29,6 @@ Relevant SVG reference manuals: #include #include #include "svg_def.c" -#include "animation.h" #include #include @@ -71,6 +70,10 @@ struct svgAnimState { #include +class extSVG; + +#include "animation.h" + //******************************************************************************************************************** class extSVG : public objSVG { @@ -79,6 +82,7 @@ class extSVG : public objSVG { std::unordered_map IDs; std::unordered_map Effects; // All effects, registered by their SVG identifier. DOUBLE SVGVersion; + DOUBLE AnimEpoch; // Epoch time for the animations. objXML *XML; objVectorScene *Scene; STRING Folder;