| /* |
| * Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann@kde.org> |
| * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org> |
| * Copyright (C) 2007 Eric Seidel <eric@webkit.org> |
| * Copyright (C) 2008 Apple Inc. All rights reserved. |
| * Copyright (C) 2009 Cameron McCormack <cam@mcc.id.au> |
| * Copyright (C) Research In Motion Limited 2010. All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library 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 |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "third_party/blink/renderer/core/svg/svg_animation_element.h" |
| |
| #include "third_party/blink/renderer/core/dom/document.h" |
| #include "third_party/blink/renderer/core/frame/web_feature.h" |
| #include "third_party/blink/renderer/core/svg/animation/element_smil_animations.h" |
| #include "third_party/blink/renderer/core/svg/svg_animate_element.h" |
| #include "third_party/blink/renderer/core/svg/svg_parser_utilities.h" |
| #include "third_party/blink/renderer/core/svg_names.h" |
| #include "third_party/blink/renderer/platform/bindings/exception_state.h" |
| #include "third_party/blink/renderer/platform/instrumentation/use_counter.h" |
| #include "third_party/blink/renderer/platform/wtf/math_extras.h" |
| |
| namespace blink { |
| |
| SVGAnimationElement::SVGAnimationElement(const QualifiedName& tag_name, |
| Document& document) |
| : SVGSMILElement(tag_name, document), |
| animation_valid_(AnimationValidity::kUnknown), |
| registered_animation_(false), |
| calc_mode_(kCalcModeLinear), |
| animation_mode_(kNoAnimation) { |
| UseCounter::Count(document, WebFeature::kSVGAnimationElement); |
| } |
| |
| bool SVGAnimationElement::ParseValues(const String& value, |
| Vector<String>& result) { |
| // Per the SMIL specification, leading and trailing white space, and white |
| // space before and after semicolon separators, is allowed and will be |
| // ignored. |
| // http://www.w3.org/TR/SVG11/animate.html#ValuesAttribute |
| result.clear(); |
| Vector<String> parse_list; |
| value.Split(';', true, parse_list); |
| unsigned last = parse_list.size() - 1; |
| for (unsigned i = 0; i <= last; ++i) { |
| parse_list[i] = parse_list[i].StripWhiteSpace(IsHTMLSpace<UChar>); |
| if (parse_list[i].IsEmpty()) { |
| // Tolerate trailing ';' |
| if (i < last) |
| goto fail; |
| } else { |
| result.push_back(parse_list[i]); |
| } |
| } |
| |
| return true; |
| fail: |
| result.clear(); |
| return false; |
| } |
| |
| static bool IsInZeroToOneRange(float value) { |
| return value >= 0 && value <= 1; |
| } |
| |
| static bool ParseKeyTimes(const String& string, |
| Vector<float>& result, |
| bool verify_order) { |
| result.clear(); |
| Vector<String> parse_list; |
| string.Split(';', true, parse_list); |
| for (unsigned n = 0; n < parse_list.size(); ++n) { |
| String time_string = parse_list[n].StripWhiteSpace(); |
| bool ok; |
| float time = time_string.ToFloat(&ok); |
| if (!ok || !IsInZeroToOneRange(time)) |
| goto fail; |
| if (verify_order) { |
| if (!n) { |
| if (time) |
| goto fail; |
| } else if (time < result.back()) { |
| goto fail; |
| } |
| } |
| result.push_back(time); |
| } |
| return true; |
| fail: |
| result.clear(); |
| return false; |
| } |
| |
| template <typename CharType> |
| static bool ParseKeySplinesInternal(const String& string, |
| Vector<gfx::CubicBezier>& result) { |
| const CharType* ptr = string.GetCharacters<CharType>(); |
| const CharType* end = ptr + string.length(); |
| |
| SkipOptionalSVGSpaces(ptr, end); |
| |
| while (ptr < end) { |
| float cp1x = 0; |
| if (!ParseNumber(ptr, end, cp1x)) |
| return false; |
| |
| float cp1y = 0; |
| if (!ParseNumber(ptr, end, cp1y)) |
| return false; |
| |
| float cp2x = 0; |
| if (!ParseNumber(ptr, end, cp2x)) |
| return false; |
| |
| float cp2y = 0; |
| if (!ParseNumber(ptr, end, cp2y, kDisallowWhitespace)) |
| return false; |
| |
| SkipOptionalSVGSpaces(ptr, end); |
| |
| if (ptr < end && *ptr == ';') |
| ptr++; |
| SkipOptionalSVGSpaces(ptr, end); |
| |
| // Require that the x values are within the [0, 1] range. |
| if (!IsInZeroToOneRange(cp1x) || !IsInZeroToOneRange(cp2x)) |
| return false; |
| |
| result.push_back(gfx::CubicBezier(cp1x, cp1y, cp2x, cp2y)); |
| } |
| |
| return ptr == end; |
| } |
| |
| static bool ParseKeySplines(const String& string, |
| Vector<gfx::CubicBezier>& result) { |
| result.clear(); |
| if (string.IsEmpty()) |
| return true; |
| bool parsed = true; |
| if (string.Is8Bit()) |
| parsed = ParseKeySplinesInternal<LChar>(string, result); |
| else |
| parsed = ParseKeySplinesInternal<UChar>(string, result); |
| if (!parsed) { |
| result.clear(); |
| return false; |
| } |
| return true; |
| } |
| |
| void SVGAnimationElement::ParseAttribute( |
| const AttributeModificationParams& params) { |
| const QualifiedName& name = params.name; |
| if (name == svg_names::kValuesAttr) { |
| if (!ParseValues(params.new_value, values_)) { |
| ReportAttributeParsingError(SVGParseStatus::kParsingFailed, name, |
| params.new_value); |
| return; |
| } |
| UpdateAnimationMode(); |
| AnimationAttributeChanged(); |
| return; |
| } |
| |
| if (name == svg_names::kKeyTimesAttr) { |
| if (!ParseKeyTimes(params.new_value, key_times_from_attribute_, true)) { |
| ReportAttributeParsingError(SVGParseStatus::kParsingFailed, name, |
| params.new_value); |
| } |
| AnimationAttributeChanged(); |
| return; |
| } |
| |
| if (name == svg_names::kKeyPointsAttr) { |
| if (IsA<SVGAnimateMotionElement>(*this)) { |
| // This is specified to be an animateMotion attribute only but it is |
| // simpler to put it here where the other timing calculatations are. |
| if (!ParseKeyTimes(params.new_value, key_points_, false)) { |
| ReportAttributeParsingError(SVGParseStatus::kParsingFailed, name, |
| params.new_value); |
| } |
| } |
| AnimationAttributeChanged(); |
| return; |
| } |
| |
| if (name == svg_names::kKeySplinesAttr) { |
| if (!ParseKeySplines(params.new_value, key_splines_)) { |
| ReportAttributeParsingError(SVGParseStatus::kParsingFailed, name, |
| params.new_value); |
| } |
| AnimationAttributeChanged(); |
| return; |
| } |
| |
| if (name == svg_names::kCalcModeAttr) { |
| SetCalcMode(params.new_value); |
| AnimationAttributeChanged(); |
| return; |
| } |
| |
| if (name == svg_names::kFromAttr || name == svg_names::kToAttr || |
| name == svg_names::kByAttr) { |
| UpdateAnimationMode(); |
| AnimationAttributeChanged(); |
| return; |
| } |
| |
| SVGSMILElement::ParseAttribute(params); |
| } |
| |
| void SVGAnimationElement::AnimationAttributeChanged() { |
| // Assumptions may not hold after an attribute change. |
| animation_valid_ = AnimationValidity::kUnknown; |
| last_values_animation_from_ = String(); |
| last_values_animation_to_ = String(); |
| } |
| |
| void SVGAnimationElement::UnregisterAnimation( |
| const QualifiedName& attribute_name) { |
| if (!registered_animation_) |
| return; |
| DCHECK(targetElement()); |
| SVGElement* target = targetElement(); |
| if (ElementSMILAnimations* smil_animations = target->GetSMILAnimations()) |
| smil_animations->RemoveAnimation(attribute_name, this); |
| registered_animation_ = false; |
| } |
| |
| void SVGAnimationElement::RegisterAnimation( |
| const QualifiedName& attribute_name) { |
| DCHECK(!registered_animation_); |
| if (!HasValidTarget() || !HasValidAnimation()) |
| return; |
| SVGElement* target = targetElement(); |
| ElementSMILAnimations& smil_animations = target->EnsureSMILAnimations(); |
| smil_animations.AddAnimation(attribute_name, this); |
| registered_animation_ = true; |
| } |
| |
| void SVGAnimationElement::WillChangeAnimationTarget() { |
| SVGSMILElement::WillChangeAnimationTarget(); |
| AnimationAttributeChanged(); |
| } |
| |
| float SVGAnimationElement::getStartTime(ExceptionState& exception_state) const { |
| SMILTime start_time = IntervalBegin(); |
| if (!start_time.IsFinite()) { |
| exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, |
| "No current interval."); |
| return 0; |
| } |
| return clampTo<float>(start_time.InSecondsF()); |
| } |
| |
| float SVGAnimationElement::getCurrentTime() const { |
| return clampTo<float>(Elapsed().InSecondsF()); |
| } |
| |
| float SVGAnimationElement::getSimpleDuration( |
| ExceptionState& exception_state) const { |
| SMILTime duration = SimpleDuration(); |
| if (!duration.IsFinite()) { |
| exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError, |
| "No simple duration defined."); |
| return 0; |
| } |
| return clampTo<float>(duration.InSecondsF()); |
| } |
| |
| void SVGAnimationElement::beginElementAt(float offset) { |
| DCHECK(std::isfinite(offset)); |
| AddInstanceTimeAndUpdate(kBegin, Elapsed() + SMILTime::FromSecondsD(offset), |
| SMILTimeOrigin::kScript); |
| } |
| |
| void SVGAnimationElement::endElementAt(float offset) { |
| DCHECK(std::isfinite(offset)); |
| AddInstanceTimeAndUpdate(kEnd, Elapsed() + SMILTime::FromSecondsD(offset), |
| SMILTimeOrigin::kScript); |
| } |
| |
| void SVGAnimationElement::UpdateAnimationMode() { |
| // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#AnimFuncValues |
| if (hasAttribute(svg_names::kValuesAttr)) |
| SetAnimationMode(kValuesAnimation); |
| else if (!ToValue().IsEmpty()) |
| SetAnimationMode(FromValue().IsEmpty() ? kToAnimation : kFromToAnimation); |
| else if (!ByValue().IsEmpty()) |
| SetAnimationMode(FromValue().IsEmpty() ? kByAnimation : kFromByAnimation); |
| else |
| SetAnimationMode(kNoAnimation); |
| } |
| |
| void SVGAnimationElement::SetCalcMode(const AtomicString& calc_mode) { |
| DEFINE_STATIC_LOCAL(const AtomicString, discrete, ("discrete")); |
| DEFINE_STATIC_LOCAL(const AtomicString, linear, ("linear")); |
| DEFINE_STATIC_LOCAL(const AtomicString, paced, ("paced")); |
| DEFINE_STATIC_LOCAL(const AtomicString, spline, ("spline")); |
| if (calc_mode == discrete) { |
| UseCounter::Count(GetDocument(), WebFeature::kSVGCalcModeDiscrete); |
| SetCalcMode(kCalcModeDiscrete); |
| } else if (calc_mode == linear) { |
| if (IsA<SVGAnimateMotionElement>(*this)) |
| UseCounter::Count(GetDocument(), WebFeature::kSVGCalcModeLinear); |
| // else linear is the default. |
| SetCalcMode(kCalcModeLinear); |
| } else if (calc_mode == paced) { |
| if (!IsA<SVGAnimateMotionElement>(*this)) |
| UseCounter::Count(GetDocument(), WebFeature::kSVGCalcModePaced); |
| // else paced is the default. |
| SetCalcMode(kCalcModePaced); |
| } else if (calc_mode == spline) { |
| UseCounter::Count(GetDocument(), WebFeature::kSVGCalcModeSpline); |
| SetCalcMode(kCalcModeSpline); |
| } else { |
| SetCalcMode(IsA<SVGAnimateMotionElement>(*this) ? kCalcModePaced |
| : kCalcModeLinear); |
| } |
| } |
| |
| String SVGAnimationElement::ToValue() const { |
| return FastGetAttribute(svg_names::kToAttr); |
| } |
| |
| String SVGAnimationElement::ByValue() const { |
| return FastGetAttribute(svg_names::kByAttr); |
| } |
| |
| String SVGAnimationElement::FromValue() const { |
| return FastGetAttribute(svg_names::kFromAttr); |
| } |
| |
| bool SVGAnimationElement::IsAdditive() const { |
| DEFINE_STATIC_LOCAL(const AtomicString, sum, ("sum")); |
| const AtomicString& value = FastGetAttribute(svg_names::kAdditiveAttr); |
| return value == sum || GetAnimationMode() == kByAnimation; |
| } |
| |
| bool SVGAnimationElement::IsAccumulated() const { |
| DEFINE_STATIC_LOCAL(const AtomicString, sum, ("sum")); |
| const AtomicString& value = FastGetAttribute(svg_names::kAccumulateAttr); |
| return value == sum; |
| } |
| |
| void SVGAnimationElement::CalculateKeyTimesForCalcModePaced() { |
| DCHECK_EQ(GetCalcMode(), kCalcModePaced); |
| DCHECK_EQ(GetAnimationMode(), kValuesAnimation); |
| |
| unsigned values_count = values_.size(); |
| DCHECK_GE(values_count, 1u); |
| if (values_count == 1) { |
| // Don't swap lists. |
| use_paced_key_times_ = false; |
| return; |
| } |
| // Clear the list and use it, even if the rest of the function fail |
| use_paced_key_times_ = true; |
| key_times_for_paced_.clear(); |
| |
| Vector<float> calculated_key_times; |
| float total_distance = 0; |
| calculated_key_times.push_back(0); |
| for (unsigned n = 0; n < values_count - 1; ++n) { |
| // Distance in any units |
| float distance = CalculateDistance(values_[n], values_[n + 1]); |
| if (distance < 0) { |
| return; |
| } |
| total_distance += distance; |
| calculated_key_times.push_back(distance); |
| } |
| if (!total_distance) { |
| return; |
| } |
| |
| // Normalize. |
| for (unsigned n = 1; n < calculated_key_times.size() - 1; ++n) { |
| calculated_key_times[n] = |
| calculated_key_times[n - 1] + calculated_key_times[n] / total_distance; |
| } |
| calculated_key_times.back() = 1.0f; |
| key_times_for_paced_.swap(calculated_key_times); |
| } |
| |
| static inline double SolveEpsilon(double duration) { |
| return 1 / (200 * duration); |
| } |
| |
| unsigned SVGAnimationElement::CalculateKeyTimesIndex(float percent) const { |
| unsigned index; |
| unsigned key_times_count = KeyTimes().size(); |
| // For linear and spline animations, the last value must be '1'. In those |
| // cases we don't need to consider the last value, since |percent| is never |
| // greater than one. |
| if (key_times_count && GetCalcMode() != kCalcModeDiscrete) |
| key_times_count--; |
| for (index = 1; index < key_times_count; ++index) { |
| if (KeyTimes()[index] > percent) |
| break; |
| } |
| return --index; |
| } |
| |
| float SVGAnimationElement::CalculatePercentForSpline( |
| float percent, |
| unsigned spline_index) const { |
| DCHECK_EQ(GetCalcMode(), kCalcModeSpline); |
| SECURITY_DCHECK(spline_index < key_splines_.size()); |
| gfx::CubicBezier bezier = key_splines_[spline_index]; |
| SMILTime duration = SimpleDuration(); |
| if (!duration.IsFinite()) |
| duration = SMILTime::FromSecondsD(100.0); |
| return clampTo<float>( |
| bezier.SolveWithEpsilon(percent, SolveEpsilon(duration.InSecondsF()))); |
| } |
| |
| float SVGAnimationElement::CalculatePercentFromKeyPoints(float percent) const { |
| DCHECK_NE(GetCalcMode(), kCalcModePaced); |
| DCHECK_GT(KeyTimes().size(), 1u); |
| DCHECK(!key_points_.IsEmpty()); |
| DCHECK_EQ(key_points_.size(), KeyTimes().size()); |
| |
| if (percent == 1) |
| return key_points_[key_points_.size() - 1]; |
| |
| unsigned index = CalculateKeyTimesIndex(percent); |
| float from_key_point = key_points_[index]; |
| |
| if (GetCalcMode() == kCalcModeDiscrete) |
| return from_key_point; |
| |
| DCHECK_LT(index + 1, KeyTimes().size()); |
| float from_percent = KeyTimes()[index]; |
| float to_percent = KeyTimes()[index + 1]; |
| float to_key_point = key_points_[index + 1]; |
| float key_point_percent = |
| (percent - from_percent) / (to_percent - from_percent); |
| |
| if (GetCalcMode() == kCalcModeSpline) { |
| DCHECK_EQ(key_splines_.size(), key_points_.size() - 1); |
| key_point_percent = CalculatePercentForSpline(key_point_percent, index); |
| } |
| return (to_key_point - from_key_point) * key_point_percent + from_key_point; |
| } |
| |
| float SVGAnimationElement::CalculatePercentForFromTo(float percent) const { |
| if (GetCalcMode() == kCalcModeDiscrete && KeyTimes().size() == 2) |
| return percent > KeyTimes()[1] ? 1 : 0; |
| |
| return percent; |
| } |
| |
| float SVGAnimationElement::CurrentValuesFromKeyPoints(float percent, |
| String& from, |
| String& to) const { |
| DCHECK_NE(GetCalcMode(), kCalcModePaced); |
| DCHECK(!key_points_.IsEmpty()); |
| DCHECK_EQ(key_points_.size(), KeyTimes().size()); |
| float effective_percent = CalculatePercentFromKeyPoints(percent); |
| unsigned index = |
| effective_percent == 1 |
| ? values_.size() - 2 |
| : static_cast<unsigned>(effective_percent * (values_.size() - 1)); |
| from = values_[index]; |
| to = values_[index + 1]; |
| return effective_percent; |
| } |
| |
| float SVGAnimationElement::CurrentValuesForValuesAnimation(float percent, |
| String& from, |
| String& to) const { |
| unsigned values_count = values_.size(); |
| DCHECK_EQ(animation_valid_, AnimationValidity::kValid); |
| DCHECK_GE(values_count, 1u); |
| |
| if (percent == 1 || values_count == 1) { |
| from = values_[values_count - 1]; |
| to = values_[values_count - 1]; |
| return 1; |
| } |
| |
| CalcMode calc_mode = GetCalcMode(); |
| if (auto* animate_element = DynamicTo<SVGAnimateElement>(this)) { |
| if (!animate_element->AnimatedPropertyTypeSupportsAddition()) |
| calc_mode = kCalcModeDiscrete; |
| } |
| if (!key_points_.IsEmpty() && calc_mode != kCalcModePaced) |
| return CurrentValuesFromKeyPoints(percent, from, to); |
| |
| unsigned key_times_count = KeyTimes().size(); |
| DCHECK(!key_times_count || values_count == key_times_count); |
| DCHECK(!key_times_count || (key_times_count > 1 && !KeyTimes()[0])); |
| |
| unsigned index = CalculateKeyTimesIndex(percent); |
| if (calc_mode == kCalcModeDiscrete) { |
| if (!key_times_count) |
| index = static_cast<unsigned>(percent * values_count); |
| from = values_[index]; |
| to = values_[index]; |
| return 0; |
| } |
| |
| float from_percent; |
| float to_percent; |
| if (key_times_count) { |
| from_percent = KeyTimes()[index]; |
| to_percent = KeyTimes()[index + 1]; |
| } else { |
| index = static_cast<unsigned>(floorf(percent * (values_count - 1))); |
| from_percent = static_cast<float>(index) / (values_count - 1); |
| to_percent = static_cast<float>(index + 1) / (values_count - 1); |
| } |
| |
| if (index == values_count - 1) |
| --index; |
| from = values_[index]; |
| to = values_[index + 1]; |
| DCHECK_GT(to_percent, from_percent); |
| float effective_percent = |
| (percent - from_percent) / (to_percent - from_percent); |
| |
| if (calc_mode == kCalcModeSpline) { |
| DCHECK_EQ(key_splines_.size(), values_.size() - 1); |
| effective_percent = CalculatePercentForSpline(effective_percent, index); |
| } |
| return effective_percent; |
| } |
| |
| bool SVGAnimationElement::CalculateValuesAnimation() { |
| if (values_.IsEmpty()) |
| return false; |
| CalcMode calc_mode = GetCalcMode(); |
| // For 'values' animations, there should be exactly as many 'keyTimes' as |
| // 'values'. |
| if (calc_mode != kCalcModePaced && |
| !FastHasAttribute(svg_names::kKeyPointsAttr) && |
| FastHasAttribute(svg_names::kKeyTimesAttr) && |
| values_.size() != KeyTimes().size()) |
| return false; |
| // If 'keyTimes' is specified its last value should be 1 (and the first 0) |
| // unless 'calcMode' is 'discrete'. |
| if (calc_mode != kCalcModeDiscrete && !KeyTimes().IsEmpty() && |
| KeyTimes().back() != 1) |
| return false; |
| // If 'calcMode' is 'spline', there should be one less spline than there are |
| // 'keyPoints' or 'values'. |
| if (calc_mode == kCalcModeSpline) { |
| if ((key_splines_.IsEmpty() || key_splines_.size() != values_.size() - 1) && |
| key_splines_.size() != key_points_.size() - 1) |
| return false; |
| } |
| // If 'keyPoints' is specified it should have the same amount of points as |
| // 'keyTimes', and at least two points. |
| if (FastHasAttribute(svg_names::kKeyPointsAttr) && |
| (KeyTimes().size() < 2 || KeyTimes().size() != key_points_.size())) |
| return false; |
| if (!CalculateToAtEndOfDurationValue(values_.back())) |
| return false; |
| if (calc_mode == kCalcModePaced) |
| CalculateKeyTimesForCalcModePaced(); |
| return true; |
| } |
| |
| bool SVGAnimationElement::CheckAnimationParameters() { |
| if (!IsValid() || !HasValidTarget()) |
| return false; |
| |
| AnimationMode animation_mode = GetAnimationMode(); |
| if (animation_mode == kNoAnimation) |
| return false; |
| |
| // These validations are appropriate for all animation modes. |
| // If 'keyPoints' is specified it should have the same amount of points as |
| // 'keyTimes'. |
| if (FastHasAttribute(svg_names::kKeyPointsAttr) && |
| KeyTimes().size() != key_points_.size()) |
| return false; |
| |
| CalcMode calc_mode = GetCalcMode(); |
| if (calc_mode == kCalcModeSpline) { |
| // If 'calcMode' is 'spline', there should be one less spline than there |
| // are 'keyTimes' or 'keyPoints' - or 'values' if it is used. |
| if (key_splines_.IsEmpty() || |
| (FastHasAttribute(svg_names::kKeyPointsAttr) && |
| key_splines_.size() != key_points_.size() - 1) || |
| (animation_mode == kValuesAnimation && |
| key_splines_.size() != values_.size() - 1) || |
| (FastHasAttribute(svg_names::kKeyTimesAttr) && |
| key_splines_.size() != KeyTimes().size() - 1)) |
| return false; |
| } |
| |
| if (animation_mode == kFromToAnimation || |
| animation_mode == kFromByAnimation || animation_mode == kToAnimation || |
| animation_mode == kByAnimation) { |
| if (FastHasAttribute(svg_names::kKeyTimesAttr)) { |
| // If 'keyPoints' is specified it should have the same amount of points |
| // as 'keyTimes', and at least two points. |
| if (FastHasAttribute(svg_names::kKeyPointsAttr) && |
| (KeyTimes().size() < 2 || KeyTimes().size() != key_points_.size())) |
| return false; |
| } |
| } |
| const String& from = FromValue(); |
| const String& to = ToValue(); |
| const String& by = ByValue(); |
| if (animation_mode == kFromToAnimation) |
| return CalculateFromAndToValues(from, to); |
| if (animation_mode == kToAnimation) { |
| // For to-animations the from value is the current accumulated value from |
| // lower priority animations. |
| // The value is not static and is determined during the animation. |
| return CalculateFromAndToValues(g_empty_string, to); |
| } |
| if (animation_mode == kFromByAnimation) |
| return CalculateFromAndByValues(from, by); |
| if (animation_mode == kByAnimation) |
| return CalculateFromAndByValues(g_empty_string, by); |
| if (animation_mode == kValuesAnimation) |
| return CalculateValuesAnimation(); |
| if (animation_mode == kPathAnimation) { |
| if (calc_mode == kCalcModePaced) |
| return true; |
| // If 'keyPoints' is specified it should have the same amount of points as |
| // 'keyTimes', and at least two points. |
| if (FastHasAttribute(svg_names::kKeyPointsAttr) && |
| (KeyTimes().size() < 2 || KeyTimes().size() != key_points_.size())) |
| return false; |
| return true; |
| } |
| return false; |
| } |
| |
| void SVGAnimationElement::ApplyAnimation(SVGAnimationElement* result_element) { |
| if (animation_valid_ == AnimationValidity::kUnknown) { |
| if (CheckAnimationParameters()) { |
| animation_valid_ = AnimationValidity::kValid; |
| |
| if (IsAdditive() || |
| (IsAccumulated() && GetAnimationMode() != kToAnimation)) { |
| UseCounter::Count(&GetDocument(), |
| WebFeature::kSVGSMILAdditiveAnimation); |
| } |
| } else { |
| animation_valid_ = AnimationValidity::kInvalid; |
| } |
| } |
| DCHECK_NE(animation_valid_, AnimationValidity::kUnknown); |
| |
| if (animation_valid_ != AnimationValidity::kValid || !targetElement()) |
| return; |
| |
| const ProgressState& progress_state = GetProgressState(); |
| const float percent = progress_state.progress; |
| |
| float effective_percent; |
| CalcMode calc_mode = GetCalcMode(); |
| AnimationMode animation_mode = GetAnimationMode(); |
| if (animation_mode == kValuesAnimation) { |
| String from; |
| String to; |
| effective_percent = CurrentValuesForValuesAnimation(percent, from, to); |
| if (from != last_values_animation_from_ || |
| to != last_values_animation_to_) { |
| if (!CalculateFromAndToValues(from, to)) { |
| animation_valid_ = AnimationValidity::kInvalid; |
| return; |
| } |
| last_values_animation_from_ = from; |
| last_values_animation_to_ = to; |
| } |
| } else if (calc_mode != kCalcModePaced && !key_points_.IsEmpty()) { |
| effective_percent = CalculatePercentFromKeyPoints(percent); |
| } else if (calc_mode == kCalcModeSpline && key_points_.IsEmpty() && |
| KeyTimes().size() > 1) { |
| effective_percent = |
| CalculatePercentForSpline(percent, CalculateKeyTimesIndex(percent)); |
| } else if (animation_mode == kFromToAnimation || |
| animation_mode == kToAnimation) { |
| effective_percent = CalculatePercentForFromTo(percent); |
| } else { |
| effective_percent = percent; |
| } |
| CalculateAnimatedValue(effective_percent, progress_state.repeat, |
| result_element); |
| } |
| |
| bool SVGAnimationElement::OverwritesUnderlyingAnimationValue() const { |
| if (IsAdditive() || IsAccumulated()) |
| return false; |
| return GetAnimationMode() != kToAnimation && |
| GetAnimationMode() != kByAnimation && |
| GetAnimationMode() != kNoAnimation; |
| } |
| |
| } // namespace blink |