blob: 2c14dfd26f2ce63fb10d858e38e1680431111fe5 [file] [log] [blame]
/*
* Copyright (C) 2008 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/core/svg/animation/smil_animation_sandwich.h"
#include <algorithm>
#include "third_party/blink/renderer/core/svg/animation/svg_smil_element.h"
namespace blink {
namespace {
struct PriorityCompare {
PriorityCompare(SMILTime elapsed) : elapsed_(elapsed) {}
bool operator()(const Member<SVGSMILElement>& a,
const Member<SVGSMILElement>& b) {
return b->IsHigherPriorityThan(a, elapsed_);
}
SMILTime elapsed_;
};
} // namespace
SMILAnimationSandwich::SMILAnimationSandwich() = default;
void SMILAnimationSandwich::Schedule(SVGSMILElement* animation) {
DCHECK(!sandwich_.Contains(animation));
sandwich_.push_back(animation);
}
void SMILAnimationSandwich::Unschedule(SVGSMILElement* animation) {
auto* position = std::find(sandwich_.begin(), sandwich_.end(), animation);
DCHECK(sandwich_.end() != position);
sandwich_.erase(position);
if (animation == ResultElement())
animation->ClearAnimatedType();
}
void SMILAnimationSandwich::Reset() {
if (SVGSMILElement* result_element = ResultElement())
result_element->ClearAnimatedType();
for (SVGSMILElement* animation : sandwich_)
animation->Reset();
}
void SMILAnimationSandwich::UpdateTiming(SMILTime elapsed) {
for (const auto& animation : sandwich_) {
DCHECK(animation->HasValidTarget());
if (!animation->NeedsIntervalUpdate(elapsed))
continue;
animation->UpdateInterval(elapsed);
}
}
SMILTime SMILAnimationSandwich::NextIntervalTime(
SMILTime presentation_time) const {
SMILTime interval_time = SMILTime::Indefinite();
for (const auto& animation : sandwich_) {
interval_time =
std::min(interval_time, animation->NextIntervalTime(presentation_time));
}
return interval_time;
}
SMILTime SMILAnimationSandwich::NextProgressTime(
SMILTime presentation_time) const {
SMILTime earliest_progress_time = SMILTime::Unresolved();
for (const auto& animation : sandwich_) {
earliest_progress_time = std::min(
earliest_progress_time, animation->NextProgressTime(presentation_time));
if (earliest_progress_time <= presentation_time)
break;
}
return earliest_progress_time;
}
void SMILAnimationSandwich::UpdateActiveStateAndOrder(
SMILTime presentation_time) {
for (auto& animation : sandwich_)
animation->UpdateActiveState(presentation_time);
if (!std::is_sorted(sandwich_.begin(), sandwich_.end(),
PriorityCompare(presentation_time))) {
std::sort(sandwich_.begin(), sandwich_.end(),
PriorityCompare(presentation_time));
}
}
SVGSMILElement* SMILAnimationSandwich::ResultElement() const {
return !active_.IsEmpty() ? active_.front() : nullptr;
}
void SMILAnimationSandwich::UpdateActiveAnimationStack(
SMILTime presentation_time) {
SVGSMILElement* old_result_element = ResultElement();
active_.Shrink(0);
active_.ReserveCapacity(sandwich_.size());
// Build the contributing/active sandwich.
for (auto& animation : sandwich_) {
if (!animation->IsContributing(presentation_time))
continue;
animation->UpdateProgressState(presentation_time);
active_.push_back(animation);
}
// If we switched result element, clear the old one.
if (old_result_element && old_result_element != ResultElement())
old_result_element->ClearAnimatedType();
}
SVGSMILElement* SMILAnimationSandwich::ApplyAnimationValues() {
SVGSMILElement* result_element = ResultElement();
if (!result_element)
return nullptr;
// Only reset the animated type to the base value once for
// the lowest priority animation that animates and
// contributes to a particular element/attribute pair.
result_element->ResetAnimatedType();
// Animations have to be applied lowest to highest prio.
//
// Only calculate the relevant animations. If we actually set the
// animation value, we don't need to calculate what is beneath it
// in the sandwich.
auto* sandwich_start = active_.end();
while (sandwich_start != active_.begin()) {
--sandwich_start;
if ((*sandwich_start)->OverwritesUnderlyingAnimationValue())
break;
}
for (auto* sandwich_it = sandwich_start; sandwich_it != active_.end();
sandwich_it++) {
(*sandwich_it)->UpdateAnimatedValue(result_element);
}
result_element->ApplyResultsToTarget();
return result_element;
}
void SMILAnimationSandwich::Trace(blink::Visitor* visitor) {
visitor->Trace(sandwich_);
visitor->Trace(active_);
}
} // namespace blink